From 6e773492db4fb26139e2975e09299fca7476e31a Mon Sep 17 00:00:00 2001 From: Anthony Gubler Date: Sat, 24 Nov 2018 15:29:42 +0000 Subject: [PATCH 1/4] Support eliding imports and requires with variable definitions --- src/static-build-loader/loader.ts | 99 ++++++++++++++++--- src/static-build-loader/recast.d.ts | 10 +- tests/support/fixtures/static-has-base.js | 7 ++ .../fixtures/static-has-foo-true-bar-false.js | 7 ++ tests/support/fixtures/static-has-no-flags.js | 7 ++ tests/support/fixtures/static-has-qat-true.js | 7 ++ 6 files changed, 122 insertions(+), 15 deletions(-) diff --git a/src/static-build-loader/loader.ts b/src/static-build-loader/loader.ts index ef0cfc27..8634b5c4 100644 --- a/src/static-build-loader/loader.ts +++ b/src/static-build-loader/loader.ts @@ -1,6 +1,7 @@ import getFeatures from './getFeatures'; import { LoaderContext, RawSourceMap } from 'webpack'; import * as recast from 'recast'; +import { ExpressionStatement, BaseNode } from 'estree'; const { getOptions } = require('loader-utils'); const types = recast.types; @@ -31,6 +32,33 @@ function hasCheck(hasIdentifier: string, args: any, callee: any) { ); } +function getExpressionValue(node: ExpressionStatement): string | undefined { + if (namedTypes.Literal.check(node.expression) && typeof node.expression.value === 'string') { + return node.expression.value; + } + if (namedTypes.TemplateLiteral.check(node.expression)) { + if ( + node.expression.quasis.length === 1 && + namedTypes.TemplateElement.check(node.expression.quasis[0]) && + typeof node.expression.quasis[0].value.raw === 'string' + ) { + return node.expression.quasis[0].value.raw; + } + } +} + +function setComment( + node: T, + path: recast.Path, + comment: string, + parentPath: recast.Path, + name: string +) { + const next = (Array.isArray(parentPath.value) && parentPath.value[Number(name) + 1]) || parentPath.node; + next.comments = [...((node as any).comments || []), ...(next.comments || []), builders.commentLine(comment)]; + path.replace(null); +} + /** * Checks code for usage of has pragmas or other calls to @dojo/framework/has and optimizes them out based on the flags or * feature sets specified statically. This loader should act on JavaScript, so it should run after the compiler @@ -69,7 +97,7 @@ export default function loader(this: LoaderContext, content: string, sourceMap?: let features: StaticHasFeatures; let elideNextImport = false; let hasIdentifier: string | undefined; - + let comment: string | undefined; if (!featuresOption || Array.isArray(featuresOption) || typeof featuresOption === 'string') { features = getFeatures(featuresOption); } else { @@ -79,9 +107,9 @@ export default function loader(this: LoaderContext, content: string, sourceMap?: types.visit(ast, { visitExpressionStatement(path) { const { node, parentPath, name } = path; - let comment; - if (namedTypes.Literal.check(node.expression) && typeof node.expression.value === 'string') { - const hasPragma = HAS_PRAGMA.exec(node.expression.value); + const expressionValue = getExpressionValue(node); + if (expressionValue) { + const hasPragma = HAS_PRAGMA.exec(expressionValue); if (hasPragma) { const [, negate, flag] = hasPragma; comment = ` ${negate}has('${flag}')`; @@ -109,25 +137,35 @@ export default function loader(this: LoaderContext, content: string, sourceMap?: } if (comment && parentPath && typeof name !== 'undefined') { - const next = (Array.isArray(parentPath.value) && parentPath.value[Number(name) + 1]) || parentPath.node; - next.comments = [ - ...((node as any).comments || []), - ...(next.comments || []), - builders.commentLine(comment) - ]; - path.replace(null); + setComment(node, path, comment, parentPath, name); + comment = undefined; return false; } + comment = undefined; this.traverse(path); }, visitDeclaration(path) { + const { node, parentPath, name } = path; if (namedTypes.ImportDeclaration.check(path.node)) { const value = path.node.source.value; - if (typeof value === 'string' && HAS_MID.test(value)) { - const specifier = path.node.specifiers[0]; - if (specifier.type === 'ImportDefaultSpecifier') { + const specifier = path.node.specifiers[0]; + + if (elideNextImport) { + comment = ` elided: import '${value}'`; + elideNextImport = false; + } + if (comment && parentPath && typeof name !== 'undefined') { + setComment(node, path, comment, parentPath, name); + comment = undefined; + return false; + } + + comment = undefined; + + if (specifier && specifier.type === 'ImportDefaultSpecifier') { + if (typeof value === 'string' && HAS_MID.test(value)) { hasIdentifier = specifier.local.name; } } @@ -138,9 +176,42 @@ export default function loader(this: LoaderContext, content: string, sourceMap?: // Look for `require('*/has');` and set the variable name to `hasIdentifier` visitVariableDeclaration(path) { const { + name, + node, + parentPath, parentPath: { node: parentNode }, node: { declarations } } = path; + + if (elideNextImport === true && declarations.length === 1) { + const callExpression = declarations[0]; + if (namedTypes.VariableDeclarator.check(callExpression)) { + if ( + callExpression.init && + namedTypes.CallExpression.check(callExpression.init) && + namedTypes.Identifier.check(callExpression.init.callee) + ) { + if ( + callExpression.init.callee.name === 'require' && + callExpression.init.arguments.length === 1 + ) { + const [arg] = callExpression.init.arguments; + if (namedTypes.Literal.check(arg)) { + comment = ` elided: import '${arg.value}'`; + elideNextImport = false; + } + } + } + } + + if (comment && parentPath && typeof name !== 'undefined') { + setComment(node, path, comment, parentPath, name); + comment = undefined; + return false; + } + comment = undefined; + } + // Get all the top level variable declarations if (ast.program === parentNode && !hasIdentifier) { declarations.forEach(({ id, init }) => { diff --git a/src/static-build-loader/recast.d.ts b/src/static-build-loader/recast.d.ts index 403abf49..f5638a09 100644 --- a/src/static-build-loader/recast.d.ts +++ b/src/static-build-loader/recast.d.ts @@ -15,7 +15,11 @@ declare module 'recast/main' { ExpressionStatement, VariableDeclaration, MemberExpression, - ImportDeclaration + ImportDeclaration, + AssignmentExpression, + TemplateLiteral, + TemplateElement, + VariableDeclarator } from 'estree'; namespace recast { @@ -32,6 +36,10 @@ declare module 'recast/main' { ExpressionStatement: ExpressionStatement; VariableDeclaration: VariableDeclaration; ImportDeclaration: ImportDeclaration; + AssignmentExpression: AssignmentExpression; + TemplateLiteral: TemplateLiteral; + TemplateElement: TemplateElement; + VariableDeclarator: VariableDeclarator; } interface AST { diff --git a/tests/support/fixtures/static-has-base.js b/tests/support/fixtures/static-has-base.js index c5229e34..2ed39009 100644 --- a/tests/support/fixtures/static-has-base.js +++ b/tests/support/fixtures/static-has-base.js @@ -20,7 +20,14 @@ require(foo); require('foo'); "!has('baz')"; require("qat"); +"!has('bar')"; +var importedValue = require('bar'); +`!has('bar')`; +import another from 'default-import'; +"!has('bar')"; +import 'no-var-import'; +var newVar = ''; somename.default.add('foo'); var dynamicHas = somename.default(foo); diff --git a/tests/support/fixtures/static-has-foo-true-bar-false.js b/tests/support/fixtures/static-has-foo-true-bar-false.js index 4fe54fd1..d4f3339a 100644 --- a/tests/support/fixtures/static-has-foo-true-bar-false.js +++ b/tests/support/fixtures/static-has-foo-true-bar-false.js @@ -23,6 +23,13 @@ require('foo'); // !has('baz') require("qat"); +// !has('bar') +// elided: import 'bar' +// !has('bar') +// elided: import 'default-import' +// !has('bar') +// elided: import 'no-var-import' +var newVar = ''; somename.default.add('foo'); var dynamicHas = somename.default(foo); diff --git a/tests/support/fixtures/static-has-no-flags.js b/tests/support/fixtures/static-has-no-flags.js index d79a6ed9..968acdd1 100644 --- a/tests/support/fixtures/static-has-no-flags.js +++ b/tests/support/fixtures/static-has-no-flags.js @@ -20,7 +20,14 @@ require(foo); require('foo'); // !has('baz') require("qat"); +// !has('bar') +var importedValue = require('bar'); +// !has('bar') +import another from 'default-import'; +// !has('bar') +import 'no-var-import'; +var newVar = ''; somename.default.add('foo'); var dynamicHas = somename.default(foo); diff --git a/tests/support/fixtures/static-has-qat-true.js b/tests/support/fixtures/static-has-qat-true.js index c1beb356..85cc4602 100644 --- a/tests/support/fixtures/static-has-qat-true.js +++ b/tests/support/fixtures/static-has-qat-true.js @@ -20,7 +20,14 @@ require(foo); // elided: import 'foo' // !has('baz') require("qat"); +// !has('bar') +var importedValue = require('bar'); +// !has('bar') +import another from 'default-import'; +// !has('bar') +import 'no-var-import'; +var newVar = ''; somename.default.add('foo'); var dynamicHas = somename.default(foo); From c0f63fd942e64b5636427c0e03f236a690955baa Mon Sep 17 00:00:00 2001 From: Anthony Gubler Date: Tue, 27 Nov 2018 08:35:43 +0000 Subject: [PATCH 2/4] Replace features with one single modern feature set --- src/static-build-loader/features/chrome.json | 38 ------------------- src/static-build-loader/features/edge.json | 38 ------------------- src/static-build-loader/features/firefox.json | 38 ------------------- src/static-build-loader/features/ie11.json | 38 ------------------- src/static-build-loader/features/ios.json | 38 ------------------- .../features/{android.json => modern.json} | 3 -- src/static-build-loader/features/node.json | 38 ------------------- src/static-build-loader/features/node8.json | 38 ------------------- src/static-build-loader/features/safari.json | 38 ------------------- 9 files changed, 307 deletions(-) delete mode 100644 src/static-build-loader/features/chrome.json delete mode 100644 src/static-build-loader/features/edge.json delete mode 100644 src/static-build-loader/features/firefox.json delete mode 100644 src/static-build-loader/features/ie11.json delete mode 100644 src/static-build-loader/features/ios.json rename src/static-build-loader/features/{android.json => modern.json} (88%) delete mode 100644 src/static-build-loader/features/node.json delete mode 100644 src/static-build-loader/features/node8.json delete mode 100644 src/static-build-loader/features/safari.json diff --git a/src/static-build-loader/features/chrome.json b/src/static-build-loader/features/chrome.json deleted file mode 100644 index a8c7a176..00000000 --- a/src/static-build-loader/features/chrome.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "arraybuffer": true, - "blob": true, - "dom-mutationobserver": true, - "dom-webanimation": false, - "dom-pointer-events": true, - "dom-intersection-observer": true, - "es-observable": false, - "es2017-object": true, - "es2017-string": true, - "es6-array": true, - "es6-array-fill": true, - "es6-map": true, - "es6-math": true, - "es6-math-imul": true, - "es6-object": true, - "es6-promise": true, - "es6-set": true, - "es6-string": true, - "es6-string-raw": true, - "es6-symbol": true, - "es6-weakmap": true, - "es7-array": true, - "fetch": true, - "filereader": true, - "float32array": true, - "formdata": true, - "host-node": false, - "host-browser": true, - "microtasks": true, - "node-buffer": false, - "object-assign": true, - "postmessage": true, - "raf": true, - "setimmediate": false, - "xhr": true, - "xhr2": true -} diff --git a/src/static-build-loader/features/edge.json b/src/static-build-loader/features/edge.json deleted file mode 100644 index f3188453..00000000 --- a/src/static-build-loader/features/edge.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "arraybuffer": true, - "blob": true, - "dom-mutationobserver": true, - "dom-webanimation": false, - "dom-pointer-events": true, - "dom-intersection-observer": true, - "es-observable": false, - "es2017-object": true, - "es2017-string": true, - "es6-array": true, - "es6-array-fill": true, - "es6-map": true, - "es6-math": true, - "es6-math-imul": true, - "es6-object": true, - "es6-promise": true, - "es6-set": true, - "es6-string": true, - "es6-string-raw": true, - "es6-symbol": true, - "es6-weakmap": true, - "es7-array": true, - "fetch": true, - "filereader": true, - "float32array": true, - "formdata": true, - "host-node": false, - "host-browser": true, - "microtasks": true, - "node-buffer": false, - "object-assign": true, - "postmessage": true, - "raf": true, - "setimmediate": true, - "xhr": true, - "xhr2": true -} diff --git a/src/static-build-loader/features/firefox.json b/src/static-build-loader/features/firefox.json deleted file mode 100644 index 4a57b48f..00000000 --- a/src/static-build-loader/features/firefox.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "arraybuffer": true, - "blob": true, - "dom-mutationobserver": true, - "dom-webanimation": false, - "dom-pointer-events": false, - "dom-intersection-observer": true, - "es-observable": false, - "es2017-object": true, - "es2017-string": true, - "es6-array": true, - "es6-array-fill": true, - "es6-map": true, - "es6-math": true, - "es6-math-imul": true, - "es6-object": true, - "es6-promise": true, - "es6-set": true, - "es6-string": true, - "es6-string-raw": true, - "es6-symbol": true, - "es6-weakmap": true, - "es7-array": true, - "fetch": true, - "filereader": true, - "float32array": true, - "formdata": true, - "host-node": false, - "host-browser": true, - "microtasks": true, - "node-buffer": false, - "object-assign": true, - "postmessage": true, - "raf": true, - "setimmediate": false, - "xhr": true, - "xhr2": true -} diff --git a/src/static-build-loader/features/ie11.json b/src/static-build-loader/features/ie11.json deleted file mode 100644 index 92a08938..00000000 --- a/src/static-build-loader/features/ie11.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "arraybuffer": true, - "blob": true, - "dom-mutationobserver": false, - "dom-webanimation": false, - "dom-pointer-events": true, - "dom-intersection-observer": false, - "es-observable": false, - "es2017-object": false, - "es2017-string": false, - "es6-array": false, - "es6-array-fill": false, - "es6-map": false, - "es6-math": false, - "es6-math-imul": false, - "es6-object": false, - "es6-promise": false, - "es6-set": false, - "es6-string": false, - "es6-string-raw": false, - "es6-symbol": false, - "es6-weakmap": false, - "es7-array": false, - "fetch": false, - "filereader": true, - "float32array": true, - "formdata": false, - "host-node": false, - "host-browser": true, - "microtasks": true, - "node-buffer": false, - "object-assign": false, - "postmessage": true, - "raf": true, - "setimmediate": true, - "xhr": true, - "xhr2": true -} diff --git a/src/static-build-loader/features/ios.json b/src/static-build-loader/features/ios.json deleted file mode 100644 index f6a39abd..00000000 --- a/src/static-build-loader/features/ios.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "arraybuffer": true, - "blob": true, - "dom-mutationobserver": true, - "dom-webanimation": false, - "dom-pointer-events": false, - "dom-intersection-observer": false, - "es-observable": false, - "es2017-object": false, - "es2017-string": false, - "es6-array": true, - "es6-array-fill": true, - "es6-map": true, - "es6-math": true, - "es6-math-imul": true, - "es6-object": true, - "es6-promise": true, - "es6-set": true, - "es6-string": true, - "es6-string-raw": true, - "es6-symbol": true, - "es6-weakmap": true, - "es7-array": false, - "fetch": true, - "filereader": true, - "float32array": true, - "formdata": true, - "host-node": false, - "host-browser": true, - "microtasks": true, - "node-buffer": false, - "object-assign": true, - "postmessage": true, - "raf": true, - "setimmediate": false, - "xhr": true, - "xhr2": true -} diff --git a/src/static-build-loader/features/android.json b/src/static-build-loader/features/modern.json similarity index 88% rename from src/static-build-loader/features/android.json rename to src/static-build-loader/features/modern.json index a8c7a176..d69194be 100644 --- a/src/static-build-loader/features/android.json +++ b/src/static-build-loader/features/modern.json @@ -2,9 +2,6 @@ "arraybuffer": true, "blob": true, "dom-mutationobserver": true, - "dom-webanimation": false, - "dom-pointer-events": true, - "dom-intersection-observer": true, "es-observable": false, "es2017-object": true, "es2017-string": true, diff --git a/src/static-build-loader/features/node.json b/src/static-build-loader/features/node.json deleted file mode 100644 index d1de9fe1..00000000 --- a/src/static-build-loader/features/node.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "arraybuffer": true, - "blob": true, - "dom-mutationobserver": false, - "dom-webanimation": false, - "dom-pointer-events": false, - "dom-intersection-observer": false, - "es-observable": false, - "es2017-object": false, - "es2017-string": false, - "es6-array": true, - "es6-array-fill": true, - "es6-map": true, - "es6-math": true, - "es6-math-imul": true, - "es6-object": true, - "es6-promise": true, - "es6-set": true, - "es6-string": true, - "es6-string-raw": true, - "es6-symbol": true, - "es6-weakmap": true, - "es7-array": true, - "fetch": false, - "filereader": false, - "float32array": true, - "formdata": false, - "host-node": true, - "host-browser": false, - "microtasks": true, - "node-buffer": true, - "object-assign": true, - "postmessage": false, - "raf": false, - "setimmediate": true, - "xhr": false, - "xhr2": false -} diff --git a/src/static-build-loader/features/node8.json b/src/static-build-loader/features/node8.json deleted file mode 100644 index d6dc64d3..00000000 --- a/src/static-build-loader/features/node8.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "arraybuffer": true, - "blob": true, - "dom-mutationobserver": false, - "dom-webanimation": false, - "dom-pointer-events": false, - "dom-intersection-observer": false, - "es-observable": false, - "es2017-object": true, - "es2017-string": true, - "es6-array": true, - "es6-array-fill": true, - "es6-map": true, - "es6-math": true, - "es6-math-imul": true, - "es6-object": true, - "es6-promise": true, - "es6-set": true, - "es6-string": true, - "es6-string-raw": true, - "es6-symbol": true, - "es6-weakmap": true, - "es7-array": true, - "fetch": false, - "filereader": false, - "float32array": true, - "formdata": false, - "host-node": true, - "host-browser": false, - "microtasks": true, - "node-buffer": true, - "object-assign": true, - "postmessage": false, - "raf": false, - "setimmediate": true, - "xhr": false, - "xhr2": false -} diff --git a/src/static-build-loader/features/safari.json b/src/static-build-loader/features/safari.json deleted file mode 100644 index b8daaaf4..00000000 --- a/src/static-build-loader/features/safari.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "arraybuffer": true, - "blob": true, - "dom-mutationobserver": true, - "dom-webanimation": false, - "dom-pointer-events": false, - "dom-intersection-observer": false, - "es-observable": false, - "es2017-object": false, - "es2017-string": true, - "es6-array": true, - "es6-array-fill": true, - "es6-map": true, - "es6-math": true, - "es6-math-imul": true, - "es6-object": true, - "es6-promise": true, - "es6-set": true, - "es6-string": true, - "es6-string-raw": true, - "es6-symbol": true, - "es6-weakmap": true, - "es7-array": true, - "fetch": true, - "filereader": true, - "float32array": true, - "formdata": true, - "host-node": false, - "host-browser": true, - "microtasks": true, - "node-buffer": false, - "object-assign": true, - "postmessage": true, - "raf": true, - "setimmediate": false, - "xhr": true, - "xhr2": true -} From 08180a8fac0bf28ba61aea152fa98c196df8e2a5 Mon Sep 17 00:00:00 2001 From: Anthony Gubler Date: Tue, 27 Nov 2018 08:36:12 +0000 Subject: [PATCH 3/4] Invert the static has logic so that it reads like a logic if statement --- package-lock.json | 12 ++-- src/static-build-loader/loader.ts | 2 +- tests/support/fixtures/no-import-foo-true.js | 2 +- tests/support/fixtures/no-import.js | 2 +- tests/support/fixtures/static-has-base.js | 16 ++--- .../fixtures/static-has-foo-true-bar-false.js | 16 ++--- tests/support/fixtures/static-has-no-flags.js | 12 ++-- tests/support/fixtures/static-has-qat-true.js | 16 ++--- tests/unit/static-build-loader/getFeatures.ts | 65 +++++++------------ 9 files changed, 61 insertions(+), 82 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b4cf844..6e6def76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1584,9 +1584,9 @@ } }, "caniuse-db": { - "version": "1.0.30000910", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000910.tgz", - "integrity": "sha512-eysv5eAsXCBnfnhTZsKBtCZKdgeFaRqOlTN74kCfzdHdz0In3E5Aop7PyqPI757DsdjVwJOWrFHIrTPYzmll6g==", + "version": "1.0.30000911", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000911.tgz", + "integrity": "sha512-TrcnJ/w8DA7ZCXbZ2/EPgBmZtkYQAPegRovKpB0BJmDsf86ZoQM/CizblSeNt0TZBxqgeHmj+eElPe33fAGwdw==", "dev": true }, "capture-stack-trace": { @@ -9215,9 +9215,9 @@ } }, "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==" }, "ms": { "version": "2.1.1", diff --git a/src/static-build-loader/loader.ts b/src/static-build-loader/loader.ts index 8634b5c4..4981f30e 100644 --- a/src/static-build-loader/loader.ts +++ b/src/static-build-loader/loader.ts @@ -114,7 +114,7 @@ export default function loader(this: LoaderContext, content: string, sourceMap?: const [, negate, flag] = hasPragma; comment = ` ${negate}has('${flag}')`; if (flag in features) { - elideNextImport = negate ? !features[flag] : features[flag]; + elideNextImport = negate ? !!features[flag] : !features[flag]; } } } diff --git a/tests/support/fixtures/no-import-foo-true.js b/tests/support/fixtures/no-import-foo-true.js index 927cc312..fce16b8c 100644 --- a/tests/support/fixtures/no-import-foo-true.js +++ b/tests/support/fixtures/no-import-foo-true.js @@ -1,5 +1,5 @@ var x = require('y'); var z = require('./hasZ'); -// has('foo') +// !has('foo') // elided: import 'import' has.default('foo'); diff --git a/tests/support/fixtures/no-import.js b/tests/support/fixtures/no-import.js index 72f4e00c..29e42f88 100644 --- a/tests/support/fixtures/no-import.js +++ b/tests/support/fixtures/no-import.js @@ -1,5 +1,5 @@ var x = require('y'); var z = require('./hasZ'); -"has('foo')"; +"!has('foo')"; require('import'); has.default('foo'); diff --git a/tests/support/fixtures/static-has-base.js b/tests/support/fixtures/static-has-base.js index 2ed39009..13da57d2 100644 --- a/tests/support/fixtures/static-has-base.js +++ b/tests/support/fixtures/static-has-base.js @@ -5,26 +5,26 @@ const value = notRequire('something/has'); const somename = require('something/has'), chainedDeclarations = true; const afterHasRequire = true; -'has("foo")'; +'!has("foo")'; "use strict"; exports.__esModule = true; require("foo"); -'!has("bar")'; +'has("bar")'; require("bar"); require("baz"); -"has('qat')"; +"!has('qat')"; require("qat"); -"has('qat')"; +"!has('qat')"; const foo = 'bar'; require(foo); require('foo'); "!has('baz')"; require("qat"); -"!has('bar')"; +"has('bar')"; var importedValue = require('bar'); -`!has('bar')`; +`has('bar')`; import another from 'default-import'; -"!has('bar')"; +"has('bar')"; import 'no-var-import'; var newVar = ''; @@ -68,5 +68,5 @@ if (somename.default('foo')) var variable = somename.default('bar') || returnArg(somename.default('foo')); -'has("foo")'; +'!has("foo")'; require('elided'); diff --git a/tests/support/fixtures/static-has-foo-true-bar-false.js b/tests/support/fixtures/static-has-foo-true-bar-false.js index d4f3339a..227ab4d0 100644 --- a/tests/support/fixtures/static-has-foo-true-bar-false.js +++ b/tests/support/fixtures/static-has-foo-true-bar-false.js @@ -1,4 +1,4 @@ -// has('foo') +// !has('foo') // elided: import 'elided' const { destructured } = { destructured: 1 }; const someOtherVariable = require('something/someothermodule'); @@ -7,27 +7,27 @@ const value = notRequire('something/has'); const somename = require('something/has'), chainedDeclarations = true; const afterHasRequire = true; -// has('foo') +// !has('foo') "use strict"; exports.__esModule = true; // elided: import 'foo' -// !has('bar') +// has('bar') // elided: import 'bar' require("baz"); -// has('qat') +// !has('qat') require("qat"); -// has('qat') +// !has('qat') const foo = 'bar'; require(foo); require('foo'); // !has('baz') require("qat"); -// !has('bar') +// has('bar') // elided: import 'bar' -// !has('bar') +// has('bar') // elided: import 'default-import' -// !has('bar') +// has('bar') // elided: import 'no-var-import' var newVar = ''; somename.default.add('foo'); diff --git a/tests/support/fixtures/static-has-no-flags.js b/tests/support/fixtures/static-has-no-flags.js index 968acdd1..9da0014d 100644 --- a/tests/support/fixtures/static-has-no-flags.js +++ b/tests/support/fixtures/static-has-no-flags.js @@ -5,7 +5,7 @@ const value = notRequire('something/has'); const somename = require('something/has'), chainedDeclarations = true; const afterHasRequire = true; -// has('foo') +// !has('foo') "use strict"; exports.__esModule = true; require("foo"); @@ -18,13 +18,13 @@ require("qat"); const foo = 'bar'; require(foo); require('foo'); -// !has('baz') +// has('baz') require("qat"); -// !has('bar') +// has('bar') var importedValue = require('bar'); -// !has('bar') +// has('bar') import another from 'default-import'; -// !has('bar') +// has('bar') import 'no-var-import'; var newVar = ''; @@ -68,5 +68,5 @@ if (somename.default('foo')) var variable = somename.default('bar') || returnArg(somename.default('foo')); -// has('foo') +// !has('foo') require('elided'); diff --git a/tests/support/fixtures/static-has-qat-true.js b/tests/support/fixtures/static-has-qat-true.js index 85cc4602..c982946c 100644 --- a/tests/support/fixtures/static-has-qat-true.js +++ b/tests/support/fixtures/static-has-qat-true.js @@ -5,26 +5,26 @@ const value = notRequire('something/has'); const somename = require('something/has'), chainedDeclarations = true; const afterHasRequire = true; -// has('foo') +// !has('foo') "use strict"; exports.__esModule = true; require("foo"); -// !has('bar') +// has('bar') require("bar"); require("baz"); -// has('qat') +// !has('qat') // elided: import 'qat' -// has('qat') +// !has('qat') const foo = 'bar'; require(foo); // elided: import 'foo' // !has('baz') require("qat"); -// !has('bar') +// has('bar') var importedValue = require('bar'); -// !has('bar') +// has('bar') import another from 'default-import'; -// !has('bar') +// has('bar') import 'no-var-import'; var newVar = ''; @@ -68,5 +68,5 @@ if (somename.default('foo')) var variable = somename.default('bar') || returnArg(somename.default('foo')); -// has('foo') +// !has('foo') require('elided'); diff --git a/tests/unit/static-build-loader/getFeatures.ts b/tests/unit/static-build-loader/getFeatures.ts index 9f0b84fe..799575a2 100644 --- a/tests/unit/static-build-loader/getFeatures.ts +++ b/tests/unit/static-build-loader/getFeatures.ts @@ -10,67 +10,46 @@ registerSuite('getFeatures', { }, 'single feature set'() { - assert.deepEqual(getFeatures('ie11'), { + assert.deepEqual(getFeatures('modern'), { arraybuffer: true, blob: true, - 'dom-mutationobserver': false, - 'dom-webanimation': false, - 'dom-pointer-events': true, - 'dom-intersection-observer': false, + 'dom-mutationobserver': true, 'es-observable': false, - 'es2017-object': false, - 'es2017-string': false, - 'es6-array': false, - 'es6-array-fill': false, - 'es6-map': false, - 'es6-math': false, - 'es6-math-imul': false, - 'es6-object': false, - 'es6-promise': false, - 'es6-set': false, - 'es6-string': false, - 'es6-string-raw': false, - 'es6-symbol': false, - 'es6-weakmap': false, - 'es7-array': false, - fetch: false, + 'es2017-object': true, + 'es2017-string': true, + 'es6-array': true, + 'es6-array-fill': true, + 'es6-map': true, + 'es6-math': true, + 'es6-math-imul': true, + 'es6-object': true, + 'es6-promise': true, + 'es6-set': true, + 'es6-string': true, + 'es6-string-raw': true, + 'es6-symbol': true, + 'es6-weakmap': true, + 'es7-array': true, + fetch: true, filereader: true, float32array: true, - formdata: false, + formdata: true, 'host-node': false, 'host-browser': true, microtasks: true, 'node-buffer': false, - 'object-assign': false, + 'object-assign': true, postmessage: true, raf: true, - setimmediate: true, + setimmediate: false, xhr: true, xhr2: true }); }, - 'two feature sets'() { - assert.deepEqual(getFeatures(['ie11', 'node']), { - arraybuffer: true, - blob: true, - 'dom-intersection-observer': false, - 'dom-mutationobserver': false, - 'dom-webanimation': false, - 'es2017-object': false, - 'es2017-string': false, - 'es-observable': false, - fetch: false, - float32array: true, - formdata: false, - microtasks: true, - setimmediate: true - }); - }, - 'not found feature set'() { const logStub = stub(console, 'log'); - const features = getFeatures(['ie11', 'foo']); + const features = getFeatures(['foo']); logStub.restore(); assert.deepEqual(features, {}); assert.isTrue(logStub.calledOnce, 'log should have been called'); From 45e5cec8882a5a85aaab2b2c8968796a1db6aa67 Mon Sep 17 00:00:00 2001 From: Anthony Gubler Date: Tue, 27 Nov 2018 13:39:01 +0000 Subject: [PATCH 4/4] Replace variable declarations with an undefined declaration --- src/static-build-loader/loader.ts | 29 ++++++++++++++++--- src/static-build-loader/recast.d.ts | 3 ++ tests/support/fixtures/static-has-base.js | 2 +- .../fixtures/static-has-foo-true-bar-false.js | 2 ++ tests/support/fixtures/static-has-no-flags.js | 2 +- tests/support/fixtures/static-has-qat-true.js | 2 +- 6 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/static-build-loader/loader.ts b/src/static-build-loader/loader.ts index 4981f30e..4d6b356b 100644 --- a/src/static-build-loader/loader.ts +++ b/src/static-build-loader/loader.ts @@ -52,11 +52,12 @@ function setComment( path: recast.Path, comment: string, parentPath: recast.Path, - name: string + name: string, + replacement: any = null ) { const next = (Array.isArray(parentPath.value) && parentPath.value[Number(name) + 1]) || parentPath.node; next.comments = [...((node as any).comments || []), ...(next.comments || []), builders.commentLine(comment)]; - path.replace(null); + path.replace(replacement); } /** @@ -157,7 +158,17 @@ export default function loader(this: LoaderContext, content: string, sourceMap?: elideNextImport = false; } if (comment && parentPath && typeof name !== 'undefined') { - setComment(node, path, comment, parentPath, name); + let replacement: any = null; + if (path.node.specifiers.length) { + replacement = builders.variableDeclaration( + 'var', + path.node.specifiers.map((specifier) => { + return builders.variableDeclarator(specifier.local, builders.identifier('undefined')); + }) + ); + } + + setComment(node, path, comment, parentPath, name, replacement); comment = undefined; return false; } @@ -183,6 +194,8 @@ export default function loader(this: LoaderContext, content: string, sourceMap?: node: { declarations } } = path; + let identifier: any = undefined; + if (elideNextImport === true && declarations.length === 1) { const callExpression = declarations[0]; if (namedTypes.VariableDeclarator.check(callExpression)) { @@ -195,6 +208,10 @@ export default function loader(this: LoaderContext, content: string, sourceMap?: callExpression.init.callee.name === 'require' && callExpression.init.arguments.length === 1 ) { + if (callExpression.id) { + identifier = callExpression.id; + } + const [arg] = callExpression.init.arguments; if (namedTypes.Literal.check(arg)) { comment = ` elided: import '${arg.value}'`; @@ -205,7 +222,11 @@ export default function loader(this: LoaderContext, content: string, sourceMap?: } if (comment && parentPath && typeof name !== 'undefined') { - setComment(node, path, comment, parentPath, name); + const replacement = builders.variableDeclaration('var', [ + builders.variableDeclarator(identifier, builders.identifier('undefined')) + ]); + setComment(node, path, comment, parentPath, name, replacement); + comment = undefined; return false; } diff --git a/src/static-build-loader/recast.d.ts b/src/static-build-loader/recast.d.ts index f5638a09..44829ec0 100644 --- a/src/static-build-loader/recast.d.ts +++ b/src/static-build-loader/recast.d.ts @@ -62,8 +62,11 @@ declare module 'recast/main' { export const namedTypes: { [type in keyof NamedTypes]: NamedType }; export const builders: { + identifier(id: string): Identifier; commentLine(comment: string, trailing?: boolean, leading?: boolean): Comment; literal(value: boolean | string | number | null | RegExp): Literal; + variableDeclarator(id: Identifier, value: Literal | Identifier): VariableDeclarator; + variableDeclaration(value: string, declarators: VariableDeclarator[]): VariableDeclaration; }; export function visit( ast: AST, diff --git a/tests/support/fixtures/static-has-base.js b/tests/support/fixtures/static-has-base.js index 13da57d2..4c364809 100644 --- a/tests/support/fixtures/static-has-base.js +++ b/tests/support/fixtures/static-has-base.js @@ -23,7 +23,7 @@ require("qat"); "has('bar')"; var importedValue = require('bar'); `has('bar')`; -import another from 'default-import'; +import another, { namedExport } from 'default-import'; "has('bar')"; import 'no-var-import'; diff --git a/tests/support/fixtures/static-has-foo-true-bar-false.js b/tests/support/fixtures/static-has-foo-true-bar-false.js index 227ab4d0..4db497db 100644 --- a/tests/support/fixtures/static-has-foo-true-bar-false.js +++ b/tests/support/fixtures/static-has-foo-true-bar-false.js @@ -22,6 +22,8 @@ require(foo); require('foo'); // !has('baz') require("qat"); +var importedValue = undefined; +var another = undefined, namedExport = undefined; // has('bar') // elided: import 'bar' diff --git a/tests/support/fixtures/static-has-no-flags.js b/tests/support/fixtures/static-has-no-flags.js index 9da0014d..07f2e56a 100644 --- a/tests/support/fixtures/static-has-no-flags.js +++ b/tests/support/fixtures/static-has-no-flags.js @@ -23,7 +23,7 @@ require("qat"); // has('bar') var importedValue = require('bar'); // has('bar') -import another from 'default-import'; +import another, { namedExport } from 'default-import'; // has('bar') import 'no-var-import'; diff --git a/tests/support/fixtures/static-has-qat-true.js b/tests/support/fixtures/static-has-qat-true.js index c982946c..edbd8a9c 100644 --- a/tests/support/fixtures/static-has-qat-true.js +++ b/tests/support/fixtures/static-has-qat-true.js @@ -23,7 +23,7 @@ require("qat"); // has('bar') var importedValue = require('bar'); // has('bar') -import another from 'default-import'; +import another, { namedExport } from 'default-import'; // has('bar') import 'no-var-import';