Merge pull request #9856 from koto/tt-integration

This commit is contained in:
Tobias Koppers 2021-05-10 11:27:42 +02:00 committed by GitHub
commit 2e2cd6de85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 571 additions and 13 deletions

View File

@ -2085,6 +2085,10 @@ export interface Output {
* Handles exceptions in module loading correctly at a performance cost (Deprecated). This will handle module error compatible with the Node.js CommonJS way.
*/
strictModuleExceptionHandling?: StrictModuleExceptionHandling;
/**
* Use a Trusted Types policy to create urls for chunks. 'output.uniqueName' is used a default policy name. Passing a string sets a custom policy name.
*/
trustedTypes?: true | string | TrustedTypes;
/**
* If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.
*/
@ -2156,6 +2160,15 @@ export interface Environment {
*/
module?: boolean;
}
/**
* Use a Trusted Types policy to create urls for chunks.
*/
export interface TrustedTypes {
/**
* The name of the Trusted Types policy created by webpack to serve bundle chunks.
*/
policyName?: string;
}
/**
* Configuration object for web performance recommendations.
*/
@ -3038,6 +3051,10 @@ export interface OutputNormalized {
* Handles exceptions in module loading correctly at a performance cost (Deprecated). This will handle module error compatible with the Node.js CommonJS way.
*/
strictModuleExceptionHandling?: StrictModuleExceptionHandling;
/**
* Use a Trusted Types policy to create urls for chunks.
*/
trustedTypes?: TrustedTypes;
/**
* A unique name of the webpack build to avoid multiple webpack runtimes to conflict when using globals.
*/

View File

@ -168,6 +168,13 @@ exports.scriptNonce = "__webpack_require__.nc";
*/
exports.loadScript = "__webpack_require__.l";
/**
* function to promote a string to a TrustedScriptURL using webpack's Trusted
* Types policy
* Arguments: (url: string) => TrustedScriptURL
*/
exports.createScriptUrl = "__webpack_require__.tu";
/**
* the chunk name of the chunk with the runtime
*/

View File

@ -13,6 +13,7 @@ const AutoPublicPathRuntimeModule = require("./runtime/AutoPublicPathRuntimeModu
const CompatGetDefaultExportRuntimeModule = require("./runtime/CompatGetDefaultExportRuntimeModule");
const CompatRuntimeModule = require("./runtime/CompatRuntimeModule");
const CreateFakeNamespaceObjectRuntimeModule = require("./runtime/CreateFakeNamespaceObjectRuntimeModule");
const CreateScriptUrlRuntimeModule = require("./runtime/CreateScriptUrlRuntimeModule");
const DefinePropertyGettersRuntimeModule = require("./runtime/DefinePropertyGettersRuntimeModule");
const EnsureChunkRuntimeModule = require("./runtime/EnsureChunkRuntimeModule");
const GetChunkFilenameRuntimeModule = require("./runtime/GetChunkFilenameRuntimeModule");
@ -38,6 +39,7 @@ const GLOBALS_ON_REQUIRE = [
RuntimeGlobals.runtimeId,
RuntimeGlobals.compatGetDefaultExport,
RuntimeGlobals.createFakeNamespaceObject,
RuntimeGlobals.createScriptUrl,
RuntimeGlobals.definePropertyGetters,
RuntimeGlobals.ensureChunk,
RuntimeGlobals.entryModuleId,
@ -319,7 +321,23 @@ class RuntimePlugin {
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.loadScript)
.tap("RuntimePlugin", (chunk, set) => {
compilation.addRuntimeModule(chunk, new LoadScriptRuntimeModule());
const withCreateScriptUrl = !!compilation.outputOptions.trustedTypes;
if (withCreateScriptUrl) {
set.add(RuntimeGlobals.createScriptUrl);
}
compilation.addRuntimeModule(
chunk,
new LoadScriptRuntimeModule(withCreateScriptUrl)
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.createScriptUrl)
.tap("RuntimePlugin", (chunk, set) => {
compilation.addRuntimeModule(
chunk,
new CreateScriptUrlRuntimeModule()
);
return true;
});
compilation.hooks.runtimeRequirementInTree

View File

@ -732,6 +732,16 @@ const applyOutputDefaults = (
F(output.environment, "dynamicImport", () => tp && tp.dynamicImport);
F(output.environment, "module", () => tp && tp.module);
const { trustedTypes } = output;
if (trustedTypes) {
F(
trustedTypes,
"policyName",
() =>
output.uniqueName.replace(/[^a-zA-Z0-9\-#=_/@.%]+/g, "_") || "webpack"
);
}
/**
* @param {function(EntryDescription): void} fn iterator
* @returns {void}

View File

@ -338,6 +338,15 @@ const getNormalizedWebpackOptions = config => {
sourceMapFilename: output.sourceMapFilename,
sourcePrefix: output.sourcePrefix,
strictModuleExceptionHandling: output.strictModuleExceptionHandling,
trustedTypes: optionalNestedConfig(
output.trustedTypes,
trustedTypes => {
if (trustedTypes === true) return {};
if (typeof trustedTypes === "string")
return { policyName: trustedTypes };
return { ...trustedTypes };
}
),
uniqueName: output.uniqueName,
wasmLoading: output.wasmLoading,
webassemblyModuleFilename: output.webassemblyModuleFilename,

View File

@ -0,0 +1,54 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const makeSerializable = require("../util/makeSerializable");
const NullDependency = require("./NullDependency");
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
class CreateScriptUrlDependency extends NullDependency {
/**
* @param {[number, number]} range range
*/
constructor(range) {
super();
this.range = range;
}
get type() {
return "create script url";
}
}
CreateScriptUrlDependency.Template = class CreateScriptUrlDependencyTemplate extends (
NullDependency.Template
) {
/**
* @param {Dependency} dependency the dependency for which the template should be applied
* @param {ReplaceSource} source the current replace source which can be modified
* @param {DependencyTemplateContext} templateContext the context object
* @returns {void}
*/
apply(dependency, source, { runtimeRequirements }) {
const dep = /** @type {CreateScriptUrlDependency} */ (dependency);
runtimeRequirements.add(RuntimeGlobals.createScriptUrl);
source.insert(dep.range[0], `${RuntimeGlobals.createScriptUrl}(`);
source.insert(dep.range[1], ")");
}
};
makeSerializable(
CreateScriptUrlDependency,
"webpack/lib/dependencies/CreateScriptUrlDependency"
);
module.exports = CreateScriptUrlDependency;

View File

@ -15,6 +15,7 @@ const createHash = require("../util/createHash");
const { contextify } = require("../util/identifier");
const EnableWasmLoadingPlugin = require("../wasm/EnableWasmLoadingPlugin");
const ConstDependency = require("./ConstDependency");
const CreateScriptUrlDependency = require("./CreateScriptUrlDependency");
const {
harmonySpecifierTag
} = require("./HarmonyImportDependencyParserPlugin");
@ -78,6 +79,10 @@ class WorkerPlugin {
WorkerDependency,
new WorkerDependency.Template()
);
compilation.dependencyTemplates.set(
CreateScriptUrlDependency,
new CreateScriptUrlDependency.Template()
);
/**
* @param {JavascriptParser} parser the parser
@ -297,7 +302,14 @@ class WorkerPlugin {
dep.loc = expr.loc;
block.addDependency(dep);
parser.state.module.addBlock(block);
parser.walkExpression(expr.callee);
if (compilation.outputOptions.trustedTypes) {
const dep = new CreateScriptUrlDependency(
expr.arguments[0].range
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
}
if (expressions.type) {
const expr = expressions.type;
@ -329,6 +341,7 @@ class WorkerPlugin {
parser.state.module.addPresentationalDependency(dep2);
}
parser.walkExpression(expr.callee);
for (const key of Object.keys(expressions)) {
if (expressions[key]) parser.walkExpression(expressions[key]);
}

View File

@ -0,0 +1,61 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
*/
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const HelperRuntimeModule = require("./HelperRuntimeModule");
class CreateScriptUrlRuntimeModule extends HelperRuntimeModule {
constructor() {
super("trusted types");
}
/**
* @returns {string} runtime code
*/
generate() {
const { compilation } = this;
const { runtimeTemplate, outputOptions } = compilation;
const { trustedTypes } = outputOptions;
const fn = RuntimeGlobals.createScriptUrl;
if (!trustedTypes) {
// Skip Trusted Types logic.
return Template.asString([
`${fn} = ${runtimeTemplate.returningFunction("url", "url")};`
]);
}
return Template.asString([
"var policy;",
`${fn} = ${runtimeTemplate.basicFunction("url", [
"// Create Trusted Type policy if Trusted Types are available and the policy doesn't exist yet.",
"if (policy === undefined) {",
Template.indent([
"policy = {",
Template.indent([
`createScriptURL: ${runtimeTemplate.returningFunction(
"url",
"url"
)}`
]),
"};",
'if (typeof trustedTypes !== "undefined" && trustedTypes.createPolicy) {',
Template.indent([
`policy = trustedTypes.createPolicy(${JSON.stringify(
trustedTypes.policyName
)}, policy);`
]),
"}"
]),
"}",
"return policy.createScriptURL(url);"
])};`
]);
}
}
module.exports = CreateScriptUrlRuntimeModule;

View File

@ -42,8 +42,12 @@ class LoadScriptRuntimeModule extends HelperRuntimeModule {
return hooks;
}
constructor() {
/**
* @param {boolean=} withCreateScriptUrl use create script url for trusted types
*/
constructor(withCreateScriptUrl) {
super("load script");
this._withCreateScriptUrl = withCreateScriptUrl;
}
/**
@ -78,7 +82,11 @@ class LoadScriptRuntimeModule extends HelperRuntimeModule {
uniqueName
? 'script.setAttribute("data-webpack", dataWebpackPrefix + key);'
: "",
`script.src = url;`,
`script.src = ${
this._withCreateScriptUrl
? `${RuntimeGlobals.createScriptUrl}(url)`
: "url"
};`,
crossOriginLoading
? Template.asString([
"if (script.src.indexOf(window.location.origin + '/') !== 0) {",

View File

@ -45,6 +45,8 @@ module.exports = {
require("../dependencies/AMDRequireItemDependency"),
"dependencies/CachedConstDependency": () =>
require("../dependencies/CachedConstDependency"),
"dependencies/CreateScriptUrlDependency": () =>
require("../dependencies/CreateScriptUrlDependency"),
"dependencies/CommonJsRequireContextDependency": () =>
require("../dependencies/CommonJsRequireContextDependency"),
"dependencies/CommonJsExportRequireDependency": () =>

View File

@ -6,6 +6,7 @@
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const CreateScriptUrlRuntimeModule = require("../runtime/CreateScriptUrlRuntimeModule");
const StartupChunkDependenciesPlugin = require("../runtime/StartupChunkDependenciesPlugin");
const ImportScriptsChunkLoadingRuntimeModule = require("./ImportScriptsChunkLoadingRuntimeModule");
@ -39,11 +40,13 @@ class ImportScriptsChunkLoadingPlugin {
if (onceForChunkSet.has(chunk)) return;
onceForChunkSet.add(chunk);
if (!isEnabledForChunk(chunk)) return;
const withCreateScriptUrl = !!compilation.outputOptions.trustedTypes;
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
set.add(RuntimeGlobals.hasOwnProperty);
if (withCreateScriptUrl) set.add(RuntimeGlobals.createScriptUrl);
compilation.addRuntimeModule(
chunk,
new ImportScriptsChunkLoadingRuntimeModule(set)
new ImportScriptsChunkLoadingRuntimeModule(set, withCreateScriptUrl)
);
};
compilation.hooks.runtimeRequirementInTree
@ -58,6 +61,15 @@ class ImportScriptsChunkLoadingPlugin {
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.baseURI)
.tap("ImportScriptsChunkLoadingPlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.createScriptUrl)
.tap("RuntimePlugin", (chunk, set) => {
compilation.addRuntimeModule(
chunk,
new CreateScriptUrlRuntimeModule()
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)

View File

@ -16,9 +16,10 @@ const compileBooleanMatcher = require("../util/compileBooleanMatcher");
const { getUndoPath } = require("../util/identifier");
class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule {
constructor(runtimeRequirements) {
constructor(runtimeRequirements, withCreateScriptUrl) {
super("importScripts chunk loading", RuntimeModule.STAGE_ATTACH);
this.runtimeRequirements = runtimeRequirements;
this._withCreateScriptUrl = withCreateScriptUrl;
}
/**
@ -31,7 +32,8 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule {
compilation: {
runtimeTemplate,
outputOptions: { globalObject, chunkLoadingGlobal, hotUpdateGlobal }
}
},
_withCreateScriptUrl: withCreateScriptUrl
} = this;
const fn = RuntimeGlobals.ensureChunkHandlers;
const withBaseURI = this.runtimeRequirements.has(RuntimeGlobals.baseURI);
@ -121,7 +123,11 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule {
? "if(true) { // all chunks have JS"
: `if(${hasJsMatcher("chunkId")}) {`,
Template.indent(
`importScripts(${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId));`
`importScripts(${
withCreateScriptUrl
? `${RuntimeGlobals.createScriptUrl}(${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId))`
: `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId)`
});`
),
"}"
]),
@ -158,7 +164,11 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule {
"success = true;"
])};`,
"// start update chunk loading",
`importScripts(${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId));`,
`importScripts(${
withCreateScriptUrl
? `${RuntimeGlobals.createScriptUrl}(${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId))`
: `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId)`
});`,
'if(!success) throw new Error("Loading update chunk failed for unknown reason");'
]),
"}",

File diff suppressed because one or more lines are too long

View File

@ -2686,6 +2686,22 @@
"strictModuleExceptionHandling": {
"$ref": "#/definitions/StrictModuleExceptionHandling"
},
"trustedTypes": {
"description": "Use a Trusted Types policy to create urls for chunks. 'output.uniqueName' is used a default policy name. Passing a string sets a custom policy name.",
"anyOf": [
{
"enum": [true]
},
{
"description": "The name of the Trusted Types policy created by webpack to serve bundle chunks.",
"type": "string",
"minLength": 1
},
{
"$ref": "#/definitions/TrustedTypes"
}
]
},
"umdNamedDefine": {
"cli": {
"exclude": true
@ -2839,6 +2855,9 @@
"strictModuleExceptionHandling": {
"$ref": "#/definitions/StrictModuleExceptionHandling"
},
"trustedTypes": {
"$ref": "#/definitions/TrustedTypes"
},
"uniqueName": {
"$ref": "#/definitions/UniqueName"
},
@ -4356,6 +4375,18 @@
}
]
},
"TrustedTypes": {
"description": "Use a Trusted Types policy to create urls for chunks.",
"type": "object",
"additionalProperties": false,
"properties": {
"policyName": {
"description": "The name of the Trusted Types policy created by webpack to serve bundle chunks.",
"type": "string",
"minLength": 1
}
}
},
"UmdNamedDefine": {
"description": "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.",
"type": "boolean"

View File

@ -326,6 +326,7 @@ describe("Defaults", () => {
"sourceMapFilename": "[file].map[query]",
"sourcePrefix": undefined,
"strictModuleExceptionHandling": false,
"trustedTypes": undefined,
"uniqueName": "webpack",
"wasmLoading": "fetch",
"webassemblyModuleFilename": "[hash].module.wasm",
@ -1636,7 +1637,8 @@ describe("Defaults", () => {
"uniqueName",
{
output: {
uniqueName: "@@@Hello World!"
uniqueName: "@@@Hello World!",
trustedTypes: true
}
},
e =>
@ -1654,7 +1656,11 @@ describe("Defaults", () => {
- "hotUpdateGlobal": "webpackHotUpdatewebpack",
+ "hotUpdateGlobal": "webpackHotUpdate_Hello_World_",
@@ ... @@
- "trustedTypes": undefined,
- "uniqueName": "webpack",
+ "trustedTypes": Object {
+ "policyName": "@@@Hello_World_",
+ },
+ "uniqueName": "@@@Hello World!",
`)
);

View File

@ -496,7 +496,7 @@ describe("Validation", () => {
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.output has an unknown property 'ecmaVersion'. These properties are valid:
object { assetModuleFilename?, auxiliaryComment?, charset?, chunkFilename?, chunkFormat?, chunkLoadTimeout?, chunkLoading?, chunkLoadingGlobal?, clean?, compareBeforeEmit?, crossOriginLoading?, devtoolFallbackModuleFilenameTemplate?, devtoolModuleFilenameTemplate?, devtoolNamespace?, enabledChunkLoadingTypes?, enabledLibraryTypes?, enabledWasmLoadingTypes?, environment?, filename?, globalObject?, hashDigest?, hashDigestLength?, hashFunction?, hashSalt?, hotUpdateChunkFilename?, hotUpdateGlobal?, hotUpdateMainFilename?, iife?, importFunctionName?, importMetaName?, library?, libraryExport?, libraryTarget?, module?, path?, pathinfo?, publicPath?, scriptType?, sourceMapFilename?, sourcePrefix?, strictModuleErrorHandling?, strictModuleExceptionHandling?, umdNamedDefine?, uniqueName?, wasmLoading?, webassemblyModuleFilename?, workerChunkLoading?, workerWasmLoading? }
object { assetModuleFilename?, auxiliaryComment?, charset?, chunkFilename?, chunkFormat?, chunkLoadTimeout?, chunkLoading?, chunkLoadingGlobal?, clean?, compareBeforeEmit?, crossOriginLoading?, devtoolFallbackModuleFilenameTemplate?, devtoolModuleFilenameTemplate?, devtoolNamespace?, enabledChunkLoadingTypes?, enabledLibraryTypes?, enabledWasmLoadingTypes?, environment?, filename?, globalObject?, hashDigest?, hashDigestLength?, hashFunction?, hashSalt?, hotUpdateChunkFilename?, hotUpdateGlobal?, hotUpdateMainFilename?, iife?, importFunctionName?, importMetaName?, library?, libraryExport?, libraryTarget?, module?, path?, pathinfo?, publicPath?, scriptType?, sourceMapFilename?, sourcePrefix?, strictModuleErrorHandling?, strictModuleExceptionHandling?, trustedTypes?, umdNamedDefine?, uniqueName?, wasmLoading?, webassemblyModuleFilename?, workerChunkLoading?, workerWasmLoading? }
-> Options affecting the output of the compilation. \`output\` options tell webpack how to write the compiled files to disk.
Did you mean output.environment (output.ecmaVersion was a temporary configuration option during webpack 5 beta)?"
`)

View File

@ -5205,6 +5205,41 @@ Object {
"multiple": false,
"simpleType": "boolean",
},
"output-trusted-types": Object {
"configs": Array [
Object {
"description": "Use a Trusted Types policy to create urls for chunks. 'output.uniqueName' is used a default policy name. Passing a string sets a custom policy name.",
"multiple": false,
"path": "output.trustedTypes",
"type": "enum",
"values": Array [
true,
],
},
Object {
"description": "The name of the Trusted Types policy created by webpack to serve bundle chunks.",
"multiple": false,
"path": "output.trustedTypes",
"type": "string",
},
],
"description": "Use a Trusted Types policy to create urls for chunks. 'output.uniqueName' is used a default policy name. Passing a string sets a custom policy name. The name of the Trusted Types policy created by webpack to serve bundle chunks.",
"multiple": false,
"simpleType": "string",
},
"output-trusted-types-policy-name": Object {
"configs": Array [
Object {
"description": "The name of the Trusted Types policy created by webpack to serve bundle chunks.",
"multiple": false,
"path": "output.trustedTypes.policyName",
"type": "string",
},
],
"description": "The name of the Trusted Types policy created by webpack to serve bundle chunks.",
"multiple": false,
"simpleType": "string",
},
"output-unique-name": Object {
"configs": Array [
Object {

View File

@ -0,0 +1,28 @@
it("should load chunk using trusted types with custom policy name", function () {
// emulate trusted types in a window object
const noop = i => i;
const rules = {
createScriptURL: noop
};
window.trustedTypes = {
createPolicy: () => rules
};
const createScriptURLSpy = jest.spyOn(rules, "createScriptURL");
const createPolicySpy = jest.spyOn(window.trustedTypes, "createPolicy");
const promise = import("./empty?b" /* webpackChunkName: "trusted-types" */);
var script = document.head._children.pop();
__non_webpack_require__("./trusted-types.web.js");
expect(script.src).toBe("https://test.cases/path/trusted-types.web.js");
expect(createScriptURLSpy).toHaveBeenCalledWith(
"https://test.cases/path/trusted-types.web.js"
);
expect(createPolicySpy).toHaveBeenCalledWith(
"customPolicyName",
expect.objectContaining({
createScriptURL: expect.anything()
})
);
return promise;
});

View File

@ -0,0 +1,14 @@
module.exports = {
target: "web",
output: {
chunkFilename: "[name].web.js",
crossOriginLoading: "anonymous",
trustedTypes: "customPolicyName"
},
performance: {
hints: false
},
optimization: {
minimize: false
}
};

View File

@ -0,0 +1,30 @@
it("should use default trusted types policy name", function () {
// emulate trusted types in a window object
const noop = i => i;
const rules = {
createScriptURL: noop
};
window.trustedTypes = {
createPolicy: () => rules
};
const createScriptURLSpy = jest.spyOn(rules, "createScriptURL");
const createPolicySpy = jest.spyOn(window.trustedTypes, "createPolicy");
const promise = import(
"./empty?b" /* webpackChunkName: "default-policy-name" */
);
var script = document.head._children.pop();
expect(script.src).toBe("https://test.cases/path/default-policy-name.web.js");
__non_webpack_require__("./default-policy-name.web.js");
expect(createScriptURLSpy).toHaveBeenCalledWith(
"https://test.cases/path/default-policy-name.web.js"
);
expect(createPolicySpy).toHaveBeenCalledWith(
"webpack",
expect.objectContaining({
createScriptURL: expect.anything()
})
);
return promise;
});

View File

@ -0,0 +1,14 @@
module.exports = {
target: "web",
output: {
chunkFilename: "[name].web.js",
crossOriginLoading: "anonymous",
trustedTypes: true
},
performance: {
hints: false
},
optimization: {
minimize: false
}
};

View File

@ -0,0 +1,25 @@
it("should skip trusted types logic when policy name is empty", function () {
// emulate trusted types in a window object
const noop = i => i;
const rules = {
createScriptURL: noop
};
window.trustedTypes = {
createPolicy: () => rules
};
const createScriptURLSpy = jest.spyOn(rules, "createScriptURL");
const createPolicySpy = jest.spyOn(window.trustedTypes, "createPolicy");
const promise = import(
"./empty?b" /* webpackChunkName: "no-trusted-types-policy-name" */
);
var script = document.head._children.pop();
__non_webpack_require__("./no-trusted-types-policy-name.web.js");
expect(script.src).toBe(
"https://test.cases/path/no-trusted-types-policy-name.web.js"
);
expect(createScriptURLSpy).not.toHaveBeenCalled();
expect(createPolicySpy).not.toHaveBeenCalled();
return promise;
});

View File

@ -0,0 +1,13 @@
module.exports = {
target: "web",
output: {
chunkFilename: "[name].web.js",
crossOriginLoading: "anonymous"
},
performance: {
hints: false
},
optimization: {
minimize: false
}
};

View File

@ -0,0 +1,11 @@
it("should load chunk when there are no trusted types", function () {
const promise = import(
"./empty?a" /* webpackChunkName: "no-trusted-types" */
);
var script = document.head._children.pop();
__non_webpack_require__("./no-trusted-types.web.js");
expect(script.src).toBe("https://test.cases/path/no-trusted-types.web.js");
return promise;
});

View File

@ -0,0 +1,14 @@
module.exports = {
target: "web",
output: {
chunkFilename: "[name].web.js",
crossOriginLoading: "anonymous",
trustedTypes: true
},
performance: {
hints: false
},
optimization: {
minimize: false
}
};

View File

@ -0,0 +1,25 @@
// Mock Trusted Types to test if the import (rewritten as importScripts) goes through TT as well.
let policyName = "none";
let scriptURL = "none";
self.trustedTypes = {
createPolicy: (name, rules) => {
policyName = name;
const createScriptURL = rules.createScriptURL;
rules.createScriptURL = url => {
scriptURL = url;
return createScriptURL(url);
};
return rules;
}
};
onmessage = async event => {
const { upper } = await import("./module");
postMessage({
data: upper(event.data),
policyName,
scriptURL
});
};

View File

@ -0,0 +1,45 @@
it("should load a WebWorker using a TrustedScriptURL", async () => {
const noop = i => i;
const rules = {
createScriptURL: noop
};
window.trustedTypes = {
createPolicy: () => rules
};
const createScriptURLSpy = jest.spyOn(rules, "createScriptURL");
const createPolicySpy = jest.spyOn(window.trustedTypes, "createPolicy");
const worker = new Worker(new URL("./worker.js", import.meta.url), {
type: "module"
});
expect(createScriptURLSpy.mock.calls[0][0].toString()).toContain("chunk");
expect(createPolicySpy).toHaveBeenCalledWith("webpack", expect.anything());
worker.postMessage("ok");
const result = await new Promise(resolve => {
worker.onmessage = event => {
resolve(event.data);
};
});
expect(result).toEqual("data: ok, thanks");
await worker.terminate();
});
it("should use Trusted Types for loading modules inside worker", async () => {
const worker = new Worker(new URL("./importingWorker.js", import.meta.url), {
type: "module"
});
worker.postMessage("ok");
const result = await new Promise(resolve => {
worker.onmessage = event => {
resolve(event.data);
};
});
expect(result).toEqual({
data: "OK",
policyName: "webpack",
scriptURL: expect.stringContaining("chunk")
});
await worker.terminate();
});

View File

@ -0,0 +1,3 @@
export function upper(s) {
return s.toUpperCase();
}

View File

@ -0,0 +1,5 @@
module.exports = {
findBundle: function (i, options) {
return ["main.js"];
}
};

View File

@ -0,0 +1,5 @@
var supportsWorker = require("../../../helpers/supportsWorker");
module.exports = function (config) {
return supportsWorker();
};

View File

@ -0,0 +1,8 @@
module.exports = {
output: {
filename: "[name].js",
chunkFilename: "chunk.[name].js",
trustedTypes: true
},
target: "web"
};

View File

@ -0,0 +1,4 @@
//importScripts("./imported.js");
onmessage = async event => {
postMessage(`data: ${event.data}, thanks`);
};

23
types.d.ts vendored
View File

@ -5766,7 +5766,7 @@ declare interface LoadScriptCompilationHooks {
createScript: SyncWaterfallHook<[string, Chunk]>;
}
declare class LoadScriptRuntimeModule extends HelperRuntimeModule {
constructor();
constructor(withCreateScriptUrl?: boolean);
static getCompilationHooks(
compilation: Compilation
): LoadScriptCompilationHooks;
@ -7939,6 +7939,11 @@ declare interface Output {
*/
strictModuleExceptionHandling?: boolean;
/**
* Use a Trusted Types policy to create urls for chunks. 'output.uniqueName' is used a default policy name. Passing a string sets a custom policy name.
*/
trustedTypes?: string | true | TrustedTypes;
/**
* If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.
*/
@ -8201,6 +8206,11 @@ declare interface OutputNormalized {
*/
strictModuleExceptionHandling?: boolean;
/**
* Use a Trusted Types policy to create urls for chunks.
*/
trustedTypes?: TrustedTypes;
/**
* A unique name of the webpack build to avoid multiple webpack runtimes to conflict when using globals.
*/
@ -11291,6 +11301,16 @@ declare interface TimestampAndHash {
timestampHash?: string;
hash: string;
}
/**
* Use a Trusted Types policy to create urls for chunks.
*/
declare interface TrustedTypes {
/**
* The name of the Trusted Types policy created by webpack to serve bundle chunks.
*/
policyName?: string;
}
declare const UNDEFINED_MARKER: unique symbol;
declare interface UpdateHashContextDependency {
chunkGraph: ChunkGraph;
@ -12018,6 +12038,7 @@ declare namespace exports {
export let uncaughtErrorHandler: string;
export let scriptNonce: string;
export let loadScript: string;
export let createScriptUrl: string;
export let chunkName: string;
export let runtimeId: string;
export let getChunkScriptFilename: string;