From d13c745e257e1361a874adfebf3ba03b6bb48284 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 12 May 2021 18:03:15 +0200 Subject: [PATCH] refactor: Use babel AST for polyfill --- index.js | 28 +++++++-- lib/replace-imports-preprocessor.js | 22 ------- lib/transpile-modules.js | 93 +++++++++++++++++++++++++++++ package.json | 1 - tests/unit/renamed-import-test.ts | 33 ++++++++++ yarn.lock | 56 +---------------- 6 files changed, 149 insertions(+), 84 deletions(-) delete mode 100644 lib/replace-imports-preprocessor.js create mode 100644 lib/transpile-modules.js create mode 100644 tests/unit/renamed-import-test.ts diff --git a/index.js b/index.js index e371440..6fee715 100644 --- a/index.js +++ b/index.js @@ -1,15 +1,31 @@ 'use strict'; -const ReplaceImportsPreprocessor = require('./lib/replace-imports-preprocessor'); +const { resolve } = require('path'); module.exports = { name: require('./package').name, - setupPreprocessorRegistry(type, registry) { - if (type !== 'parent') { - return; - } + included() { + this._super.included.apply(this, arguments); - registry.add('js', new ReplaceImportsPreprocessor()); + this.patchEmberModulesAPIPolyfill(); + }, + + patchEmberModulesAPIPolyfill() { + const babel = this.parent.findOwnAddonByName + ? this.parent.findOwnAddonByName('ember-cli-babel') // parent is an addon + : this.parent.findAddonByName('ember-cli-babel'); // parent is an app + + if (babel.__CachedDecoratorPolyfillApplied) return; + babel.__CachedDecoratorPolyfillApplied = true; + + const { _getEmberModulesAPIPolyfill } = babel; + + babel._getEmberModulesAPIPolyfill = function (...args) { + const plugins = _getEmberModulesAPIPolyfill.apply(this, args); + if (!plugins) return; + + return [[resolve(__dirname, './lib/transpile-modules.js')], ...plugins]; + }; } }; diff --git a/lib/replace-imports-preprocessor.js b/lib/replace-imports-preprocessor.js deleted file mode 100644 index 853a914..0000000 --- a/lib/replace-imports-preprocessor.js +++ /dev/null @@ -1,22 +0,0 @@ -const BroccoliReplace = require('broccoli-replace'); - -module.exports = class ReplaceImportsPreprocessor { - name = 'ember-cached-decorator-polyfill-preprocessor'; - - toTree(tree) { - return new BroccoliReplace(tree, { - files: ['**/*.js', '**/*.ts'], - patterns: [ - { - match: /import\s+{[\s(tracked,)]*cached[\s(,tracked)]*}\s*from\s*['"]@glimmer\/tracking['"]/m, - replacement: str => { - return str.includes('tracked') - ? `import { tracked } from '@glimmer/tracking'; -import { cached } from 'ember-cached-decorator-polyfill';` - : `import { cached } from 'ember-cached-decorator-polyfill';`; - } - } - ] - }); - } -}; diff --git a/lib/transpile-modules.js b/lib/transpile-modules.js new file mode 100644 index 0000000..a1078eb --- /dev/null +++ b/lib/transpile-modules.js @@ -0,0 +1,93 @@ +'use strict'; + +const path = require('path'); + +/** + * Based on `babel-plugin-ember-modules-api-polyfill`. + * @see https://github.com/ember-cli/babel-plugin-ember-modules-api-polyfill/blob/master/src/index.js + */ +module.exports = function (babel) { + const t = babel.types; + + const MODULE = '@glimmer/tracking'; + const IMPORT = 'cached'; + const REPLACED_MODULE = 'ember-cached-decorator-polyfill'; + + return { + name: 'ember-cache-decorator-polyfill', + visitor: { + ImportDeclaration(path) { + let node = path.node; + let declarations = []; + let removals = []; + let specifiers = path.get('specifiers'); + let importPath = node.source.value; + + // Only walk specifiers if this is a module we have a mapping for + if (importPath === MODULE) { + // Iterate all the specifiers and attempt to locate their mapping + specifiers.forEach(specifierPath => { + let specifier = specifierPath.node; + let importName; + + // imported is the name of the module being imported, e.g. import foo from bar + const imported = specifier.imported; + + // local is the name of the module in the current scope, this is usually the same + // as the imported value, unless the module is aliased + const local = specifier.local; + + // We only care about these 2 specifiers + if ( + specifier.type !== 'ImportDefaultSpecifier' && + specifier.type !== 'ImportSpecifier' + ) { + if (specifier.type === 'ImportNamespaceSpecifier') { + throw new Error( + `Using \`import * as ${specifier.local.name} from '${importPath}'\` is not supported.` + ); + } + return; + } + + // Determine the import name, either default or named + if (specifier.type === 'ImportDefaultSpecifier') { + importName = 'default'; + } else { + importName = imported.name; + } + + if (importName !== IMPORT) return; + + removals.push(specifierPath); + + declarations.push( + t.importDeclaration( + [ + t.importSpecifier( + t.identifier(local.name), + t.identifier(IMPORT) + ) + ], + t.stringLiteral(REPLACED_MODULE) + ) + ); + }); + } + + if (removals.length > 0) { + if (removals.length === node.specifiers.length) { + path.replaceWithMultiple(declarations); + } else { + removals.forEach(specifierPath => specifierPath.remove()); + path.insertAfter(declarations); + } + } + } + } + }; +}; + +// Provide the path to the package's base directory for caching with broccoli +// Ref: https://github.com/babel/broccoli-babel-transpiler#caching +module.exports.baseDir = () => path.resolve(__dirname, '..'); diff --git a/package.json b/package.json index 648822b..c5dbfee 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ }, "dependencies": { "@glimmer/tracking": "^1.0.4", - "broccoli-replace": "^2.0.2", "ember-cache-primitive-polyfill": "^1.0.1", "ember-cli-babel": "^7.21.0" }, diff --git a/tests/unit/renamed-import-test.ts b/tests/unit/renamed-import-test.ts new file mode 100644 index 0000000..400c344 --- /dev/null +++ b/tests/unit/renamed-import-test.ts @@ -0,0 +1,33 @@ +import { module, test } from 'qunit'; +import { tracked, cached as localCached } from '@glimmer/tracking'; + +module('Unit | Import | renamed import', function () { + test('it works', function (assert) { + class Person { + @tracked firstName = 'Jen'; + lastName = 'Weber'; + + @localCached + get fullName() { + const fullName = `${this.firstName} ${this.lastName}`; + assert.step(fullName); + return fullName; + } + } + + const person = new Person(); + assert.verifySteps([], 'getter is not called after class initialization'); + + assert.strictEqual(person.fullName, 'Jen Weber'); + assert.verifySteps( + ['Jen Weber'], + 'getter was called after property access' + ); + + assert.strictEqual(person.fullName, 'Jen Weber'); + assert.verifySteps( + [], + 'getter was not called again after repeated property access' + ); + }); +}); diff --git a/yarn.lock b/yarn.lock index 47828f3..196ddcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2005,17 +2005,6 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -applause@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/applause/-/applause-2.0.4.tgz#0b771418fdd1bb326a225ad031d41ba0b20cd0f4" - integrity sha512-wFhNjSoflbAEgelX3psyKSXV2iQFjuYW31DEhcCOD/bQ98VdfltLclK4p1mI6E58Qp4Q7+5RCbBdr+Nc9b5QhA== - dependencies: - lodash "^4.17.21" - optional-require "^1.0.2" - optionalDependencies: - cson-parser "^4.0.8" - js-yaml "^4.0.0" - aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -2036,11 +2025,6 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -3246,7 +3230,7 @@ broccoli-file-creator@^2.1.1: broccoli-plugin "^1.1.0" mkdirp "^0.5.1" -broccoli-filter@^1.2.2, broccoli-filter@^1.2.3, broccoli-filter@^1.3.0: +broccoli-filter@^1.2.2, broccoli-filter@^1.2.3: version "1.3.0" resolved "https://registry.yarnpkg.com/broccoli-filter/-/broccoli-filter-1.3.0.tgz#71e3a8e32a17f309e12261919c5b1006d6766de6" integrity sha512-VXJXw7eBfG82CFxaBDjYmyN7V72D4In2zwLVQJd/h3mBfF3CMdRTsv2L20lmRTtCv1sAHcB+LgMso90e/KYiLw== @@ -3509,15 +3493,6 @@ broccoli-plugin@^4.0.1, broccoli-plugin@^4.0.2, broccoli-plugin@^4.0.3: rimraf "^3.0.0" symlink-or-copy "^1.3.0" -broccoli-replace@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/broccoli-replace/-/broccoli-replace-2.0.2.tgz#eb40046bb124a7f651486aece947ce28bbec30b1" - integrity sha512-1e8uyGUo8HqiKKB4oWz5nUX1rlLSRgShLxczuwSXJlmGljVWerDGF0oW5VshGAuKKYkAoDsI3Cc0TKEgo4SWTg== - dependencies: - applause "^2.0.0" - broccoli-filter "^1.3.0" - minimatch "^3.0.4" - broccoli-slow-trees@^3.0.1, broccoli-slow-trees@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/broccoli-slow-trees/-/broccoli-slow-trees-3.1.0.tgz#8e48903f59e061bf1213963733b9e61dec2ee5d7" @@ -4181,11 +4156,6 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -coffeescript@1.12.7: - version "1.12.7" - resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-1.12.7.tgz#e57ee4c4867cf7f606bfc4a0f2d550c0981ddd27" - integrity sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA== - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -4564,13 +4534,6 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -cson-parser@^4.0.8: - version "4.0.9" - resolved "https://registry.yarnpkg.com/cson-parser/-/cson-parser-4.0.9.tgz#eef0cf77edd057f97861ef800300c8239224eedb" - integrity sha512-I79SAcCYquWnEfXYj8hBqOOWKj6eH6zX1hhX3yqmS4K3bYp7jME3UFpHPzu3rUew0oyfc0s8T6IlWGXRAheHag== - dependencies: - coffeescript "1.12.7" - cssom@0.3.x, cssom@^0.3.4: version "0.3.8" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" @@ -7963,13 +7926,6 @@ js-yaml@^3.13.1, js-yaml@^3.2.5, js-yaml@^3.2.7: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -8496,11 +8452,6 @@ lodash@4.17.20, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17. resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -9343,11 +9294,6 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -optional-require@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/optional-require/-/optional-require-1.0.3.tgz#275b8e9df1dc6a17ad155369c2422a440f89cb07" - integrity sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA== - optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"