From b0206896a2396ab5394da44fd1a059e849c83f80 Mon Sep 17 00:00:00 2001 From: Vince Chen Date: Mon, 22 Jan 2018 15:29:19 -0800 Subject: [PATCH 1/6] enable @wire to use imported function identifier as adapter id --- .../src/__tests__/wire-adapters.test.js | 8 ++++++++ .../src/__tests__/wire-service.test.js | 14 +++++++++++++- packages/lwc-wire-service/src/wire-service.js | 16 +++++++++++++--- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/lwc-wire-service/src/__tests__/wire-adapters.test.js b/packages/lwc-wire-service/src/__tests__/wire-adapters.test.js index e7369561b4..205e330807 100644 --- a/packages/lwc-wire-service/src/__tests__/wire-adapters.test.js +++ b/packages/lwc-wire-service/src/__tests__/wire-adapters.test.js @@ -22,5 +22,13 @@ describe("wire-adapters.js", () => { buildWireAdapterMap([adapter]); }).toThrow(); }); + + it("accepts function identifier as adapter id", () => { + const myFunc = () => {}; + const adapter = () => { + return { myFunc }; + } + expect(buildWireAdapterMap([adapter]).size).toBe(1); + }); }); }); diff --git a/packages/lwc-wire-service/src/__tests__/wire-service.test.js b/packages/lwc-wire-service/src/__tests__/wire-service.test.js index c42f6b80f6..75a9b7f5e6 100644 --- a/packages/lwc-wire-service/src/__tests__/wire-service.test.js +++ b/packages/lwc-wire-service/src/__tests__/wire-service.test.js @@ -48,8 +48,9 @@ describe("wire-service.js", () => { }); describe("getAdapter()", () => { + const knownFunc = () => {}; const adapters = () => { - return { known: () => { } }; + return { known: () => { }, knownFunc }; }; target.setWireAdapters([adapters]); @@ -63,6 +64,17 @@ describe("wire-service.js", () => { const wireDef = { type: "known" }; target.getAdapter(wireDef, "target"); }); + it("throws with an unknown function identifier adapter id", () => { + const unknownFunc = () => {}; + const wireDef = { adapter: unknownFunc }; + expect(() => { + target.getAdapter(wireDef, "target"); + }).toThrow(); + }); + it("returns with a known function identifier adapter id", () => { + const wireDef = { adapter: knownFunc }; + target.getAdapter(wireDef, "target"); + }); }); describe("getStaticConfig()", () => { diff --git a/packages/lwc-wire-service/src/wire-service.js b/packages/lwc-wire-service/src/wire-service.js index 03b58fa6d3..63007d8bfe 100644 --- a/packages/lwc-wire-service/src/wire-service.js +++ b/packages/lwc-wire-service/src/wire-service.js @@ -50,10 +50,20 @@ export function getPropToParams(wireDef, wireTarget) { * @returns {Function} The wire adapter. */ export function getAdapter(wireDef, wireTarget) { - const adapter = ADAPTERS.get(wireDef.type); - if (!adapter) { - throw new Error(`Unknown wire adapter id '${wireDef.type}' in ${wireTarget}'s @wire('${wireDef.type}, ...)`); + let adapter; + // TODO: deprecate type once consumers have migrate to use function identifier for adapter id. + if (wireDef.type) { + adapter = ADAPTERS.get(wireDef.type); + if (!adapter) { + throw new Error(`Unknown wire adapter id '${wireDef.type}' in ${wireTarget}'s @wire('${wireDef.type}, ...)`); + } + } else if (wireDef.adapter) { + adapter = ADAPTERS.get(wireDef.adapter.name); + if (!adapter) { + throw new Error(`Unknown wire adapter id '${wireDef.adapter.name}' in ${wireTarget}'s @wire('${wireDef.adapter.name}, ...)`); + } } + return adapter; } From d533399c48caa1f949c60d4813c2a54e59617b13 Mon Sep 17 00:00:00 2001 From: Vince Chen Date: Tue, 23 Jan 2018 08:38:01 -0800 Subject: [PATCH 2/6] enable function identifier as wire adapter id in @wire decorator --- .../src/__tests__/wire-decorator.test.js | 38 +++++++++++---- .../src/decorators/wire.js | 48 +++++++++++++++---- 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/packages/babel-plugin-transform-lwc-class/src/__tests__/wire-decorator.test.js b/packages/babel-plugin-transform-lwc-class/src/__tests__/wire-decorator.test.js index 84c9f6c0f1..bd312cccb1 100644 --- a/packages/babel-plugin-transform-lwc-class/src/__tests__/wire-decorator.test.js +++ b/packages/babel-plugin-transform-lwc-class/src/__tests__/wire-decorator.test.js @@ -12,9 +12,9 @@ describe('Wired field', () => { export default class Test {} Test.wire = { innerRecord: { - type: "record", params: { recordId: "recordId" }, - static: { fields: ["Account", 'Rate'] } + static: { fields: ["Account", 'Rate'] }, + type: "record" } }; `); @@ -31,17 +31,37 @@ describe('Wired field', () => { }, }); - test('decorator expects a string as first parameter', ` + test('decorator expects a function identifier as first parameter', ` + import { wire } from 'engine'; + import { record } from 'data-service'; + export default class Test { + @wire(record, {}) innerRecord; + } + `, ` + import { wire } from 'engine'; + import { record } from 'data-service'; + export default class Test {} + Test.wire = { + innerRecord: { + params: {}, + static: {}, + adapter: record + } + }; + `); + + test('decorator expects an imported identifier as first parameter', ` + import { wire } from 'engine'; const RECORD = "record" export default class Test { @wire(RECORD, {}) innerRecord; } `, undefined, { - message: 'test.js: @wire expects a string as first parameter.', - loc: { - line: 3, - column: 10, - }, + message: 'test.js: @wire expects a function identifier to be imported as first parameter.', + loc: { + line: 4, + column: 6, + }, }); test('decorator expects an oject as second parameter', ` @@ -69,9 +89,9 @@ describe('Wired method', () => { } Test.wire = { innerRecordMethod: { - type: "record", params: { recordId: "recordId" }, static: { fields: ["Account", 'Rate'] }, + type: "record", method: 1 } }; diff --git a/packages/babel-plugin-transform-lwc-class/src/decorators/wire.js b/packages/babel-plugin-transform-lwc-class/src/decorators/wire.js index 50c0a1a51f..23fc54bf14 100644 --- a/packages/babel-plugin-transform-lwc-class/src/decorators/wire.js +++ b/packages/babel-plugin-transform-lwc-class/src/decorators/wire.js @@ -31,10 +31,6 @@ module.exports = function wireVisitor ({ types: t }) { function buildWireConfigValue(wiredValues) { return t.objectExpression(wiredValues.map(wiredValue => { const wireConfig = [ - t.objectProperty( - t.identifier('type'), - t.stringLiteral(wiredValue.type) - ), t.objectProperty( t.identifier('params'), t.objectExpression(wiredValue.params) @@ -45,6 +41,25 @@ module.exports = function wireVisitor ({ types: t }) { ) ]; + // TODO: deprecate type (string as adapter id once consumer has migrated to use imported identifier) + if (wiredValue.type) { + wireConfig.push( + t.objectProperty( + t.identifier('type'), + t.stringLiteral(wiredValue.type) + ) + ) + } + + if (wiredValue.adapter) { + wireConfig.push( + t.objectProperty( + t.identifier('adapter'), + t.identifier(wiredValue.adapter) + ) + ) + } + if (wiredValue.isClassMethod) { wireConfig.push( t.objectProperty( @@ -72,9 +87,16 @@ module.exports = function wireVisitor ({ types: t }) { ); } - if (!id.isStringLiteral()) { + // TODO: deprecate string as adapter id once consumer has migrated to use imported identifier + if (!id.isStringLiteral() && !id.isIdentifier()) { + throw id.buildCodeFrameError( + `@wire expects a string or a function identifier as first parameter.` + ); + } + + if (id.isIdentifier() && !path.scope.getBinding(id.node.name).path.isImportSpecifier()) { throw id.buildCodeFrameError( - `@wire expects a string as first parameter.` + `@wire expects a function identifier to be imported as first parameter.` ); } @@ -89,13 +111,21 @@ module.exports = function wireVisitor ({ types: t }) { kind: 'method' }); - wiredValues.push({ + const wiredValue = { propertyName, isClassMethod, - type: id.node.value, static: getWiredStatic(config), params: getWiredParams(config), - }); + } + + // TODO: deprecate type (string as adapter id once consumer has migrated to use imported identifier) + if (id.isStringLiteral()) { + wiredValue.type = id.node.value; + } else if (id.isIdentifier()) { + wiredValue.adapter = id.node.name; + } + + wiredValues.push(wiredValue); path.remove(); } From ffe55dbdf11b52242c55ab3c5966fdbfa9c300bf Mon Sep 17 00:00:00 2001 From: Vince Chen Date: Tue, 23 Jan 2018 20:52:54 -0800 Subject: [PATCH 3/6] update wire-service getAdapter error message and tests --- packages/lwc-wire-service/src/__tests__/wire-service.test.js | 4 ++-- packages/lwc-wire-service/src/wire-service.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/lwc-wire-service/src/__tests__/wire-service.test.js b/packages/lwc-wire-service/src/__tests__/wire-service.test.js index 75a9b7f5e6..c9bf25e1c7 100644 --- a/packages/lwc-wire-service/src/__tests__/wire-service.test.js +++ b/packages/lwc-wire-service/src/__tests__/wire-service.test.js @@ -58,7 +58,7 @@ describe("wire-service.js", () => { const wireDef = { type: "unknown" }; expect(() => { target.getAdapter(wireDef, "target"); - }).toThrow(); + }).toThrowError("Unknown wire adapter id 'unknown' in target's @wire('unknown', ...)"); }); it("returns with a known adapter id", () => { const wireDef = { type: "known" }; @@ -69,7 +69,7 @@ describe("wire-service.js", () => { const wireDef = { adapter: unknownFunc }; expect(() => { target.getAdapter(wireDef, "target"); - }).toThrow(); + }).toThrowError("Unknown wire adapter id 'unknownFunc' in target's @wire(unknownFunc, ...)"); }); it("returns with a known function identifier adapter id", () => { const wireDef = { adapter: knownFunc }; diff --git a/packages/lwc-wire-service/src/wire-service.js b/packages/lwc-wire-service/src/wire-service.js index 63007d8bfe..3141415aba 100644 --- a/packages/lwc-wire-service/src/wire-service.js +++ b/packages/lwc-wire-service/src/wire-service.js @@ -55,12 +55,12 @@ export function getAdapter(wireDef, wireTarget) { if (wireDef.type) { adapter = ADAPTERS.get(wireDef.type); if (!adapter) { - throw new Error(`Unknown wire adapter id '${wireDef.type}' in ${wireTarget}'s @wire('${wireDef.type}, ...)`); + throw new Error(`Unknown wire adapter id '${wireDef.type}' in ${wireTarget}'s @wire('${wireDef.type}', ...)`); } } else if (wireDef.adapter) { adapter = ADAPTERS.get(wireDef.adapter.name); if (!adapter) { - throw new Error(`Unknown wire adapter id '${wireDef.adapter.name}' in ${wireTarget}'s @wire('${wireDef.adapter.name}, ...)`); + throw new Error(`Unknown wire adapter id '${wireDef.adapter.name}' in ${wireTarget}'s @wire(${wireDef.adapter.name}, ...)`); } } From efebc1beeeb1fb9fdf59f8fb41a5cad1899304dc Mon Sep 17 00:00:00 2001 From: Vince Chen Date: Thu, 25 Jan 2018 09:01:54 -0800 Subject: [PATCH 4/6] update wired integration tests to use function identifier --- .../test-wired-method-suite/wired-method/wired-method.js | 4 ++-- .../wired/test-wired-prop-suite/wired-prop/wired-prop.js | 4 ++-- packages/lwc-integration/src/shared/templates.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/lwc-integration/src/components/wired/test-wired-method-suite/wired-method/wired-method.js b/packages/lwc-integration/src/components/wired/test-wired-method-suite/wired-method/wired-method.js index 3fbe7420c2..0dd882743e 100644 --- a/packages/lwc-integration/src/components/wired/test-wired-method-suite/wired-method/wired-method.js +++ b/packages/lwc-integration/src/components/wired/test-wired-method-suite/wired-method/wired-method.js @@ -1,10 +1,10 @@ import { Element } from 'engine'; - +import { serviceTodo } from 'todo'; export default class WiredMethod extends Element { @api todoId; @track state = { error: undefined, todo: undefined }; - @wire('todo', { id: '$todoId' }) + @wire(serviceTodo, { id: '$todoId' }) function(error, data) { this.state = { error: error, todo: data }; } diff --git a/packages/lwc-integration/src/components/wired/test-wired-prop-suite/wired-prop/wired-prop.js b/packages/lwc-integration/src/components/wired/test-wired-prop-suite/wired-prop/wired-prop.js index af1f79199e..300de1919b 100644 --- a/packages/lwc-integration/src/components/wired/test-wired-prop-suite/wired-prop/wired-prop.js +++ b/packages/lwc-integration/src/components/wired/test-wired-prop-suite/wired-prop/wired-prop.js @@ -1,9 +1,9 @@ import { Element } from 'engine'; - +import { serviceTodo } from 'todo'; export default class WiredProp extends Element { @api todoId; - @wire('todo', { id: '$todoId' }) + @wire(serviceTodo, { id: '$todoId' }) todo; get error() { diff --git a/packages/lwc-integration/src/shared/templates.js b/packages/lwc-integration/src/shared/templates.js index 537666357a..b2dddfc027 100644 --- a/packages/lwc-integration/src/shared/templates.js +++ b/packages/lwc-integration/src/shared/templates.js @@ -90,7 +90,7 @@ exports.todoApp = function (cmpName) { registerWireService(register, () => { return { - 'todo': serviceTodo + serviceTodo }; }); From daa9873783949871f86afb73de4a4b7e57367b8c Mon Sep 17 00:00:00 2001 From: Vince Chen Date: Thu, 25 Jan 2018 10:01:55 -0800 Subject: [PATCH 5/6] update wired test template --- packages/lwc-integration/scripts/build.js | 7 +- .../lwc-integration/src/shared/templates.js | 77 +---------------- packages/lwc-integration/src/shared/todo.js | 85 +++++++++++++++++++ 3 files changed, 92 insertions(+), 77 deletions(-) create mode 100644 packages/lwc-integration/src/shared/todo.js diff --git a/packages/lwc-integration/scripts/build.js b/packages/lwc-integration/scripts/build.js index 21473edb49..0921f8507d 100644 --- a/packages/lwc-integration/scripts/build.js +++ b/packages/lwc-integration/scripts/build.js @@ -82,12 +82,13 @@ const globalModules = { 'babel/helpers/createClass' : 'createClass', 'babel/helpers/defineProperty': 'defineProperty', 'babel/helpers/objectDestructuringEmpty': 'objectDestructuringEmpty', - 'wire-service': 'WireService' + 'wire-service': 'WireService', + 'todo': 'Todo' }; const baseInputConfig = { external: function (id) { - if (id.includes('babel/helpers') || id.includes('engine') || id.includes('wire-service')) { + if (id.includes('babel/helpers') || id.includes('engine') || id.includes('wire-service') || id.includes('todo')) { return true; } }, @@ -116,6 +117,7 @@ const baseOutputConfig = { const engineModeFile = path.join(require.resolve(`lwc-engine/dist/umd/${isCompat ? 'es5': 'es2017'}/engine.js`)); const compatPath = path.join(require.resolve('proxy-compat-build/dist/umd/compat_downgrade.js')); const wireServicePath = path.join(require.resolve(`lwc-wire-service/dist/umd/${isCompat ? 'es5': 'es2017'}/wire-service.js`)); +const todoPath = path.join(require.resolve('../src/shared/todo.js')); if (!fs.existsSync(engineModeFile)) { throw new Error("Compat version of engine not generated in expected location: " + engineModeFile @@ -126,6 +128,7 @@ if (!fs.existsSync(engineModeFile)) { fs.copySync(compatPath, path.join(testSharedOutput, 'compat.js')); fs.copySync(engineModeFile, path.join(testSharedOutput,'engine.js')); fs.copySync(wireServicePath, path.join(testSharedOutput, 'wire-service.js')); +fs.copySync(todoPath, path.join(testSharedOutput, 'todo.js')); // -- Build component tests -----------------------------------------------------= diff --git a/packages/lwc-integration/src/shared/templates.js b/packages/lwc-integration/src/shared/templates.js index b2dddfc027..4db99fb58c 100644 --- a/packages/lwc-integration/src/shared/templates.js +++ b/packages/lwc-integration/src/shared/templates.js @@ -9,85 +9,11 @@ exports.app = function (cmpName) { exports.todoApp = function (cmpName) { return ` + import { serviceTodo } from 'todo'; import registerWireService from 'wire-service'; import { createElement, register } from 'engine'; import Cmp from '${cmpName}'; - function getSubject(initialValue, initialError) { - let observer; - - function next(value) { - observer.next(value); - } - - function error(err) { - observer.error(err); - } - - function complete() { - observer.complete(); - } - - const observable = { - subscribe: (obs) => { - observer = obs; - if (initialValue) { - next(initialValue); - } - if (initialError) { - error(initialError); - } - return { - unsubscribe: () => { } - }; - } - }; - - return { - next, - error, - complete, - observable - }; - } - - function generateTodo(id, completed) { - return { - id, - title: 'task ' + id, - completed - }; - } - - const TODO = [ - generateTodo(0, true), - generateTodo(1, false), - // intentionally skip 2 - generateTodo(3, true), - generateTodo(4, true), - // intentionally skip 5 - generateTodo(6, false), - generateTodo(7, false) - ].reduce((acc, value) => { - acc[value.id] = value; - return acc; - }, {}); - - - function serviceTodo(config) { - if (!('id' in config)) { - return undefined; - } - - const todo = TODO[config.id]; - if (!todo) { - const subject = getSubject(undefined, { message: 'Todo not found' }); - return subject.observable; - } - - return getSubject(todo).observable; - } - registerWireService(register, () => { return { serviceTodo @@ -132,6 +58,7 @@ exports.wireServiceHtml = function (cmpName, isCompat) { ${isCompat ? COMPAT : ''} + diff --git a/packages/lwc-integration/src/shared/todo.js b/packages/lwc-integration/src/shared/todo.js new file mode 100644 index 0000000000..ec2a094dde --- /dev/null +++ b/packages/lwc-integration/src/shared/todo.js @@ -0,0 +1,85 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.Todo = {}))); +}(this, (function (exports) { + 'use strict'; + +function getSubject(initialValue, initialError) { + let observer; + + function next(value) { + observer.next(value); + } + + function error(err) { + observer.error(err); + } + + function complete() { + observer.complete(); + } + + const observable = { + subscribe: (obs) => { + observer = obs; + if (initialValue) { + next(initialValue); + } + if (initialError) { + error(initialError); + } + return { + unsubscribe: () => { } + }; + } + }; + + return { + next, + error, + complete, + observable + }; +} + +function generateTodo(id, completed) { + return { + id, + title: 'task ' + id, + completed + }; +} + +const TODO = [ + generateTodo(0, true), + generateTodo(1, false), + // intentionally skip 2 + generateTodo(3, true), + generateTodo(4, true), + // intentionally skip 5 + generateTodo(6, false), + generateTodo(7, false) +].reduce((acc, value) => { + acc[value.id] = value; + return acc; +}, {}); + + +function serviceTodo(config) { + if (!('id' in config)) { + return undefined; + } + + const todo = TODO[config.id]; + if (!todo) { + const subject = getSubject(undefined, { message: 'Todo not found' }); + return subject.observable; + } + + return getSubject(todo).observable; +} + + exports.serviceTodo = serviceTodo; + Object.defineProperty(exports, '__esModule', { value: true }); +}))); \ No newline at end of file From b46ef012cc421913cf5060a664ec334e9c270f97 Mon Sep 17 00:00:00 2001 From: Vince Chen Date: Thu, 25 Jan 2018 11:33:42 -0800 Subject: [PATCH 6/6] make todo module compat with ie11 --- packages/lwc-integration/src/shared/todo.js | 28 ++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/lwc-integration/src/shared/todo.js b/packages/lwc-integration/src/shared/todo.js index ec2a094dde..4f2fbbba78 100644 --- a/packages/lwc-integration/src/shared/todo.js +++ b/packages/lwc-integration/src/shared/todo.js @@ -6,7 +6,7 @@ 'use strict'; function getSubject(initialValue, initialError) { - let observer; + var observer; function next(value) { observer.next(value); @@ -20,8 +20,8 @@ function getSubject(initialValue, initialError) { observer.complete(); } - const observable = { - subscribe: (obs) => { + var observable = { + subscribe: function(obs) { observer = obs; if (initialValue) { next(initialValue); @@ -30,28 +30,28 @@ function getSubject(initialValue, initialError) { error(initialError); } return { - unsubscribe: () => { } + unsubscribe: function() {} }; } }; return { - next, - error, - complete, - observable + next: next, + error: error, + complete: complete, + observable: observable }; } function generateTodo(id, completed) { return { - id, + id: id, title: 'task ' + id, - completed + completed: completed }; } -const TODO = [ +var TODO = [ generateTodo(0, true), generateTodo(1, false), // intentionally skip 2 @@ -60,7 +60,7 @@ const TODO = [ // intentionally skip 5 generateTodo(6, false), generateTodo(7, false) -].reduce((acc, value) => { +].reduce(function(acc, value) { acc[value.id] = value; return acc; }, {}); @@ -71,9 +71,9 @@ function serviceTodo(config) { return undefined; } - const todo = TODO[config.id]; + var todo = TODO[config.id]; if (!todo) { - const subject = getSubject(undefined, { message: 'Todo not found' }); + var subject = getSubject(undefined, { message: 'Todo not found' }); return subject.observable; }