Skip to content

Commit 1d459ed

Browse files
committed
refactor: Use preprocessor for polyfill
1 parent 5759283 commit 1d459ed

File tree

10 files changed

+227
-284
lines changed

10 files changed

+227
-284
lines changed

addon/index.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { createCache, getValue } from '@glimmer/tracking/primitives/cache';
2+
import { assert } from '@ember/debug';
3+
4+
export function cached(...args) {
5+
const [target, key, descriptor] = args;
6+
7+
// Error on `@cached()`, `@cached(...args)`, and `@cached propName = value;`
8+
assert(
9+
'You attempted to use @cached(), which is not necessary nor supported. Remove the parentheses and you will be good to go!',
10+
target !== undefined
11+
);
12+
assert(
13+
`You attempted to use @cached on with ${
14+
args.length > 1 ? 'arguments' : 'an argument'
15+
} ( @cached(${args
16+
.map(d => `'${d}'`)
17+
.join(
18+
', '
19+
)}), which is not supported. Dependencies are automatically tracked, so you can just use ${'`@cached`'}`,
20+
typeof target === 'object' &&
21+
typeof key === 'string' &&
22+
typeof descriptor === 'object' &&
23+
args.length === 3
24+
);
25+
assert(
26+
`The @cached decorator must be applied to getters. '${key}' is not a getter.`,
27+
typeof descriptor.get == 'function'
28+
);
29+
30+
const caches = new WeakMap();
31+
const getter = descriptor.get;
32+
descriptor.get = function () {
33+
if (!caches.has(this)) caches.set(this, createCache(getter.bind(this)));
34+
return getValue(caches.get(this));
35+
};
36+
}

index.js

Lines changed: 5 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,15 @@
11
'use strict';
22

3-
const { resolve } = require('path');
3+
const ReplaceImportsPreprocessor = require('./lib/replace-imports-preprocessor');
44

