Optional migrations & basic w-bind migration (#1192)

* Setup optional migration support

* Add migration call for w-bind

* Add tests for optional migrations

* Add assertions based on migration prompts

* Skip migration tests on node 6

* Fix typo
This commit is contained in:
Dylan Piercey 2018-12-14 09:50:05 -08:00 committed by GitHub
parent c6c9122ca8
commit 21ce06f8ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 597 additions and 126 deletions

8
.gitignore vendored
View File

@ -1,9 +1,9 @@
/work
/build
/.idea/
/npm-debug.log
/node_modules
/*.sublime-workspace
.idea/
npm-debug.log
node_modules
*.sublime-workspace
*.orig
.DS_Store
.vscode

302
package-lock.json generated
View File

@ -13,6 +13,66 @@
"@babel/highlight": "^7.0.0"
}
},
"@babel/core": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.2.0.tgz",
"integrity": "sha512-7pvAdC4B+iKjFFp9Ztj0QgBndJ++qaMeonT185wAqUnhipw8idm9Rv1UMyBuKtYjfl6ORNkgEgcsYLfHX/GpLw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/generator": "^7.2.0",
"@babel/helpers": "^7.2.0",
"@babel/parser": "^7.2.0",
"@babel/template": "^7.1.2",
"@babel/traverse": "^7.1.6",
"@babel/types": "^7.2.0",
"convert-source-map": "^1.1.0",
"debug": "^4.1.0",
"json5": "^2.1.0",
"lodash": "^4.17.10",
"resolve": "^1.3.2",
"semver": "^5.4.1",
"source-map": "^0.5.0"
},
"dependencies": {
"debug": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
"integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"json5": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz",
"integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
}
}
},
"@babel/generator": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.0.tgz",
@ -69,6 +129,17 @@
"@babel/types": "^7.0.0"
}
},
"@babel/helpers": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.2.0.tgz",
"integrity": "sha512-Fr07N+ea0dMcMN8nFpuK6dUIT7/ivt9yKQdEEnjVS83tG2pHwPi03gYmk/tyuwONnZ+sY+GFFPlWGgCtW1hF9A==",
"dev": true,
"requires": {
"@babel/template": "^7.1.2",
"@babel/traverse": "^7.1.5",
"@babel/types": "^7.2.0"
}
},
"@babel/highlight": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
@ -123,6 +194,23 @@
"integrity": "sha512-M74+GvK4hn1eejD9lZ7967qAwvqTZayQa3g10ag4s9uewgR7TKjeaT0YMyoq+gVfKYABiWZ4MQD701/t5e1Jhg==",
"dev": true
},
"@babel/runtime": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz",
"integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.12.0"
},
"dependencies": {
"regenerator-runtime": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==",
"dev": true
}
}
},
"@babel/template": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz",
@ -193,6 +281,78 @@
}
}
},
"@marko/migrate": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@marko/migrate/-/migrate-1.2.0.tgz",
"integrity": "sha512-UbzOgxhJJBhfW2Y5xozJgqWBsZUqcunQFq689DZlMerlKxpTNhsQprQghPS4YWTay4BLRhGEgbDHczhwxt5vag==",
"dev": true,
"requires": {
"@babel/runtime": "^7.2.0",
"@marko/migrate-v3-widget": "^1.0.1",
"@marko/prettyprint": "^1.2.0",
"argly": "^1.2.0",
"enquirer": "^2.1.1",
"glob": "^7.1.3",
"lasso-package-root": "^1.0.1",
"mz": "^2.7.0",
"resolve-from": "^4.0.0"
},
"dependencies": {
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
}
}
},
"@marko/migrate-v3-widget": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@marko/migrate-v3-widget/-/migrate-v3-widget-1.0.1.tgz",
"integrity": "sha512-luWXWYUPlStF0OFeB8NbBm5w59khC2dLgRcoXlj1N6QejZ1+ZVd2xlrkTj88XJavS+cZxWtObHvG77xRePp9aQ==",
"dev": true,
"requires": {
"@babel/core": "^7.1.5",
"@babel/traverse": "^7.1.6",
"mz": "^2.7.0",
"recast": "^0.16.1",
"tslib": "^1.9.3"
}
},
"@marko/prettyprint": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@marko/prettyprint/-/prettyprint-1.2.0.tgz",
"integrity": "sha512-YXm5nW0koZ36cXkmlJyG9WMfMK0NkjzE3H6Svz/01vCwMMtuj2zDcYvJm3V1ql0bW0Nwc960zOB5raN3kKVrBQ==",
"dev": true,
"requires": {
"argly": "^1.2.0",
"editorconfig": "^0.15.2",
"lasso-package-root": "^1.0.1",
"marko": "^4.14.3",
"minimatch": "^3.0.4",
"prettier": "^1.15.3",
"redent": "^2.0.0",
"resolve-from": "^4.0.0"
},
"dependencies": {
"redent": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz",
"integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=",
"dev": true,
"requires": {
"indent-string": "^3.0.0",
"strip-indent": "^2.0.0"
}
},
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
}
}
},
"@samverschueren/stream-to-observable": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz",
@ -202,6 +362,18 @@
"any-observable": "^0.3.0"
}
},
"@types/node": {
"version": "10.12.15",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.15.tgz",
"integrity": "sha512-9kROxduaN98QghwwHmxXO2Xz3MaWf+I1sLVAA6KJDF5xix+IyXVhds0MAfdNwtcpSrzhaTsNB0/jnL86fgUhqA==",
"dev": true
},
"@types/semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==",
"dev": true
},
"abab": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz",
@ -283,6 +455,12 @@
"integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
"dev": true
},
"ansi-colors": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
"integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
"dev": true
},
"ansi-escapes": {
"version": "3.1.0",
"resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
@ -307,6 +485,12 @@
"integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==",
"dev": true
},
"any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=",
"dev": true
},
"anymatch": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
@ -436,6 +620,12 @@
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
"dev": true
},
"ast-types": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.6.tgz",
"integrity": "sha512-nHiuV14upVGl7MWwFUYbzJ6YlfwWS084CU9EA8HajfYQjMSli5TQi3UTRygGF58LFWVkXxS1rbgRhROEqlQkXg==",
"dev": true
},
"async-each": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
@ -1610,6 +1800,20 @@
"safer-buffer": "^2.1.0"
}
},
"editorconfig": {
"version": "0.15.2",
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.2.tgz",
"integrity": "sha512-GWjSI19PVJAM9IZRGOS+YKI8LN+/sjkSjNyvxL5ucqP9/IqtYNXBaQ/6c/hkPNYQHyOHra2KoXZI/JVpuqwmcQ==",
"dev": true,
"requires": {
"@types/node": "^10.11.7",
"@types/semver": "^5.5.0",
"commander": "^2.19.0",
"lru-cache": "^4.1.3",
"semver": "^5.6.0",
"sigmund": "^1.0.1"
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -1628,6 +1832,15 @@
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"dev": true
},
"enquirer": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.1.1.tgz",
"integrity": "sha512-Ky4Uo7q/dY9jSJIDkPf+y5nM+9F4ESINZaWJSSJbJqOVY15kVXIa/goQR6XmHNcS/MQ38wZT/N2+1pBwQeC2wQ==",
"dev": true,
"requires": {
"ansi-colors": "^3.2.1"
}
},
"entities": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
@ -4147,6 +4360,45 @@
"object-visit": "^1.0.0"
}
},
"marko": {
"version": "4.14.4",
"resolved": "https://registry.npmjs.org/marko/-/marko-4.14.4.tgz",
"integrity": "sha512-GyIDRYWe9cPolvqEFSaGRmSGSzdcvL4L/s64AdBd1YMbTdhAT2vQzqkl23LpiFzc5r2FBB+kgvaK8carqlwTPw==",
"dev": true,
"requires": {
"app-module-path": "^2.2.0",
"argly": "^1.0.0",
"browser-refresh-client": "^1.0.0",
"char-props": "~0.1.5",
"complain": "^1.3.0",
"deresolve": "^1.1.2",
"escodegen": "^1.8.1",
"esprima": "^4.0.0",
"estraverse": "^4.2.0",
"events": "^1.0.2",
"events-light": "^1.0.0",
"he": "^1.1.0",
"htmljs-parser": "^2.5.0",
"lasso-caching-fs": "^1.0.1",
"lasso-modules-client": "^2.0.4",
"lasso-package-root": "^1.0.1",
"listener-tracker": "^2.0.0",
"minimatch": "^3.0.2",
"object-assign": "^4.1.0",
"property-handlers": "^1.0.0",
"raptor-json": "^1.0.1",
"raptor-polyfill": "^1.0.0",
"raptor-promises": "^1.0.1",
"raptor-regexp": "^1.0.0",
"raptor-util": "^3.2.0",
"resolve-from": "^2.0.0",
"shorthash": "0.0.2",
"simple-sha1": "^2.1.0",
"strip-json-comments": "^2.0.1",
"try-require": "^1.2.1",
"warp10": "^1.0.0"
}
},
"marko-widgets": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/marko-widgets/-/marko-widgets-7.0.1.tgz",
@ -4631,6 +4883,17 @@
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
"dev": true
},
"mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dev": true,
"requires": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
"thenify-all": "^1.0.0"
}
},
"nan": {
"version": "2.11.1",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
@ -6544,6 +6807,18 @@
"readable-stream": "^2.0.2"
}
},
"recast": {
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/recast/-/recast-0.16.1.tgz",
"integrity": "sha512-ZUQm94F3AHozRaTo4Vz6yIgkSEZIL7p+BsWeGZ23rx+ZVRoqX+bvBA8br0xmCOU0DSR4qYGtV7Y5HxTsC4V78A==",
"dev": true,
"requires": {
"ast-types": "0.11.6",
"esprima": "~4.0.0",
"private": "~0.1.5",
"source-map": "~0.6.1"
}
},
"rechoir": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
@ -6943,6 +7218,12 @@
"resolved": "https://registry.npmjs.org/shorthash/-/shorthash-0.0.2.tgz",
"integrity": "sha1-WbJo7sveWQOLMNogK8+93rLEpOs="
},
"sigmund": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
"dev": true
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
@ -7089,8 +7370,7 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"optional": true
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-resolve": {
"version": "0.5.2",
@ -7445,6 +7725,24 @@
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true
},
"thenify": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
"integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=",
"dev": true,
"requires": {
"any-promise": "^1.0.0"
}
},
"thenify-all": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
"dev": true,
"requires": {
"thenify": ">= 3.1.0 < 4"
}
},
"through": {
"version": "2.3.8",
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",

View File

@ -61,6 +61,7 @@
"warp10": "^1.0.0"
},
"devDependencies": {
"@marko/migrate": "^1.2.0",
"babel-cli": "^6.24.1",
"babel-core": "^6.24.1",
"babel-plugin-minprops": "^2.0.1",

View File

@ -952,6 +952,10 @@ class CompileContext extends EventEmitter {
isServerTarget() {
return this.target === "server";
}
addMigration() {
// Actual functionality implemented by marko migrate cli command.
}
}
CompileContext.prototype.util = {

View File

@ -220,6 +220,10 @@ function parse(templateSrc, filename, options) {
filename,
Builder.DEFAULT_BUILDER
);
if (options.onContext) {
options.onContext(context);
}
var parsed = defaultParser.parse(templateSrc, context, options);
if (context.hasErrors()) {

View File

@ -11,8 +11,11 @@ module.exports = function handleComponentBind(options) {
context.firstBind = true;
let isLegacyComponent = (this.isLegacyComponent =
options.isLegacyComponent === true);
if (options.isLegacyComponent) {
context.setMeta("legacy", true);
}
let isLegacyComponent = this.isLegacyComponent;
let componentModule = options.componentModule;
let rendererModule = options.rendererModule;
let componentProps = options.componentProps || {};

View File

@ -1,32 +1,4 @@
"use strict";
const resolveFrom = require("resolve-from");
function legacyGetDefaultComponentModule(dirname) {
var filename;
var legacy = true;
if ((filename = resolveFrom(dirname, "./widget"))) {
return {
filename,
requirePath: "./widget",
legacy
};
} else if ((filename = resolveFrom(dirname, "./component"))) {
return {
filename,
requirePath: "./component",
legacy
};
} else if ((filename = resolveFrom(dirname, "./"))) {
return {
filename,
requirePath: "./",
legacy
};
} else {
return null;
}
}
function checkIsInnerBind(el) {
var curNode = el;
@ -45,107 +17,58 @@ module.exports = function handleLegacyBind() {
let context = this.context;
let builder = this.builder;
let componentModule;
let rendererModule;
let componentModule = context.legacyComponentModule;
let rendererModule = context.legacyRendererModule;
if (el.hasAttribute("w-bind")) {
let bindAttr = el.getAttribute("w-bind");
let bindAttr = el.getAttribute("w-bind");
el.data.hasBoundComponent = true;
// Remove the w-bind attribute since we don't want it showing up in the output DOM
el.removeAttribute("w-bind");
// Set key value so we can get the root using this.el
el.setAttributeValue("key", builder.literal("_wbind"));
// Read the value for the w-bind attribute. This will be an AST node for the parsed JavaScript
let bindAttrValue = bindAttr.value;
const hasWidgetTypes = context.isFlagSet("hasWidgetTypes");
if (hasWidgetTypes) {
context.deprecate(
"Legacy components using w-bind and defineRenderer/defineComponent or defineComponent are deprecated. See: https://github.com/marko-js/marko/issues/421"
"The <widget-types> tag is deprecated. Please remove it. See: https://github.com/marko-js/marko/issues/514"
);
this.isLegacyComponent = true;
context.setMeta("legacy", true);
}
// Remove the w-bind attribute since we don't want it showing up in the output DOM
el.removeAttribute("w-bind");
// Set key value so we can get the root using this.el
el.setAttributeValue("key", builder.literal("_wbind"));
// Read the value for the w-bind attribute. This will be an AST node for the parsed JavaScript
let bindAttrValue = bindAttr.value;
const hasWidgetTypes = context.isFlagSet("hasWidgetTypes");
if (hasWidgetTypes) {
context.deprecate(
"The <widget-types> tag is deprecated. Please remove it. See: https://github.com/marko-js/marko/issues/514"
if (bindAttrValue != null && !bindAttr.isLiteralValue()) {
// This is a dynamic expression. The <widget-types> should have been found.
if (!hasWidgetTypes) {
this.addError(
'The <widget-types> tag must be used to declare components when the value of the "w-bind" attribute is a dynamic expression.'
);
return;
}
if (bindAttrValue == null) {
componentModule = legacyGetDefaultComponentModule(this.dirname);
if (!componentModule) {
this.addError(
'No corresponding JavaScript module found in the same directory (either "component.js" or "index.js").'
);
return;
}
if (componentModule.requirePath === "./") {
rendererModule = componentModule;
}
} else if (bindAttr.isLiteralValue()) {
if (typeof bindAttr.literalValue !== "string") {
this.addError(
'The value for the "w-bind" attribute should be a string. Actual: ' +
componentModule
);
return;
}
let requirePath = bindAttr.literalValue;
let filename = resolveFrom(this.dirname, requirePath);
if (!filename) {
this.addError(
"Target file not found: " +
requirePath +
" (from: " +
this.dirname +
")"
);
return;
}
componentModule = {
legacy: true,
filename,
requirePath
};
} else {
// This is a dynamic expression. The <widget-types> should have been found.
if (!hasWidgetTypes) {
this.addError(
'The <widget-types> tag must be used to declare components when the value of the "w-bind" attribute is a dynamic expression.'
);
return;
}
el.insertSiblingBefore(
builder.functionCall(
el.insertSiblingBefore(
builder.functionCall(
builder.memberExpression(
builder.identifier("__component"),
builder.identifier("t")
),
[
builder.memberExpression(
builder.identifier("__component"),
builder.identifier("t")
),
[
builder.memberExpression(
builder.identifier("marko_componentTypes"),
bindAttrValue,
true /* computed */
)
]
)
);
}
} else {
return;
builder.identifier("marko_componentTypes"),
bindAttrValue,
true /* computed */
)
]
)
);
}
let isLegacyInnerBind = checkIsInnerBind(el.parentNode);
el.data.hasBoundComponent = true;
// A component is bound to the el...
if (el.hasAttribute("w-config")) {

View File

@ -139,6 +139,10 @@ class TransformHelper {
return this.context.data[MARKO_WIDGETS_VAR_KEY];
}
get isLegacyComponent() {
return this.context.meta.legacy;
}
buildComponentElIdFunctionCall(id) {
var builder = this.builder;

View File

@ -8,7 +8,7 @@ module.exports = function codeGenerator(el, codegen) {
var context = codegen.context;
var transformHelper = getTransformHelper(el, context);
transformHelper.isLegacyComponent = true;
context.setMeta("legacy", true);
var builder = codegen.builder;

View File

@ -0,0 +1,118 @@
const resolveFrom = require("resolve-from");
module.exports = function migrate(el, context) {
const attr = el.getAttribute("w-bind");
if (!attr) {
return;
}
const value = attr.value;
const hasWidgetTypes = context.isFlagSet("hasWidgetTypes");
let componentModule;
let rendererModule;
context.setMeta("legacy", true);
context.deprecate(
"Legacy components using w-bind and defineRenderer/defineComponent or defineComponent are deprecated. See: https://github.com/marko-js/marko/issues/421"
);
if (value == null) {
componentModule = getDefaultWidgetFile(context.dirname);
if (!componentModule) {
context.addError(
'No corresponding JavaScript module found in the same directory (either "widget.js", "component.js" or "index.js").'
);
return;
}
if (componentModule.requirePath === "./") {
rendererModule = componentModule;
}
} else if (attr.isLiteralValue()) {
const literalValue = attr.literalValue;
if (typeof literalValue !== "string") {
context.addError(
'The value for the "w-bind" attribute should be a string. Actual: ' +
literalValue
);
return;
}
const filename = resolveFrom(context.dirname, literalValue);
if (!filename) {
this.addError(
"Target file not found: " +
literalValue +
" (from: " +
context.dirname +
")"
);
return;
}
componentModule = {
legacy: true,
filename,
requirePath: literalValue
};
}
context.legacyComponentModule = componentModule;
context.legacyRendererModule = rendererModule;
context.addMigration({
description: "Migrate legacy widget with w-bind",
apply(helpers) {
if (hasWidgetTypes) {
return;
}
return helpers
.prompt({
type: "confirm",
message:
"A widget file was discovered, would you like to migrate that as well?\n" +
"Note: widget migrations are not 100% safe and should be tested after migration.",
initial: true
})
.then(shouldMigrate => {
if (shouldMigrate) {
el.removeAttribute("w-bind");
return helpers.run("componentFile", componentModule);
}
});
}
});
};
function getDefaultWidgetFile(dirname) {
var filename;
var legacy = true;
if ((filename = resolveFrom(dirname, "./widget"))) {
return {
filename,
requirePath: "./widget",
legacy
};
} else if ((filename = resolveFrom(dirname, "./component"))) {
return {
filename,
requirePath: "./component",
legacy
};
} else if ((filename = resolveFrom(dirname, "./"))) {
return {
filename,
requirePath: "./",
legacy
};
} else {
return null;
}
}

View File

@ -25,7 +25,8 @@ Module._resolveFilename = function(request, parent, isMain) {
request === "marko/components" ||
request === "marko/jquery" ||
request === "marko/legacy-components" ||
request === "marko/ready"
request === "marko/ready" ||
request === "marko/env"
) {
request = nodePath.join(
rootDir,

View File

@ -0,0 +1,3 @@
module.exports = require("marko-widgets").defineComponent({
template: require("./template")
});

View File

@ -0,0 +1,7 @@
module.exports = [
{
question:
"A widget file was discovered, would you like to migrate that as well?\nNote: widget migrations are not 100% safe and should be tested after migration.",
answer: false
}
];

View File

@ -0,0 +1,3 @@
<!-- test/migrate/fixtures/w-bind-prompt-n/template.marko -->
<div w-bind/>

View File

@ -0,0 +1 @@
<div w-bind/>

View File

@ -0,0 +1,3 @@
module.exports = require("marko-widgets").defineComponent({
template: require("./template")
});

View File

@ -0,0 +1,7 @@
module.exports = [
{
question:
"A widget file was discovered, would you like to migrate that as well?\nNote: widget migrations are not 100% safe and should be tested after migration.",
answer: true
}
];

View File

@ -0,0 +1,3 @@
// test/migrate/fixtures/w-bind-prompt-y/index.js
module.exports = {};

View File

@ -0,0 +1,3 @@
<!-- test/migrate/fixtures/w-bind-prompt-y/template.marko -->
<div/>

View File

@ -0,0 +1 @@
<div w-bind/>

View File

@ -0,0 +1,79 @@
"use strict";
if (!/^v6\..*/.test(process.version)) {
require("../__util__/test-init");
var path = require("path");
var chai = require("chai");
chai.config.includeStack = true;
var cp = require("child_process");
var autotest = require("../autotest");
var migrate = require("@marko/migrate").default;
var CWD = process.cwd();
cp.execSync("npm i --no-package-lock", { cwd: __dirname });
autotest("fixtures", fixture => {
let test = fixture.test;
let resolve = fixture.resolve;
let snapshot = fixture.snapshot;
let expectedPrompts = [];
try {
expectedPrompts = require(resolve("./prompts.js")).slice();
} catch (_) {
// Ignore
}
test(() => {
return migrate({
dir: __dirname,
files: [resolve("template.marko")],
prompt(opts) {
if (!expectedPrompts.length) {
throw new Error(
"Prompt occurred during migration test but no answer provided."
);
}
const currentPrompt = expectedPrompts.shift();
chai.expect(opts.message).to.eql(currentPrompt.question);
return currentPrompt.answer;
}
}).then(outputs => {
const byExtension = Object.entries(outputs)
.sort(([a], [b]) => a.localeCompare(b))
.reduce((r, [file, source]) => {
const ext = path.extname(file);
const parts = (r[ext] = r[ext] || []);
parts.push(
`${toComment(
ext,
path.relative(CWD, file)
)}\n\n${source}`
);
return r;
}, {});
Object.entries(byExtension).forEach(([ext, files]) => {
snapshot(files.join("\n\n"), {
ext,
name: "snapshot"
});
});
});
});
});
}
function toComment(ext, str) {
switch (ext) {
case ".marko":
return `<!-- ${str} -->`;
case ".js":
case ".json":
return `// ${str}`;
default:
return `# ${str}`;
}
}

View File

@ -0,0 +1,5 @@
{
"dependencies": {
"marko": "file:../../"
}
}