Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] Implements Decorators #17548

Merged
merged 4 commits into from
Feb 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const path = require('path');

module.exports = {
root: true,
parser: 'babel-eslint',
extends: [
'eslint:recommended',
'prettier',
Expand Down
10 changes: 9 additions & 1 deletion broccoli/packages.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const WriteFile = require('broccoli-file-creator');
const StringReplace = require('broccoli-string-replace');
const GlimmerTemplatePrecompiler = require('./glimmer-template-compiler');
const VERSION_PLACEHOLDER = /VERSION_STRING_PLACEHOLDER/g;
const transfromBabelPlugins = require('./transforms/transform-babel-plugins');
pzuraq marked this conversation as resolved.
Show resolved Hide resolved

const debugTree = BroccoliDebug.buildDebugCallback('ember-source');

Expand Down Expand Up @@ -83,6 +84,13 @@ module.exports.getPackagesES = function getPackagesES() {
exclude: ['**/*.ts'],
});

// tsc / typescript handles decorators and class properties on its own
// so for non ts, transpile the proposal features (decorators, etc)
let transpiledProposals = debugTree(
transfromBabelPlugins(debugTree(nonTypeScriptContents, `get-packages-es:babel-plugins:input`)),
pzuraq marked this conversation as resolved.
Show resolved Hide resolved
`get-packages-es:babel-plugins:output`
);

let typescriptContents = new Funnel(debuggedCompiledTemplatesAndTypeScript, {
include: ['**/*.ts'],
});
Expand All @@ -95,7 +103,7 @@ module.exports.getPackagesES = function getPackagesES() {

let debuggedCompiledTypescript = debugTree(typescriptCompiled, `get-packages-es:ts:output`);

let mergedFinalOutput = new MergeTrees([nonTypeScriptContents, debuggedCompiledTypescript], {
let mergedFinalOutput = new MergeTrees([transpiledProposals, debuggedCompiledTypescript], {
overwrite: true,
});

Expand Down
20 changes: 20 additions & 0 deletions broccoli/test-polyfills.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const Rollup = require('broccoli-rollup');
const writeFile = require('broccoli-file-creator');
const resolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');

module.exports = function polyfills() {
let polyfillEntry = writeFile('polyfill-entry.js', 'require("core-js/modules/es6.symbol");');

return new Rollup(polyfillEntry, {
rollup: {
input: 'polyfill-entry.js',
output: {
file: 'polyfill.js',
name: 'polyfill',
format: 'iife',
},
plugins: [resolve(), commonjs()],
},
});
};
13 changes: 13 additions & 0 deletions broccoli/transforms/transform-babel-plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const Babel = require('broccoli-babel-transpiler');

module.exports = function(tree) {
let options = {
sourceMaps: true,
plugins: [
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true, legacy: false }],
['@babel/plugin-proposal-class-properties'],
],
};

return new Babel(tree, options);
};
2 changes: 2 additions & 0 deletions ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const bootstrapModule = require('./broccoli/bootstrap-modules');
const concatBundle = require('./broccoli/concat-bundle');
const concat = require('broccoli-concat');
const testIndexHTML = require('./broccoli/test-index-html');
const testPolyfills = require('./broccoli/test-polyfills');
const toES5 = require('./broccoli/to-es5');
const toNamedAMD = require('./broccoli/to-named-amd');
const stripForProd = require('./broccoli/strip-for-prod');
Expand Down Expand Up @@ -141,6 +142,7 @@ module.exports = function() {
nodeTests(),

// test harness
testPolyfills(),
testIndexHTML(),
jquery(),
qunit(),
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
},
"devDependencies": {
"@babel/helper-module-imports": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.2.1",
"@babel/plugin-proposal-decorators": "^7.2.0",
"@babel/plugin-transform-arrow-functions": "^7.2.0",
"@babel/plugin-transform-block-scoping": "^7.2.0",
"@babel/plugin-transform-classes": "^7.2.2",
Expand All @@ -101,6 +103,7 @@
"@types/rsvp": "^4.0.2",
"auto-dist-tag": "^1.0.0",
"aws-sdk": "^2.399.0",
"babel-eslint": "^10.0.1",
"babel-plugin-debug-macros": "^0.3.0",
"babel-plugin-filter-imports": "^2.0.4",
"babel-plugin-module-resolver": "^3.1.3",
Expand All @@ -118,6 +121,7 @@
"broccoli-typescript-compiler": "^4.0.1",
"broccoli-uglify-sourcemap": "^2.2.0",
"common-tags": "^1.8.0",
"core-js": "^2.6.3",
"dag-map": "^2.0.2",
"ember-cli": "^3.7.1",
"ember-cli-blueprint-test-helpers": "^0.19.2",
Expand Down Expand Up @@ -147,6 +151,8 @@
"prettier": "1.16.4",
"puppeteer": "^1.12.2",
"qunit": "^2.9.1",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-node-resolve": "^4.0.0",
"route-recognizer": "^0.3.4",
"router_js": "^6.2.3",
"rsvp": "^4.8.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { set, computed } from '@ember/-internals/metal';
import { getDebugFunction, setDebugFunction } from '@ember/debug';
import { readOnly } from '@ember/object/computed';
import { Object as EmberObject, ObjectProxy } from '@ember/-internals/runtime';
import { HAS_NATIVE_SYMBOL } from '@ember/-internals/utils';
import { constructStyleDeprecationMessage } from '@ember/-internals/views';
import { Component, SafeString, htmlSafe } from '../utils/helpers';

Expand Down Expand Up @@ -682,7 +683,7 @@ let GlimmerContentTestCases = new ContentTestGenerator([
[Object.create(null), EMPTY, 'an object with no toString'],
]);

if (typeof Symbol !== 'undefined') {
if (HAS_NATIVE_SYMBOL) {
GlimmerContentTestCases.cases.push([Symbol('debug'), 'Symbol(debug)', 'a symbol']);
}

Expand Down
2 changes: 0 additions & 2 deletions packages/@ember/-internals/meta/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
export {
counters,
deleteMeta,
descriptorFor,
isDescriptor,
Meta,
meta,
MetaCounters,
Expand Down
42 changes: 0 additions & 42 deletions packages/@ember/-internals/meta/lib/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -965,48 +965,6 @@ if (DEBUG) {
meta._counters = counters;
}

/**
Returns the CP descriptor assocaited with `obj` and `keyName`, if any.

@method descriptorFor
@param {Object} obj the object to check
@param {String} keyName the key to check
@return {Descriptor}
@private
*/
export function descriptorFor(obj: object, keyName: string, _meta?: Meta | null) {
assert('Cannot call `descriptorFor` on null', obj !== null);
assert('Cannot call `descriptorFor` on undefined', obj !== undefined);
assert(
`Cannot call \`descriptorFor\` on ${typeof obj}`,
typeof obj === 'object' || typeof obj === 'function'
);

let meta = _meta === undefined ? peekMeta(obj) : _meta;

if (meta !== null) {
return meta.peekDescriptors(keyName);
}
}

/**
Check whether a value is a CP descriptor.

@method isDescriptor
@param {any} possibleDesc the value to check
@return {boolean}
@private
*/
export function isDescriptor(possibleDesc: any | undefined | null): boolean {
// TODO make this return `possibleDesc is Descriptor`
return (
possibleDesc !== undefined &&
possibleDesc !== null &&
typeof possibleDesc === 'object' &&
possibleDesc.isDescriptor === true
);
}

export { counters };

function indexOfListener(
Expand Down
13 changes: 9 additions & 4 deletions packages/@ember/-internals/metal/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { default as computed, ComputedProperty, _globalsComputed } from './lib/computed';
export { default as computed, _globalsComputed, ComputedProperty } from './lib/computed';
export { getCacheFor, getCachedValueFor, peekCacheFor } from './lib/computed_cache';
export { default as alias } from './lib/alias';
export { deprecateProperty } from './lib/deprecate_property';
Expand Down Expand Up @@ -28,7 +28,13 @@ export {
overrideChains,
PROPERTY_DID_CHANGE,
} from './lib/property_events';
export { defineProperty, Descriptor } from './lib/properties';
export { defineProperty } from './lib/properties';
export {
descriptorForProperty,
isComputedDecorator,
setComputedDecorator,
nativeDescDecorator,
} from './lib/decorator';
export { watchKey, unwatchKey } from './lib/watch_key';
export { ChainNode, finishChains, removeChainWatcher } from './lib/chains';
export { watchPath, unwatchPath } from './lib/watch_path';
Expand All @@ -40,10 +46,9 @@ export { default as expandProperties } from './lib/expand_properties';

export { addObserver, removeObserver } from './lib/observer';
export { Mixin, aliasMethod, mixin, observer, applyMixin } from './lib/mixin';
export { default as InjectedProperty } from './lib/injected_property';
export { default as inject, DEBUG_INJECTION_FUNCTIONS } from './lib/injected_property';
export { setHasViews, tagForProperty, tagFor, markObjectAsDirty } from './lib/tags';
export { default as runInTransaction, didRender, assertNotRendered } from './lib/transaction';
export { default as descriptor } from './lib/descriptor';
export { tracked } from './lib/tracked';

export {
Expand Down
57 changes: 39 additions & 18 deletions packages/@ember/-internals/metal/lib/alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,51 @@ import { Meta, meta as metaFor } from '@ember/-internals/meta';
import { inspect } from '@ember/-internals/utils';
import { assert } from '@ember/debug';
import EmberError from '@ember/error';
import { ComputedProperty } from './computed';
import { getCachedValueFor, getCacheFor } from './computed_cache';
import {
addDependentKeys,
DescriptorWithDependentKeys,
ComputedDescriptor,
Decorator,
descriptorForDecorator,
makeComputedDecorator,
removeDependentKeys,
} from './dependent_keys';
import { defineProperty, Descriptor } from './properties';
} from './decorator';
import { defineProperty } from './properties';
import { get } from './property_get';
import { set } from './property_set';

const CONSUMED = Object.freeze({});

export default function alias(altKey: string): AliasedProperty {
return new AliasedProperty(altKey);
export type AliasDecorator = Decorator & PropertyDecorator & AliasDecoratorImpl;

export default function alias(altKey: string): AliasDecorator {
return makeComputedDecorator(new AliasedProperty(altKey), AliasDecoratorImpl) as AliasDecorator;
}

// TODO: This class can be svelted once `meta` has been deprecated
class AliasDecoratorImpl extends Function {
readOnly(this: Decorator) {
(descriptorForDecorator(this) as AliasedProperty).readOnly();
return this;
}

oneWay(this: Decorator) {
(descriptorForDecorator(this) as AliasedProperty).oneWay();
return this;
}

meta(this: Decorator, meta?: any): any {
let prop = descriptorForDecorator(this) as AliasedProperty;

if (arguments.length === 0) {
return prop._meta || {};
} else {
prop._meta = meta;
}
}
}

export class AliasedProperty extends Descriptor implements DescriptorWithDependentKeys {
readonly _dependentKeys: string[];
export class AliasedProperty extends ComputedDescriptor {
readonly altKey: string;

constructor(altKey: string) {
Expand All @@ -29,9 +55,10 @@ export class AliasedProperty extends Descriptor implements DescriptorWithDepende
this._dependentKeys = [altKey];
}

setup(obj: object, keyName: string, meta: Meta): void {
setup(obj: object, keyName: string, propertyDesc: PropertyDescriptor, meta: Meta): void {
assert(`Setting alias '${keyName}' on self`, this.altKey !== keyName);
super.setup(obj, keyName, meta);
super.setup(obj, keyName, propertyDesc, meta);

if (meta.peekWatching(keyName) > 0) {
this.consume(obj, keyName, meta);
}
Expand Down Expand Up @@ -74,14 +101,12 @@ export class AliasedProperty extends Descriptor implements DescriptorWithDepende
return set(obj, this.altKey, value);
}

readOnly(): this {
readOnly(): void {
pzuraq marked this conversation as resolved.
Show resolved Hide resolved
this.set = AliasedProperty_readOnlySet;
return this;
}

oneWay(): this {
oneWay(): void {
this.set = AliasedProperty_oneWaySet;
return this;
}
}

Expand All @@ -94,7 +119,3 @@ function AliasedProperty_oneWaySet(obj: object, keyName: string, value: any): an
defineProperty(obj, keyName, null);
return set(obj, keyName, value);
}

// Backwards compatibility with Ember Data.
(AliasedProperty.prototype as any)._meta = undefined;
(AliasedProperty.prototype as any).meta = ComputedProperty.prototype.meta;
pzuraq marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 3 additions & 2 deletions packages/@ember/-internals/metal/lib/chains.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { descriptorFor, Meta, meta as metaFor, peekMeta } from '@ember/-internals/meta';
import { Meta, meta as metaFor, peekMeta } from '@ember/-internals/meta';
import { getCachedValueFor } from './computed_cache';
import { descriptorForProperty } from './decorator';
import { eachProxyFor } from './each_proxy';
import { get } from './property_get';
import { unwatchKey, watchKey } from './watch_key';
Expand All @@ -9,7 +10,7 @@ function isObject(obj: any): obj is object {
}

function isVolatile(obj: any, keyName: string, meta?: Meta | null): boolean {
let desc = descriptorFor(obj, keyName, meta);
let desc = descriptorForProperty(obj, keyName, meta);
return !(desc !== undefined && desc._volatile === false);
}

Expand Down
Loading