55
module.exports = {
66
name: require('./package').name,
77

8-
included() {
9-
this._super.included.apply(this, arguments);
10-
this._ensureThisImport();
11-
12-
this.import('vendor/ember-cached-decorator-polyfill/index.js');
13-
this.patchEmberModulesAPIPolyfill();
14-
},
15-
16-
treeForVendor(tree) {
17-
const babel = this.addons.find(a => a.name === 'ember-cli-babel');
18-
19-
return babel.transpileTree(tree, {
20-
babel: this.options.babel,
21-
22-
'ember-cli-babel': {
23-
compileModules: false
24-
}
25-
});
26-
},
27-
28-
_ensureThisImport() {
29-
if (!this.import) {
30-
this._findHost = function findHostShim() {
31-
let current = this;
32-
let app;
33-
do {
34-
app = current.app || app;
35-
// eslint-disable-next-line no-cond-assign
36-
} while (current.parent.parent && (current = current.parent));
37-
return app;
38-
};
39-
this.import = function importShim(asset, options) {
40-
const app = this._findHost();
41-
app.import(asset, options);
42-
};
8+
setupPreprocessorRegistry(type, registry) {
9+
if (type !== 'parent') {
10+
return;
4311
}
44-
},
45-
46-
patchEmberModulesAPIPolyfill() {
47-
const babel = this.parent.findOwnAddonByName
48-
? this.parent.findOwnAddonByName('ember-cli-babel') // parent is an addon
49-
: this.parent.findAddonByName('ember-cli-babel'); // parent is an app
50-
51-
if (babel.__CachedDecoratorPolyfillApplied) return;
52-
babel.__CachedDecoratorPolyfillApplied = true;
53-
54-
const { _getEmberModulesAPIPolyfill } = babel;
55-
babel._getEmberModulesAPIPolyfill = function (...args) {
56-
const plugins = _getEmberModulesAPIPolyfill.apply(this, args);
57-
if (!plugins) return;
5812

59-
return [[resolve(__dirname, './lib/transpile-modules.js')], ...plugins];
60-
};
13+
registry.add('js', new ReplaceImportsPreprocessor());
6114
}
6215
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const BroccoliReplace = require('broccoli-replace');
2+
3+
module.exports = class ReplaceImportsPreprocessor {
4+
name = 'ember-cached-decorator-polyfill-preprocessor';
5+
6+
toTree(tree) {
7+
return new BroccoliReplace(tree, {
8+
files: ['**/*.js', '**/*.ts'],
9+
patterns: [
10+
{
11+
match: /import\s+{[\s(tracked,)]*cached[\s(,tracked)]*}\s*from\s*['"]@glimmer\/tracking['"]/m,
12+
replacement: str => {
13+
return str.includes('tracked')
14+
? `import { tracked } from '@glimmer/tracking';
15+
import { cached } from 'ember-cached-decorator-polyfill';`
16+
: `import { cached } from 'ember-cached-decorator-polyfill';`;
17+
}
18+
}
19+
]
20+
});
21+
}
22+
};

lib/transpile-modules.js

Lines changed: 0 additions & 182 deletions
This file was deleted.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@
3939
"test:ember-compatibility": "ember try:each"
4040
},
4141
"dependencies": {
42+
"@glimmer/tracking": "^1.0.4",
43+
"broccoli-replace": "^2.0.2",
4244
"ember-cache-primitive-polyfill": "^1.0.1",
4345
"ember-cli-babel": "^7.21.0"
4446
},
4547
"devDependencies": {
4648
"@ember/optional-features": "^2.0.0",
4749
"@glimmer/component": "^1.0.1",
48-
"@glimmer/tracking": "^1.0.0",
4950
"@types/ember": "^3.16.0",
5051
"@types/ember-qunit": "^3.4.9",
5152
"@types/ember-resolver": "^5.0.9",

tests/unit/followed-import-test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { module, test } from 'qunit';
2+
import { cached, tracked } from '@glimmer/tracking';
3+
4+
module('Unit | Import | followed import', function () {
5+
test('it works', function (assert) {
6+
class Person {
7+
@tracked firstName = 'Jen';
8+
lastName = 'Weber';
9+
10+
@cached
11+
get fullName() {
12+
const fullName = `${this.firstName} ${this.lastName}`;
13+
assert.step(fullName);
14+
return fullName;
15+
}
16+
}
17+
18+
const person = new Person();
19+
assert.verifySteps([], 'getter is not called after class initialization');
20+
21+
assert.strictEqual(person.fullName, 'Jen Weber');
22+
assert.verifySteps(
23+
['Jen Weber'],
24+
'getter was called after property access'
25+
);
26+
27+
assert.strictEqual(person.fullName, 'Jen Weber');
28+
assert.verifySteps(
29+
[],
30+
'getter was not called again after repeated property access'
31+
);
32+
});
33+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { module, test } from 'qunit';
2+
// prettier-ignore
3+
import {
4+
cached,
5+
tracked
6+
} from "@glimmer/tracking";
7+
8+
module('Unit | Import | multi-line import', function () {
9+
test('it works', function (assert) {
10+
class Person {
11+
@tracked firstName = 'Jen';
12+
lastName = 'Weber';
13+
14+
@cached
15+
get fullName() {
16+
const fullName = `${this.firstName} ${this.lastName}`;
17+
assert.step(fullName);
18+
return fullName;
19+
}
20+
}
21+
22+
const person = new Person();
23+
assert.verifySteps([], 'getter is not called after class initialization');
24+
25+
assert.strictEqual(person.fullName, 'Jen Weber');
26+
assert.verifySteps(
27+
['Jen Weber'],
28+
'getter was called after property access'
29+
);
30+
31+
assert.strictEqual(person.fullName, 'Jen Weber');
32+
assert.verifySteps(
33+
[],
34+
'getter was not called again after repeated property access'
35+
);
36+
});
37+
});

0 commit comments

Comments
 (0)