diff --git a/.eslintrc.js b/.eslintrc.js index b584e0b742f..0c8e2e4f974 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,31 +1,29 @@ module.exports = { - "root": true, - "plugins": ["node"], - "extends": ["eslint:recommended", "plugin:node/recommended"], - "env": { - "node": true, - "es6": true, - "jest": true + extends: ["eslint:recommended"], + env: { + node: true, + es6: true, + jest: true }, - "parserOptions": { "ecmaVersion": 2017, "sourceType": "module"}, - "rules": { + parserOptions: { ecmaVersion: 2017, sourceType: "module" }, + rules: { "no-useless-escape": "off", "quote-props": ["error", "as-needed"], "no-dupe-keys": "error", - "quotes": ["error", "double"], + quotes: ["error", "double"], "no-undef": "error", "no-extra-semi": "error", - "semi": "error", + semi: "error", "no-template-curly-in-string": "error", "no-caller": "error", - "yoda": "error", - "eqeqeq": "error", + yoda: "error", + eqeqeq: "error", "global-require": "off", "brace-style": "error", "key-spacing": "error", "space-in-parens": ["error", "never"], "space-infix-ops": "error", - "indent": ["error", "tab", { "SwitchCase": 1 }], + indent: ["error", "tab", { SwitchCase: 1 }], "no-extra-bind": "warn", "no-empty": "off", "no-multiple-empty-lines": "error", @@ -33,78 +31,31 @@ module.exports = { "no-process-exit": "off", "no-trailing-spaces": "error", "no-use-before-define": "off", - "no-unused-vars": ["error", { "args": "none" }], + "no-unused-vars": ["error", { args: "none" }], "no-unsafe-negation": "error", "no-loop-func": "warn", "space-before-function-paren": ["error", "never"], "space-before-blocks": "error", "object-curly-spacing": ["error", "always"], - "object-curly-newline": ["error", { "consistent": true }], - "keyword-spacing": ["error", { - "after": true, - "overrides": { - "const": { "after": true }, - "try": { "after": true }, - "throw": { "after": true }, - "case": { "after": true }, - "return": { "after": true }, - "finally": { "after": true }, - "do": { "after": true } - } - }], - "no-console": "off", - "valid-jsdoc": "error", - "node/no-unsupported-features": ["error", { "version": 6 }], - "node/no-deprecated-api": "error", - "node/no-missing-import": "error", - "node/no-missing-require": [ - "error", - { - "resolvePaths": ["./packages"], - "allowModules": [ - "webpack", - "@webpack-cli/generators", - "@webpack-cli/init", - "@webpack-cli/migrate", - "@webpack-cli/utils", - "@webpack-cli/generate-loader", - "@webpack-cli/generate-plugin", - "@webpack-cli/webpack-scaffold" - ] - } - ], - "node/no-unpublished-bin": "error", - "node/no-unpublished-require": [ + "object-curly-newline": ["error", { consistent: true }], + "keyword-spacing": [ "error", { - "allowModules": [ - "webpack", - "webpack-dev-server", - "@webpack-cli/generators", - "@webpack-cli/init", - "@webpack-cli/migrate", - "@webpack-cli/utils", - "@webpack-cli/generate-loader", - "@webpack-cli/generate-plugin", - "@webpack-cli/webpack-scaffold" - ] - } - ], - "node/no-extraneous-require": [ - "error", - { - "allowModules": [ - "@webpack-cli/migrate", - "@webpack-cli/generators", - "@webpack-cli/utils", - "@webpack-cli/generate-loader", - "@webpack-cli/generate-plugin", - "@webpack-cli/webpack-scaffold" - ] + after: true, + overrides: { + const: { after: true }, + try: { after: true }, + throw: { after: true }, + case: { after: true }, + return: { after: true }, + finally: { after: true }, + do: { after: true } + } } ], + "no-console": "off", + "valid-jsdoc": "error", "eol-last": ["error", "always"], - "newline-per-chained-call": "off", - "node/process-exit-as-throw": "error" + "newline-per-chained-call": "off" } }; diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8f937259c0c..84c96d30ae1 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -111,7 +111,7 @@ In case you are suggesting a new feature, we will match your idea with our curre * You can run the linters: ```bash - npm run lint && npm run tslint + npm run lint ``` ### Using yarn @@ -150,7 +150,7 @@ In case you are suggesting a new feature, we will match your idea with our curre * You can run the linters: ```bash - yarn lint && yarn tslint + yarn lint ``` ## Editor Config diff --git a/.gitignore b/.gitignore index ef696edb785..6fd2e644f89 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ yarn.lock # source maps of docs docs/**/*.map +junit.xml diff --git a/bin/.eslintrc.js b/bin/.eslintrc.js new file mode 100644 index 00000000000..b32868dab61 --- /dev/null +++ b/bin/.eslintrc.js @@ -0,0 +1,63 @@ +module.exports = { + root: true, + plugins: ["node"], + extends: ["../.eslintrc.js", "plugin:node/recommended"], + env: { + node: true, + es6: true, + jest: true + }, + parserOptions: { ecmaVersion: 2017, sourceType: "module" }, + rules: { + "node/no-unsupported-features": ["error", { version: 6 }], + "node/no-deprecated-api": "error", + "node/no-missing-import": "error", + "node/no-missing-require": [ + "error", + { + resolvePaths: ["./packages"], + allowModules: [ + "webpack", + "@webpack-cli/generators", + "@webpack-cli/init", + "@webpack-cli/migrate", + "@webpack-cli/utils", + "@webpack-cli/generate-loader", + "@webpack-cli/generate-plugin", + "@webpack-cli/webpack-scaffold" + ] + } + ], + "node/no-unpublished-bin": "error", + "node/no-unpublished-require": [ + "error", + { + allowModules: [ + "webpack", + "webpack-dev-server", + "@webpack-cli/generators", + "@webpack-cli/init", + "@webpack-cli/migrate", + "@webpack-cli/utils", + "@webpack-cli/generate-loader", + "@webpack-cli/generate-plugin", + "@webpack-cli/webpack-scaffold" + ] + } + ], + "node/no-extraneous-require": [ + "error", + { + allowModules: [ + "@webpack-cli/migrate", + "@webpack-cli/generators", + "@webpack-cli/utils", + "@webpack-cli/generate-loader", + "@webpack-cli/generate-plugin", + "@webpack-cli/webpack-scaffold" + ] + } + ], + "node/process-exit-as-throw": "error" + } +}; diff --git a/bin/utils/prompt-command.js b/bin/utils/prompt-command.js index 907434c6c71..20a99a1655f 100644 --- a/bin/utils/prompt-command.js +++ b/bin/utils/prompt-command.js @@ -31,20 +31,20 @@ const npmGlobalRoot = () => { const cp = require("child_process"); return new Promise((resolve, reject) => { const command = cp.spawn("npm", ["root", "-g"]); - command.on("error", (error) => reject(error)); - command.stdout.on("data", (data) => resolve(data.toString())); - command.stderr.on("data", (data) => reject(data)); + command.on("error", error => reject(error)); + command.stdout.on("data", data => resolve(data.toString())); + command.stderr.on("data", data => reject(data)); }); }; const runWhenInstalled = (packages, pathForCmd, ...args) => { - const package = require(pathForCmd); - const func = package.default; - if (typeof func !== 'function') { + const currentPackage = require(pathForCmd); + const func = currentPackage.default; + if (typeof func !== "function") { throw new Error(`@webpack-cli/${packages} failed to export a default function`); } return func(...args); -} +}; module.exports = function promptForInstallation(packages, ...args) { const nameOfPackage = "@webpack-cli/" + packages; @@ -102,19 +102,18 @@ module.exports = function promptForInstallation(packages, ...args) { case "y": case "yes": case "1": { - runCommand(packageManager, options) - .then(_=> { + .then(_ => { if (packages === "init") { npmGlobalRoot() - .then((root) => { + .then(root => { const pathtoInit = path.resolve(root.trim(), "@webpack-cli", "init"); return pathtoInit; }) - .then((pathForInit) => { + .then(pathForInit => { return require(pathForInit).default(...args); }) - .catch((error) => { + .catch(error => { console.error(error); process.exitCode = 1; }); diff --git a/junit.xml b/junit.xml deleted file mode 100644 index 304ccef7cc6..00000000000 --- a/junit.xml +++ /dev/null @@ -1,557 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3fa90818763..f72cab5b2a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2567,6 +2567,58 @@ "integrity": "sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.6.0.tgz", + "integrity": "sha512-U224c29E2lo861TQZs6GSmyC0OYeRNg6bE9UVIiFBxN2MlA0nq2dCrgIVyyRbC05UOcrgf2Wk/CF2gGOPQKUSQ==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "1.6.0", + "@typescript-eslint/typescript-estree": "1.6.0", + "requireindex": "^1.2.0", + "tsutils": "^3.7.0" + }, + "dependencies": { + "tsutils": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz", + "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.6.0.tgz", + "integrity": "sha512-VB9xmSbfafI+/kI4gUK3PfrkGmrJQfh0N4EScT1gZXSZyUxpsBirPL99EWZg9MmPG0pzq/gMtgkk7/rAHj4aQw==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "1.6.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.6.0.tgz", + "integrity": "sha512-A4CanUwfaG4oXobD5y7EXbsOHjCwn8tj1RDd820etpPAjH+Icjc2K9e/DQM1Hac5zH2BSy+u6bjvvF2wwREvYA==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + } + } + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", @@ -4341,12 +4393,6 @@ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -6607,6 +6653,23 @@ } } }, + "eslint-config-prettier": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.1.0.tgz", + "integrity": "sha512-zILwX9/Ocz4SV2vX7ox85AsrAgXV3f2o2gpIicdMIOra48WYqgUnWNH/cR/iHtmD2Vb3dLSC3LiEJnS05Gkw7w==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, "eslint-plugin-es": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz", @@ -7480,7 +7543,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -7501,12 +7565,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7521,17 +7587,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -7648,7 +7717,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -7660,6 +7730,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7674,6 +7745,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7681,12 +7753,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7705,6 +7779,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -7785,7 +7860,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -7797,6 +7873,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -7882,7 +7959,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -7918,6 +7996,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -7937,6 +8016,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -7980,12 +8060,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -15367,6 +15449,12 @@ } } }, + "requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true + }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -16950,36 +17038,6 @@ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "dev": true }, - "tslint": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.14.0.tgz", - "integrity": "sha512-IUla/ieHVnB8Le7LdQFRGlVJid2T/gaJe5VkjzRVSRR6pA2ODYrnfR1hmxi+5+au9l50jBwpbBL34txgv4NnTQ==", - "dev": true, - "requires": { - "babel-code-frame": "^6.22.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.7.0", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", diff --git a/package.json b/package.json index a3d62ea17cc..7d4b80cf5ea 100644 --- a/package.json +++ b/package.json @@ -35,17 +35,16 @@ "docs": "typedoc", "format": "prettier-eslint ./bin/*.js ./test/**/*.js ./packages/**/*.js --write", "lint:codeOnly": "eslint \"{bin}/**/!(__testfixtures__)/*.js\" \"{bin}/**.js\"", - "lint": "eslint \"./bin/*.js\" \"./test/**/*.js\" \"packages/**/!(node_modules)/*.test.js\"", + "lint": "eslint \"./bin/*.js\" \"./test/**/*.js\" \"packages/**/!(node_modules)/*.ts\"", "postinstall": "node ./bin/opencollective.js", - "pretest": "npm run build && npm run lint && npm run tslint", + "pretest": "npm run build && npm run lint", "reportCoverage": "nyc report --reporter=json && codecov -f coverage/coverage-final.json --disable=gcov", "test": "nyc jest --maxWorkers=4 --reporters=default --reporters=jest-junit", "test:cli": "nyc jest test/ --maxWorkers=4 --reporters=default --reporters=jest-junit", "test:packages": "nyc jest packages/ --maxWorkers=4 --reporters=default --reporters=jest-junit", "test:ci": "nyc jest --maxWorkers=$(nproc) --reporters=default --reporters=jest-junit", "travis:integration": "npm run build && npm run test && npm run reportCoverage", - "travis:lint": "npm run build && npm run lint && npm run tslint", - "tslint": "tslint -c tslint.json \"packages/**/*.ts\"", + "travis:lint": "npm run build && npm run lint", "watch": "npm run build && tsc -w" }, "husky": { @@ -58,10 +57,6 @@ "{packages,bin}/**/!(__testfixtures__)/**.js": [ "eslint --fix", "git add" - ], - "{packages,bin}/**/!(__testfixtures__)/**.ts": [ - "tslint --fix", - "git add" ] }, "jest": { @@ -145,6 +140,8 @@ "@commitlint/travis-cli": "^7.2.1", "@types/jest": "^23.3.14", "@types/node": "^10.12.9", + "@typescript-eslint/eslint-plugin": "^1.6.0", + "@typescript-eslint/parser": "^1.6.0", "babel-preset-env": "^1.7.0", "babel-preset-jest": "^24.3.0", "bundlesize": "^0.17.0", @@ -154,6 +151,7 @@ "conventional-changelog-cli": "^2.0.11", "cz-customizable": "^5.3.0", "eslint": "^5.9.0", + "eslint-config-prettier": "^4.1.0", "eslint-plugin-node": "^8.0.0", "esm": "^3.2.14", "execa": "^1.0.0", @@ -170,7 +168,6 @@ "schema-utils": "^1.0.0", "ts-jest": "^23.10.5", "ts-node": "^7.0.1", - "tslint": "^5.11.0", "typedoc": "^0.13.0", "typedoc-plugin-monorepo": "^0.1.0", "typescript": "^3.3.1", diff --git a/packages/add/.eslintrc b/packages/add/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/add/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/add/index.ts b/packages/add/index.ts index bc5fe92f195..a63515c9347 100644 --- a/packages/add/index.ts +++ b/packages/add/index.ts @@ -12,11 +12,11 @@ import modifyConfigHelper from "@webpack-cli/utils/modify-config-helper"; const DEFAULT_WEBPACK_CONFIG_FILENAME = "webpack.config.js"; export default function add(...args: string[]): Function { - const filePaths = args.slice(3); - let configFile = DEFAULT_WEBPACK_CONFIG_FILENAME; - if (filePaths.length) { - configFile = filePaths[0]; - } + const filePaths = args.slice(3); + let configFile = DEFAULT_WEBPACK_CONFIG_FILENAME; + if (filePaths.length) { + configFile = filePaths[0]; + } - return modifyConfigHelper("add", defaultGenerator, configFile); + return modifyConfigHelper("add", defaultGenerator, configFile); } diff --git a/packages/add/package-lock.json b/packages/add/package-lock.json index 61a703534e8..dfebd320fe5 100644 --- a/packages/add/package-lock.json +++ b/packages/add/package-lock.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/add", - "version": "0.1.2", + "version": "0.1.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -10,6 +10,109 @@ "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.6.0.tgz", + "integrity": "sha512-U224c29E2lo861TQZs6GSmyC0OYeRNg6bE9UVIiFBxN2MlA0nq2dCrgIVyyRbC05UOcrgf2Wk/CF2gGOPQKUSQ==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "1.6.0", + "@typescript-eslint/typescript-estree": "1.6.0", + "requireindex": "^1.2.0", + "tsutils": "^3.7.0" + } + }, + "@typescript-eslint/parser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.6.0.tgz", + "integrity": "sha512-VB9xmSbfafI+/kI4gUK3PfrkGmrJQfh0N4EScT1gZXSZyUxpsBirPL99EWZg9MmPG0pzq/gMtgkk7/rAHj4aQw==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "1.6.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.6.0.tgz", + "integrity": "sha512-A4CanUwfaG4oXobD5y7EXbsOHjCwn8tj1RDd820etpPAjH+Icjc2K9e/DQM1Hac5zH2BSy+u6bjvvF2wwREvYA==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true + }, + "prettier": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.0.tgz", + "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==", + "dev": true + }, + "requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tsutils": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz", + "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "typescript": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", diff --git a/packages/add/tsconfig.json b/packages/add/tsconfig.json index 8b1269a36b6..2a74d10580a 100644 --- a/packages/add/tsconfig.json +++ b/packages/add/tsconfig.json @@ -1,3 +1,3 @@ { - "extends": "../../tsconfig.packages.json" + "extends": "../../tsconfig.packages.json" } diff --git a/packages/generate-loader/.eslintrc b/packages/generate-loader/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/generate-loader/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/generate-loader/index.ts b/packages/generate-loader/index.ts index b56835cfc97..8f280fc6226 100644 --- a/packages/generate-loader/index.ts +++ b/packages/generate-loader/index.ts @@ -1,8 +1,6 @@ import LoaderGenerator from "@webpack-cli/generators/loader-generator"; import * as yeoman from "yeoman-environment"; -import { IYeoman } from "./types/Yeoman"; - /** * Runs a yeoman generator to create a new webpack loader project * @returns {void} diff --git a/packages/generate-loader/package-lock.json b/packages/generate-loader/package-lock.json index b9561b5e442..90c9c102e80 100644 --- a/packages/generate-loader/package-lock.json +++ b/packages/generate-loader/package-lock.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/generate-loader", - "version": "0.1.2", + "version": "0.1.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -24,6 +24,47 @@ "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.6.0.tgz", + "integrity": "sha512-U224c29E2lo861TQZs6GSmyC0OYeRNg6bE9UVIiFBxN2MlA0nq2dCrgIVyyRbC05UOcrgf2Wk/CF2gGOPQKUSQ==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "1.6.0", + "@typescript-eslint/typescript-estree": "1.6.0", + "requireindex": "^1.2.0", + "tsutils": "^3.7.0" + } + }, + "@typescript-eslint/parser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.6.0.tgz", + "integrity": "sha512-VB9xmSbfafI+/kI4gUK3PfrkGmrJQfh0N4EScT1gZXSZyUxpsBirPL99EWZg9MmPG0pzq/gMtgkk7/rAHj4aQw==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "1.6.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.6.0.tgz", + "integrity": "sha512-A4CanUwfaG4oXobD5y7EXbsOHjCwn8tj1RDd820etpPAjH+Icjc2K9e/DQM1Hac5zH2BSy+u6bjvvF2wwREvYA==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + } + } + }, "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", @@ -384,6 +425,37 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -905,6 +977,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true + }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -1142,6 +1220,12 @@ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, + "prettier": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.0.tgz", + "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==", + "dev": true + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -1185,6 +1269,12 @@ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" }, + "requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -1571,6 +1661,15 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" }, + "tsutils": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz", + "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "typescript": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", diff --git a/packages/generate-loader/types/Yeoman.ts b/packages/generate-loader/types/Yeoman.ts index 4c56a771c00..16615324c03 100644 --- a/packages/generate-loader/types/Yeoman.ts +++ b/packages/generate-loader/types/Yeoman.ts @@ -1,12 +1,12 @@ -interface IRunEnv extends Object { +interface RunEnv extends Object { on?: (event: string, callbackFn: Function) => void; } -export interface IYeoman extends Object { - registerStub?(generator: IGenerator, namespace: string): void; - run?(target: string, options?: object, done?: Function): IRunEnv; +export interface Yeoman extends Object { + registerStub?(generator: YeoGenerator, namespace: string): void; + run?(target: string, options?: object, done?: Function): RunEnv; } -export interface IGenerator extends Object { +export interface YeoGenerator extends Object { composeWith?: (path: string) => void; } diff --git a/packages/generate-plugin/.eslintrc b/packages/generate-plugin/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/generate-plugin/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/generate-plugin/index.ts b/packages/generate-plugin/index.ts index c5fab329c0a..50448da6713 100644 --- a/packages/generate-plugin/index.ts +++ b/packages/generate-plugin/index.ts @@ -1,8 +1,6 @@ import PluginGenerator from "@webpack-cli/generators/plugin-generator"; import * as yeoman from "yeoman-environment"; -import { IYeoman } from "./types/Yeoman"; - /** * Runs a yeoman generator to create a new webpack plugin project * @returns {void} diff --git a/packages/generate-plugin/types/Yeoman.ts b/packages/generate-plugin/types/Yeoman.ts index 4c56a771c00..1eab3f4b3d5 100644 --- a/packages/generate-plugin/types/Yeoman.ts +++ b/packages/generate-plugin/types/Yeoman.ts @@ -1,12 +1,12 @@ -interface IRunEnv extends Object { +interface RunEnv extends Object { on?: (event: string, callbackFn: Function) => void; } -export interface IYeoman extends Object { - registerStub?(generator: IGenerator, namespace: string): void; - run?(target: string, options?: object, done?: Function): IRunEnv; +export interface Yeoman extends Object { + registerStub?(generator: Generator, namespace: string): void; + run?(target: string, options?: object, done?: Function): RunEnv; } -export interface IGenerator extends Object { +export interface Generator extends Object { composeWith?: (path: string) => void; } diff --git a/packages/generators/.eslintrc b/packages/generators/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/generators/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/generators/__tests__/.eslintrc b/packages/generators/__tests__/.eslintrc new file mode 100644 index 00000000000..e3e3b0884ac --- /dev/null +++ b/packages/generators/__tests__/.eslintrc @@ -0,0 +1,9 @@ +{ + "root": true, + "extends": [ + "../.eslintrc" + ], + "rules": { + "@typescript-eslint/explicit-function-return-type": ["off"] + } +} diff --git a/packages/generators/loader-generator.test.js b/packages/generators/__tests__/loader-generator.test.ts similarity index 84% rename from packages/generators/loader-generator.test.js rename to packages/generators/__tests__/loader-generator.test.ts index 283f6bf551a..f8d6c4667c3 100644 --- a/packages/generators/loader-generator.test.js +++ b/packages/generators/__tests__/loader-generator.test.ts @@ -1,6 +1,4 @@ -"use strict"; - -const { makeLoaderName } = require("./loader-generator"); +import { makeLoaderName } from "../loader-generator"; describe("makeLoaderName", () => { it("should kebab-case loader name and append '-loader'", () => { diff --git a/packages/generators/add-generator.ts b/packages/generators/add-generator.ts index 134c10be8b4..e05da4d3c54 100644 --- a/packages/generators/add-generator.ts +++ b/packages/generators/add-generator.ts @@ -7,20 +7,13 @@ import * as path from "path"; import npmExists from "@webpack-cli/utils/npm-exists"; import { getPackageManager } from "@webpack-cli/utils/package-manager"; import PROP_TYPES from "@webpack-cli/utils/prop-types"; -import { - AutoComplete, - Confirm, - IInquirerInput, - Input, - List, -} from "@webpack-cli/webpack-scaffold"; +import { AutoComplete, Confirm, InquirerInput, Input, List } from "@webpack-cli/webpack-scaffold"; -import { ISchemaProperties, IWebpackOptions } from "./types"; +import { SchemaProperties, WebpackOptions } from "./types"; import entryQuestions from "./utils/entry"; -// tslint:disable:no-var-requires -const webpackDevServerSchema = require("webpack-dev-server/lib/options.json"); -const webpackSchema = require("./utils/optionsSchema.json"); +import webpackDevServerSchema from "webpack-dev-server/lib/options.json"; +import webpackSchema from "./utils/optionsSchema.json"; const PROPS: string[] = Array.from(PROP_TYPES.keys()); /** @@ -28,7 +21,7 @@ const PROPS: string[] = Array.from(PROP_TYPES.keys()); * Replaces the string with a substring at the given index * https://gist.github.com/efenacigiray/9367920 * - * @param {String} string - string to be modified + * @param {String} str - string to be modified * @param {Number} index - index to replace from * @param {String} replace - string to replace starting from index * @@ -51,11 +44,13 @@ function replaceAt(str: string, index: number, replace: string): string { */ const traverseAndGetProperties = (arr: object[], prop: string): boolean => { let hasProp = false; - arr.forEach((p: object): void => { - if (p[prop]) { - hasProp = true; + arr.forEach( + (p: object): void => { + if (p[prop]) { + hasProp = true; + } } - }); + ); return hasProp; }; @@ -71,11 +66,7 @@ const traverseAndGetProperties = (arr: object[], prop: string): boolean => { */ const searchProps = (answers: object, input: string): Promise => { input = input || ""; - return Promise.resolve( - PROPS.filter((prop: string): boolean => - prop.toLowerCase().includes(input.toLowerCase()), - ), - ); + return Promise.resolve(PROPS.filter((prop: string): boolean => prop.toLowerCase().includes(input.toLowerCase()))); }; /** @@ -91,452 +82,414 @@ export default class AddGenerator extends Generator { private dependencies: string[]; private configuration: { config: { - configName?: string, - topScope?: string[], + configName?: string; + topScope?: string[]; item?: string; - webpackOptions?: IWebpackOptions, - }, + webpackOptions?: WebpackOptions; + }; }; - constructor(args, opts) { + public constructor(args, opts) { super(args, opts); this.dependencies = []; this.configuration = { config: { topScope: ["const webpack = require('webpack')"], - webpackOptions: {}, - }, + webpackOptions: {} + } }; const { registerPrompt } = this.env.adapter.promptModule; registerPrompt("autocomplete", autoComplete); } - public prompting() { - const done: (_?: void) => void | boolean = this.async(); + public prompting(): void { + const done: () => void | boolean = this.async(); let action: string; const self: this = this; - const manualOrListInput: (promptAction: string) => IInquirerInput = (promptAction: string) => + const manualOrListInput: (promptAction: string) => InquirerInput = (promptAction: string): InquirerInput => Input("actionAnswer", `What do you want to add to ${promptAction}?`); - let inputPrompt: IInquirerInput; + let inputPrompt: InquirerInput; // first index indicates if it has a deep prop, 2nd indicates what kind of + // TODO: this must be reviewed. It starts as an array of booleans but after that it get overridden + // Bye bye functional programming. + // eslint-disable-next-line const isDeepProp: any[] = [false, false]; return this.prompt([ - AutoComplete( - "actionType", - "What property do you want to add to?", - { - pageSize: 7, - source: searchProps, - suggestOnly: false, - }, - ), - ]) - .then((actionTypeAnswer: { - actionType: string, - }) => { - // Set initial prop, like devtool - this.configuration.config.webpackOptions[ - actionTypeAnswer.actionType - ] = null; - // update the action variable, we're using it later - action = actionTypeAnswer.actionType; + AutoComplete("actionType", "What property do you want to add to?", { + pageSize: 7, + source: searchProps, + suggestOnly: false }) - .then((_: void) => { - if (action === "entry") { - return this.prompt([ - Confirm("entryType", "Will your application have multiple bundles?", false), - ]) - .then((entryTypeAnswer: { - entryType: boolean, - }) => { - // Ask different questions for entry points - return entryQuestions(self, entryTypeAnswer); - }) - .then((entryOptions: { - entryType: boolean; - }) => { - this.configuration.config.webpackOptions.entry = entryOptions; - this.configuration.config.item = action; - }); + ]) + .then( + (actionTypeAnswer: { actionType: string }): void => { + // Set initial prop, like devtool + this.configuration.config.webpackOptions[actionTypeAnswer.actionType] = null; + // update the action variable, we're using it later + action = actionTypeAnswer.actionType; + } + ) + .then( + (): void => { + if (action === "entry") { + return this.prompt([ + Confirm("entryType", "Will your application have multiple bundles?", false) + ]) + .then( + (entryTypeAnswer: { entryType: boolean }): Promise<{}> => { + // Ask different questions for entry points + return entryQuestions(self, entryTypeAnswer); + } + ) + .then( + (entryOptions: { entryType: boolean }): void => { + this.configuration.config.webpackOptions.entry = entryOptions; + this.configuration.config.item = action; + } + ); } else { if (action === "topScope") { - return this.prompt([ - Input("topScope", "What do you want to add to topScope?"), - ]) - .then((topScopeAnswer: { - topScope: string; - }) => { - this.configuration.config.topScope.push(topScopeAnswer.topScope); - done(); - }); + return this.prompt([Input("topScope", "What do you want to add to topScope?")]).then( + (topScopeAnswer: { topScope: string }): void => { + this.configuration.config.topScope.push(topScopeAnswer.topScope); + done(); + } + ); } } - const temp = action; - if (action === "resolveLoader") { - action = "resolve"; - } - const webpackSchemaProp: ISchemaProperties = webpackSchema.definitions[action]; - /* - * https://github.com/webpack/webpack/blob/next/schemas/WebpackOptions.json - * Find the properties directly in the properties prop, or the anyOf prop - */ - let defOrPropDescription: object = webpackSchemaProp - ? webpackSchemaProp.properties - : webpackSchema.properties[action].properties + const temp = action; + if (action === "resolveLoader") { + action = "resolve"; + } + const webpackSchemaProp: SchemaProperties = webpackSchema.definitions[action]; + /* + * https://github.com/webpack/webpack/blob/next/schemas/WebpackOptions.json + * Find the properties directly in the properties prop, or the anyOf prop + */ + let defOrPropDescription: object = webpackSchemaProp + ? webpackSchemaProp.properties + : webpackSchema.properties[action].properties ? webpackSchema.properties[action].properties : webpackSchema.properties[action].anyOf - ? webpackSchema.properties[action].anyOf.filter( - (p: { - properties?: object, - enum?: any[], - }) => p.properties || p.enum, - ) - : null; - if (Array.isArray(defOrPropDescription)) { - // Todo: Generalize these to go through the array, then merge enum with props if needed - const hasPropertiesProp: boolean = traverseAndGetProperties( - defOrPropDescription, - "properties", - ); - const hasEnumProp: boolean = traverseAndGetProperties( - defOrPropDescription, - "enum", - ); - /* as we know he schema only has two arrays that might hold our values, - * check them for either having arr.enum or arr.properties - */ - if (hasPropertiesProp) { - defOrPropDescription = - defOrPropDescription[0].properties || - defOrPropDescription[1].properties; - if (!defOrPropDescription) { - defOrPropDescription = defOrPropDescription[0].enum; - } - // TODO: manually implement stats and devtools like sourcemaps - } else if (hasEnumProp) { - const originalPropDesc: object = defOrPropDescription[0].enum; - // Array -> Object -> Merge objects into one for compat in manualOrListInput - defOrPropDescription = Object.keys(defOrPropDescription[0].enum) - .map((p: string): object => { - return Object.assign( - {}, - { - [originalPropDesc[p]]: "noop", - }, - ); - }) - .reduce((result: object, currentObject: object): object => { - for (const key in currentObject) { - if (currentObject.hasOwnProperty(key)) { - result[key] = currentObject[key]; + ? webpackSchema.properties[action].anyOf.filter( + (p: { properties?: object; enum?: string[] }): boolean => !!p.properties || !!p.enum + ) + : null; + if (Array.isArray(defOrPropDescription)) { + // Todo: Generalize these to go through the array, then merge enum with props if needed + const hasPropertiesProp: boolean = traverseAndGetProperties(defOrPropDescription, "properties"); + const hasEnumProp: boolean = traverseAndGetProperties(defOrPropDescription, "enum"); + /* as we know he schema only has two arrays that might hold our values, + * check them for either having arr.enum or arr.properties + */ + if (hasPropertiesProp) { + defOrPropDescription = + defOrPropDescription[0].properties || defOrPropDescription[1].properties; + if (!defOrPropDescription) { + defOrPropDescription = defOrPropDescription[0].enum; + } + // TODO: manually implement stats and devtools like sourcemaps + } else if (hasEnumProp) { + const originalPropDesc: object = defOrPropDescription[0].enum; + // Array -> Object -> Merge objects into one for compat in manualOrListInput + defOrPropDescription = Object.keys(defOrPropDescription[0].enum) + .map( + (p: string): object => { + return Object.assign( + {}, + { + [originalPropDesc[p]]: "noop" + } + ); } - } - return result; - }, {}); + ) + .reduce((result: object, currentObject: object): object => { + for (const key in currentObject) { + if (currentObject.hasOwnProperty(key)) { + result[key] = currentObject[key]; + } + } + return result; + }, {}); + } } - } - // WDS has its own schema, so we gonna need to check that too - const webpackDevserverSchemaProp: ISchemaProperties = - action === "devServer" ? webpackDevServerSchema : null; - // Watch has a boolean arg, but we need to append to it manually - if (action === "watch") { - defOrPropDescription = { - false: {}, - true: {}, - }; - } - if (action === "mode") { - defOrPropDescription = { - development: {}, - production: {}, - }; - } - action = temp; - if (action === "resolveLoader") { - defOrPropDescription = Object.assign(defOrPropDescription, { - moduleExtensions: {}, - }); - } - // If we've got a schema prop or devServer Schema Prop - if (defOrPropDescription || webpackDevserverSchemaProp) { - // Check for properties in definitions[action] or properties[action] - if (defOrPropDescription) { - if (action !== "devtool") { - // Add the option of adding an own variable if the user wants - defOrPropDescription = Object.assign(defOrPropDescription, { - other: {}, - }); + // WDS has its own schema, so we gonna need to check that too + const webpackDevserverSchemaProp: SchemaProperties = + action === "devServer" ? webpackDevServerSchema : null; + // Watch has a boolean arg, but we need to append to it manually + if (action === "watch") { + defOrPropDescription = { + false: {}, + true: {} + }; + } + if (action === "mode") { + defOrPropDescription = { + development: {}, + production: {} + }; + } + action = temp; + if (action === "resolveLoader") { + defOrPropDescription = Object.assign(defOrPropDescription, { + moduleExtensions: {} + }); + } + // If we've got a schema prop or devServer Schema Prop + if (defOrPropDescription || webpackDevserverSchemaProp) { + // Check for properties in definitions[action] or properties[action] + if (defOrPropDescription) { + if (action !== "devtool") { + // Add the option of adding an own variable if the user wants + defOrPropDescription = Object.assign(defOrPropDescription, { + other: {} + }); + } else { + // The schema doesn't have the source maps we can prompt, so add those + defOrPropDescription = Object.assign(defOrPropDescription, { + "cheap-eval-source-map": {}, + "cheap-module-eval-source-map": {}, + "cheap-module-source-map": {}, + "cheap-source-map": {}, + eval: {}, + "eval-source-map": {}, + "hidden-source-map": {}, + "inline-cheap-module-source-map": {}, + "inline-cheap-source-map": {}, + "inline-source-map": {}, + "nosources-source-map": {}, + "source-map": {} + }); + } + inputPrompt = List( + "actionAnswer", + `What do you want to add to ${action}?`, + Object.keys(defOrPropDescription) + ); + // We know we're gonna append some deep prop like module.rule + isDeepProp[0] = true; + } else if (webpackDevserverSchemaProp) { + // Append the custom property option + webpackDevserverSchemaProp.properties = Object.assign( + webpackDevserverSchemaProp.properties, + { + other: {} + } + ); + inputPrompt = List( + "actionAnswer", + `What do you want to add to ${action}?`, + Object.keys(webpackDevserverSchemaProp.properties) + ); + // We know we are in a devServer.prop scenario + isDeepProp[0] = true; } else { - // The schema doesn't have the source maps we can prompt, so add those - defOrPropDescription = Object.assign(defOrPropDescription, { - "cheap-eval-source-map": {}, - "cheap-module-eval-source-map": {}, - "cheap-module-source-map": {}, - "cheap-source-map": {}, - "eval": {}, - "eval-source-map": {}, - "hidden-source-map": {}, - "inline-cheap-module-source-map": {}, - "inline-cheap-source-map": {}, - "inline-source-map": {}, - "nosources-source-map": {}, - "source-map": {}, - }); + // manual input if non-existent + inputPrompt = manualOrListInput(action); } - inputPrompt = List( - "actionAnswer", - `What do you want to add to ${action}?`, - Object.keys(defOrPropDescription), - ); - // We know we're gonna append some deep prop like module.rule - isDeepProp[0] = true; - } else if (webpackDevserverSchemaProp) { - // Append the custom property option - webpackDevserverSchemaProp.properties = Object.assign( - webpackDevserverSchemaProp.properties, - { - other: {}, - }, - ); - inputPrompt = List( - "actionAnswer", - `What do you want to add to ${action}?`, - Object.keys(webpackDevserverSchemaProp.properties), - ); - // We know we are in a devServer.prop scenario - isDeepProp[0] = true; } else { - // manual input if non-existent inputPrompt = manualOrListInput(action); } - } else { - inputPrompt = manualOrListInput(action); - } - return this.prompt([ - inputPrompt, - ]); - }) - .then((answerToAction: { - actionAnswer: string, - }) => { - if (!answerToAction) { - done(); - return; + return this.prompt([inputPrompt]); } - /* - * Plugins got their own logic, - * find the names of each natively plugin and check if it matches - */ - if (action === "plugins") { - const pluginExist: string = glob - .sync([ - "node_modules/webpack/lib/*Plugin.js", - "node_modules/webpack/lib/**/*Plugin.js", - ]) - .map((p: string): string => - p - .split("/") - .pop() - .replace(".js", ""), - ) - .find( - (p: string): boolean => p.toLowerCase().indexOf(answerToAction.actionAnswer) >= 0, - ); - - if (pluginExist) { - this.configuration.config.item = pluginExist; - const pluginsSchemaPath: string = glob - .sync([ - "node_modules/webpack/schemas/plugins/*Plugin.json", - "node_modules/webpack/schemas/plugins/**/*Plugin.json", - ]) - .find( - (p: string): boolean => + ) + .then( + (answerToAction: { actionAnswer: string }): void => { + if (!answerToAction) { + done(); + return; + } + /* + * Plugins got their own logic, + * find the names of each natively plugin and check if it matches + */ + if (action === "plugins") { + const pluginExist: string = glob + .sync(["node_modules/webpack/lib/*Plugin.js", "node_modules/webpack/lib/**/*Plugin.js"]) + .map( + (p: string): string => p .split("/") .pop() - .replace(".json", "") - .toLowerCase() - .indexOf(answerToAction.actionAnswer) >= 0, - ); - if (pluginsSchemaPath) { - const constructorPrefix: string = - pluginsSchemaPath.indexOf("optimize") >= 0 - ? "webpack.optimize" - : "webpack"; - const resolvePluginsPath: string = path.resolve(pluginsSchemaPath); - const pluginSchema: object = resolvePluginsPath - ? require(resolvePluginsPath) - : null; - let pluginsSchemaProps: string[] = ["other"]; - if (pluginSchema) { - Object.keys(pluginSchema) - .filter((p: string): boolean => Array.isArray(pluginSchema[p])) - .forEach((p: string): void => { - Object.keys(pluginSchema[p]).forEach((n: string): void => { - if (pluginSchema[p][n].properties) { - pluginsSchemaProps = Object.keys( - pluginSchema[p][n].properties, + .replace(".js", "") + ) + .find((p: string): boolean => p.toLowerCase().indexOf(answerToAction.actionAnswer) >= 0); + + if (pluginExist) { + this.configuration.config.item = pluginExist; + const pluginsSchemaPath: string = glob + .sync([ + "node_modules/webpack/schemas/plugins/*Plugin.json", + "node_modules/webpack/schemas/plugins/**/*Plugin.json" + ]) + .find( + (p: string): boolean => + p + .split("/") + .pop() + .replace(".json", "") + .toLowerCase() + .indexOf(answerToAction.actionAnswer) >= 0 + ); + if (pluginsSchemaPath) { + const constructorPrefix: string = + pluginsSchemaPath.indexOf("optimize") >= 0 ? "webpack.optimize" : "webpack"; + const resolvePluginsPath: string = path.resolve(pluginsSchemaPath); + const pluginSchema: object = resolvePluginsPath ? require(resolvePluginsPath) : null; + let pluginsSchemaProps: string[] = ["other"]; + if (pluginSchema) { + Object.keys(pluginSchema) + .filter((p: string): boolean => Array.isArray(pluginSchema[p])) + .forEach( + (p: string): void => { + Object.keys(pluginSchema[p]).forEach( + (n: string): void => { + if (pluginSchema[p][n].properties) { + pluginsSchemaProps = Object.keys( + pluginSchema[p][n].properties + ); + } + } ); } - }); - }); - } + ); + } - return this.prompt([ - List( - "pluginsPropType", - `What property do you want to add ${pluginExist}?`, - pluginsSchemaProps, - ), - ]).then((pluginsPropAnswer: { - pluginsPropType: string, - }) => { return this.prompt([ - Input( - "pluginsPropTypeVal", - `What value should ${pluginExist}.${ - pluginsPropAnswer.pluginsPropType - } have?`, - ), - ]).then((valForProp: { - pluginsPropTypeVal: string, - }) => { - this.configuration.config.webpackOptions[action] = { - [`${constructorPrefix}.${pluginExist}`]: { - [pluginsPropAnswer.pluginsPropType]: - valForProp.pluginsPropTypeVal, - }, - }; - done(); - }); - }); + List( + "pluginsPropType", + `What property do you want to add ${pluginExist}?`, + pluginsSchemaProps + ) + ]).then( + (pluginsPropAnswer: { pluginsPropType: string }): Promise<{}> => { + return this.prompt([ + Input( + "pluginsPropTypeVal", + `What value should ${pluginExist}.${ + pluginsPropAnswer.pluginsPropType + } have?` + ) + ]).then( + (valForProp: { pluginsPropTypeVal: string }): void => { + this.configuration.config.webpackOptions[action] = { + [`${constructorPrefix}.${pluginExist}`]: { + [pluginsPropAnswer.pluginsPropType]: + valForProp.pluginsPropTypeVal + } + }; + done(); + } + ); + } + ); + } else { + this.configuration.config.webpackOptions[action] = `new webpack.${pluginExist}`; + done(); + } } else { - this.configuration.config.webpackOptions[ - action - ] = `new webpack.${pluginExist}`; - done(); + // If its not in webpack, check npm + npmExists(answerToAction.actionAnswer).then( + (p: boolean): void => { + if (p) { + this.dependencies.push(answerToAction.actionAnswer); + const normalizePluginName = answerToAction.actionAnswer.replace( + "-webpack-plugin", + "Plugin" + ); + const pluginName = replaceAt( + normalizePluginName, + 0, + normalizePluginName.charAt(0).toUpperCase() + ); + this.configuration.config.topScope.push( + `const ${pluginName} = require("${answerToAction.actionAnswer}")` + ); + this.configuration.config.webpackOptions[action] = `new ${pluginName}`; + this.configuration.config.item = answerToAction.actionAnswer; + done(); + this.scheduleInstallTask(getPackageManager(), this.dependencies, { + "save-dev": true + }); + } else { + console.error( + answerToAction.actionAnswer, + "doesn't exist on NPM or is built in webpack, please check for any misspellings." + ); + process.exit(0); + } + } + ); } } else { - // If its not in webpack, check npm - npmExists(answerToAction.actionAnswer) - .then((p: string) => { - if (p) { - this.dependencies.push(answerToAction.actionAnswer); - const normalizePluginName = answerToAction.actionAnswer.replace( - "-webpack-plugin", - "Plugin", - ); - const pluginName = replaceAt( - normalizePluginName, - 0, - normalizePluginName.charAt(0).toUpperCase(), - ); - this.configuration.config.topScope.push( - `const ${pluginName} = require("${ - answerToAction.actionAnswer - }")`, - ); - this.configuration.config.webpackOptions[ - action - ] = `new ${pluginName}`; - this.configuration.config.item = answerToAction.actionAnswer; - done(); - this.scheduleInstallTask(getPackageManager(), this.dependencies, { - "save-dev": true, - }); - } else { - console.error( - answerToAction.actionAnswer, - "doesn't exist on NPM or is built in webpack, please check for any misspellings.", - ); - process.exit(0); + // If we're in the scenario with a deep-property + if (isDeepProp[0]) { + isDeepProp[1] = answerToAction.actionAnswer; + if ( + isDeepProp[1] !== "other" && + (action === "devtool" || action === "watch" || action === "mode") + ) { + this.configuration.config.item = action; + this.configuration.config.webpackOptions[action] = answerToAction.actionAnswer; + done(); + return; + } + // Either we are adding directly at the property, else we're in a prop.theOne scenario + const actionMessage = + isDeepProp[1] === "other" + ? `What do you want the key on ${action} to be? (press enter if you want it directly as a value on the property)` + : `What do you want the value of ${isDeepProp[1]} to be?`; + + this.prompt([Input("deepProp", actionMessage)]).then( + (deepPropAns: { deepProp: string }): void => { + // The other option needs to be validated of either being empty or not + if (isDeepProp[1] === "other") { + const othersDeepPropKey: string = deepPropAns.deepProp + ? `What do you want the value of ${deepPropAns.deepProp} to be?` // eslint-disable-line + : `What do you want to be the value of ${action} to be?`; + // Push the answer to the array we have created, so we can use it later + isDeepProp.push(deepPropAns.deepProp); + this.prompt([Input("innerProp", othersDeepPropKey)]).then( + (innerPropAns: { innerProp }): void => { + // Check length, if it has none, add the prop directly on the given action + if (isDeepProp[2].length === 0) { + this.configuration.config.item = action; + this.configuration.config.webpackOptions[action] = + innerPropAns.innerProp; + } else { + // If not, we're adding to something like devServer.myProp + this.configuration.config.item = action + "." + isDeepProp[2]; + this.configuration.config.webpackOptions[action] = { + [isDeepProp[2]]: innerPropAns.innerProp + }; + } + done(); + } + ); + } else { + // We got the schema prop, we've correctly prompted it, and can add it directly + this.configuration.config.item = `${action}.${isDeepProp[1]}`; + this.configuration.config.webpackOptions[action] = { + [isDeepProp[1]]: deepPropAns.deepProp + }; + done(); + } } - }); - } - } else { - // If we're in the scenario with a deep-property - if (isDeepProp[0]) { - isDeepProp[1] = answerToAction.actionAnswer; - if ( - isDeepProp[1] !== "other" && - (action === "devtool" || action === "watch" || action === "mode") - ) { + ); + } else { + // We're asking for input-only this.configuration.config.item = action; - this.configuration.config.webpackOptions[action] = - answerToAction.actionAnswer; + this.configuration.config.webpackOptions[action] = answerToAction.actionAnswer; done(); - return; } - // Either we are adding directly at the property, else we're in a prop.theOne scenario - const actionMessage = - isDeepProp[1] === "other" - ? `What do you want the key on ${ - action - } to be? (press enter if you want it directly as a value on the property)` - : `What do you want the value of ${isDeepProp[1]} to be?`; - - this.prompt([ - Input("deepProp", actionMessage), - ]).then( - (deepPropAns: { - deepProp: string, - }) => { - // The other option needs to be validated of either being empty or not - if (isDeepProp[1] === "other") { - const othersDeepPropKey: string = deepPropAns.deepProp - ? `What do you want the value of ${ - deepPropAns.deepProp - } to be?` // eslint-disable-line - : `What do you want to be the value of ${action} to be?`; - // Push the answer to the array we have created, so we can use it later - isDeepProp.push(deepPropAns.deepProp); - this.prompt([ - Input("innerProp", othersDeepPropKey), - ]).then( - (innerPropAns: { - innerProp, - }) => { - // Check length, if it has none, add the prop directly on the given action - if (isDeepProp[2].length === 0) { - this.configuration.config.item = action; - this.configuration.config.webpackOptions[action] = - innerPropAns.innerProp; - } else { - // If not, we're adding to something like devServer.myProp - this.configuration.config.item = - action + "." + isDeepProp[2]; - this.configuration.config.webpackOptions[action] = { - [isDeepProp[2]]: innerPropAns.innerProp, - }; - } - done(); - }, - ); - } else { - // We got the schema prop, we've correctly prompted it, and can add it directly - this.configuration.config.item = `${action}.${isDeepProp[1]}`; - this.configuration.config.webpackOptions[action] = { - [isDeepProp[1]]: deepPropAns.deepProp, - }; - done(); - } - }, - ); - } else { - // We're asking for input-only - this.configuration.config.item = action; - this.configuration.config.webpackOptions[action] = - answerToAction.actionAnswer; - done(); } } - }); + ); } - public writing() { + public writing(): void { this.config.set("configuration", this.configuration); } } diff --git a/packages/generators/addon-generator.ts b/packages/generators/addon-generator.ts index 7f83dddf104..d81c2d0b378 100644 --- a/packages/generators/addon-generator.ts +++ b/packages/generators/addon-generator.ts @@ -3,7 +3,8 @@ import * as path from "path"; import Generator = require("yeoman-generator"); import * as copyUtils from "@webpack-cli/utils/copy-utils"; -import { IInquirerScaffoldObject } from "@webpack-cli/webpack-scaffold"; +import { InquirerScaffoldObject } from "@webpack-cli/webpack-scaffold"; +import { YeoGenerator } from "../generate-loader/types/Yeoman"; /** * Creates a Yeoman Generator that generates a project conforming @@ -27,58 +28,58 @@ import { IInquirerScaffoldObject } from "@webpack-cli/webpack-scaffold"; * @returns {Generator} A class extending Generator */ export default function addonGenerator( - prompts: IInquirerScaffoldObject[], + prompts: InquirerScaffoldObject[], templateDir: string, copyFiles: string[], copyTemplateFiles: string[], - templateFn: Function, -) { + templateFn: Function +): YeoGenerator { return class AddOnGenerator extends Generator { - public props: IInquirerScaffoldObject; - private copy: (value: string, index: number, array: string[]) => void; - private copyTpl: (value: string, index: number, array: string[]) => void; + public props: InquirerScaffoldObject; + public copy: (value: string, index: number, array: string[]) => void; + public copyTpl: (value: string, index: number, array: string[]) => void; public prompting(): Promise<{}> { - return this.prompt(prompts) - .then((props: IInquirerScaffoldObject): void => { + return this.prompt(prompts).then( + (props: InquirerScaffoldObject): void => { this.props = props; - }); + } + ); } - public default() { + public default(): void { const currentDirName = path.basename(this.destinationPath()); if (currentDirName !== this.props.name) { this.log(` Your project must be inside a folder named ${this.props.name} I will create this folder for you. `); - mkdirp(this.props.name, (err: object) => { - console.error("Failed to create directory", err); - }); + mkdirp( + this.props.name, + (err: object): void => { + console.error("Failed to create directory", err); + } + ); const pathToProjectDir: string = this.destinationPath(this.props.name); this.destinationRoot(pathToProjectDir); } } - public writing() { + public writing(): void { this.copy = copyUtils.generatorCopy(this, templateDir); - this.copyTpl = copyUtils.generatorCopyTpl( - this, - templateDir, - templateFn(this), - ); + this.copyTpl = copyUtils.generatorCopyTpl(this, templateDir, templateFn(this)); copyFiles.forEach(this.copy); copyTemplateFiles.forEach(this.copyTpl); } - public install() { + public install(): void { this.npmInstall(["webpack-defaults", "bluebird"], { - "save-dev": true, + "save-dev": true }); } - public end() { + public end(): void { this.spawnCommand("npm", ["run", "defaults"]); } }; diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index 90207de3c61..c33e038a087 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -3,13 +3,9 @@ import * as logSymbols from "log-symbols"; import Generator = require("yeoman-generator"); import { getPackageManager } from "@webpack-cli/utils/package-manager"; -import { - Confirm, - Input, - List, -} from "@webpack-cli/webpack-scaffold"; +import { Confirm, Input, List } from "@webpack-cli/webpack-scaffold"; -import { IWebpackOptions } from "./types"; +import { WebpackOptions } from "./types"; import entryQuestions from "./utils/entry"; import getBabelPlugin from "./utils/module"; import getDefaultPlugins from "./utils/plugins"; @@ -30,32 +26,33 @@ export default class InitGenerator extends Generator { private dependencies: string[]; private configuration: { config: { - configName?: string, - topScope?: string[], - webpackOptions?: IWebpackOptions, - }, + configName?: string; + topScope?: string[]; + webpackOptions?: WebpackOptions; + }; }; - constructor(args, opts) { + public constructor(args, opts) { super(args, opts); this.isProd = false; - this.usingDefaults = false, - this.dependencies = [ - "webpack", - "webpack-cli", - "terser-webpack-plugin", - "babel-plugin-syntax-dynamic-import", - ]; + (this.usingDefaults = false), + (this.dependencies = [ + "webpack", + "webpack-cli", + "terser-webpack-plugin", + "babel-plugin-syntax-dynamic-import" + ]); this.configuration = { config: { topScope: [], - webpackOptions: {}, - }, + webpackOptions: {} + } }; } - public prompting() { - const done: (_?: void) => void | boolean = this.async(); + // eslint-disable-next-line + public prompting(): any { + const done: () => void | boolean = this.async(); const self: this = this; let regExpForStyles: string; let ExtractUseProps: object[]; @@ -65,389 +62,361 @@ export default class InitGenerator extends Generator { logSymbols.info + chalk.blue(" INFO ") + "For more information and a detailed description of each question, have a look at " + - chalk.bold.green( - "https://github.com/webpack/webpack-cli/blob/master/INIT.md", - ) + - "\n", + chalk.bold.green("https://github.com/webpack/webpack-cli/blob/master/INIT.md") + + "\n" ); process.stdout.write( logSymbols.info + chalk.blue(" INFO ") + "Alternatively, run `webpack(-cli) --help` for usage info." + - "\n\n", + "\n\n" ); this.configuration.config.webpackOptions.module = { - rules: [], + rules: [] }; this.configuration.config.topScope.push( "const webpack = require('webpack')", "const path = require('path')", - "\n", + "\n" ); - return this.prompt([ - Confirm("entryType", "Will your application have multiple bundles?", false), - ]) - .then((entryTypeAnswer: { - entryType: boolean; - }) => { - // Ask different questions for entry points - return entryQuestions(self, entryTypeAnswer); - }) - .then((entryOptions: object | string) => { - if (typeof entryOptions === "string" && entryOptions.length > 0) { + return this.prompt([Confirm("entryType", "Will your application have multiple bundles?", false)]) + .then( + (entryTypeAnswer: { entryType: boolean }): Promise<{}> => { + // Ask different questions for entry points + return entryQuestions(self, entryTypeAnswer); + } + ) + .then( + (entryOptions: object | string): Promise<{}> => { + if (typeof entryOptions === "string" && entryOptions.length > 0) { + return this.prompt([ + Input("outputType", "In which folder do you want to store your generated bundles? (dist):") + ]); + } + if (entryOptions !== '""') { + this.configuration.config.webpackOptions.entry = entryOptions; + } return this.prompt([ - Input( - "outputType", - "In which folder do you want to store your generated bundles? (dist):", - ), + Input("outputType", "In which folder do you want to store your generated bundles? (dist):") ]); } - if (entryOptions !== "\"\"") { - this.configuration.config.webpackOptions.entry = entryOptions; - } - return this.prompt([ - Input( - "outputType", - "In which folder do you want to store your generated bundles? (dist):", - ), - ]); - }) - .then((outputTypeAnswer: { - outputType: string; - }) => { - // As entry is not required anymore and we dont set it to be an empty string or """"" - // it can be undefined so falsy check is enough (vs entry.length); - if ( - !this.configuration.config.webpackOptions.entry && - !this.usingDefaults - ) { - this.configuration.config.webpackOptions.output = { - chunkFilename: "'[name].[chunkhash].js'", - filename: "'[name].[chunkhash].js'", - }; - } else if (!this.usingDefaults) { - this.configuration.config.webpackOptions.output = { - filename: "'[name].[chunkhash].js'", - }; + ) + .then( + (outputTypeAnswer: { outputType: string }): void => { + // As entry is not required anymore and we dont set it to be an empty string or """"" + // it can be undefined so falsy check is enough (vs entry.length); + if (!this.configuration.config.webpackOptions.entry && !this.usingDefaults) { + this.configuration.config.webpackOptions.output = { + chunkFilename: "'[name].[chunkhash].js'", + filename: "'[name].[chunkhash].js'" + }; + } else if (!this.usingDefaults) { + this.configuration.config.webpackOptions.output = { + filename: "'[name].[chunkhash].js'" + }; + } + if (!this.usingDefaults && outputTypeAnswer.outputType.length) { + this.configuration.config.webpackOptions.output.path = `path.resolve(__dirname, '${ + outputTypeAnswer.outputType + }')`; + } } - if (!this.usingDefaults && outputTypeAnswer.outputType.length) { - this.configuration.config.webpackOptions.output.path = - `path.resolve(__dirname, '${outputTypeAnswer.outputType}')`; + ) + .then( + (): void => { + this.isProd = this.usingDefaults ? true : false; + this.configuration.config.configName = this.isProd ? "prod" : "config"; + if (!this.isProd) { + this.configuration.config.webpackOptions.mode = "'development'"; + } + this.configuration.config.webpackOptions.plugins = this.isProd ? [] : getDefaultPlugins(); + return this.prompt([Confirm("babelConfirm", "Will you be using ES2015?")]); } - }) - .then((_: void) => { - this.isProd = this.usingDefaults ? true : false; - this.configuration.config.configName = this.isProd ? "prod" : "config"; - if (!this.isProd) { - this.configuration.config.webpackOptions.mode = "'development'"; + ) + .then( + (babelConfirmAnswer: { babelConfirm: boolean }): void => { + if (babelConfirmAnswer.babelConfirm) { + this.configuration.config.webpackOptions.module.rules.push(getBabelPlugin()); + this.dependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + } } - this.configuration.config.webpackOptions.plugins = this.isProd ? [] : getDefaultPlugins(); - return this.prompt([ - Confirm("babelConfirm", "Will you be using ES2015?"), - ]); - }) - .then((babelConfirmAnswer: { - babelConfirm: boolean; - }) => { - if (babelConfirmAnswer.babelConfirm) { - this.configuration.config.webpackOptions.module.rules.push( - getBabelPlugin(), - ); - this.dependencies.push( - "babel-loader", - "@babel/core", - "@babel/preset-env", - ); + ) + .then( + (): void => { + return this.prompt([ + List("stylingType", "Will you use one of the below CSS solutions?", [ + "No", + "CSS", + "SASS", + "LESS", + "PostCSS" + ]) + ]); } - }) - .then((_: void) => { - return this.prompt([ - List("stylingType", "Will you use one of the below CSS solutions?", [ - "No", - "CSS", - "SASS", - "LESS", - "PostCSS", - ]), - ]); - }) - .then((stylingTypeAnswer: { - stylingType: string; - }) => { - ExtractUseProps = []; - switch (stylingTypeAnswer.stylingType) { - case "SASS": - this.dependencies.push( - "sass-loader", - "node-sass", - "style-loader", - "css-loader", - ); - regExpForStyles = `${new RegExp(/\.(scss|css)$/)}`; - if (this.isProd) { - ExtractUseProps.push( - { - loader: "'css-loader'", - options: { - sourceMap: true, + ) + .then( + (stylingTypeAnswer: { stylingType: string }): void => { + ExtractUseProps = []; + switch (stylingTypeAnswer.stylingType) { + case "SASS": + this.dependencies.push("sass-loader", "node-sass", "style-loader", "css-loader"); + regExpForStyles = `${new RegExp(/\.(scss|css)$/)}`; + if (this.isProd) { + ExtractUseProps.push( + { + loader: "'css-loader'", + options: { + sourceMap: true + } }, - }, - { - loader: "'sass-loader'", - options: { - sourceMap: true, + { + loader: "'sass-loader'", + options: { + sourceMap: true + } + } + ); + } else { + ExtractUseProps.push( + { + loader: "'style-loader'" }, - }, - ); - } else { - ExtractUseProps.push( - { - loader: "'style-loader'", - }, - { - loader: "'css-loader'", - }, - { - loader: "'sass-loader'", - }, - ); - } - break; - case "LESS": - regExpForStyles = `${new RegExp(/\.(less|css)$/)}`; - this.dependencies.push( - "less-loader", - "less", - "style-loader", - "css-loader", - ); - if (this.isProd) { - ExtractUseProps.push( - { - loader: "'css-loader'", - options: { - sourceMap: true, + { + loader: "'css-loader'" }, - }, - { - loader: "'less-loader'", - options: { - sourceMap: true, + { + loader: "'sass-loader'" + } + ); + } + break; + case "LESS": + regExpForStyles = `${new RegExp(/\.(less|css)$/)}`; + this.dependencies.push("less-loader", "less", "style-loader", "css-loader"); + if (this.isProd) { + ExtractUseProps.push( + { + loader: "'css-loader'", + options: { + sourceMap: true + } }, - }, - ); - } else { - ExtractUseProps.push( - { - loader: "'css-loader'", - options: { - sourceMap: true, + { + loader: "'less-loader'", + options: { + sourceMap: true + } + } + ); + } else { + ExtractUseProps.push( + { + loader: "'css-loader'", + options: { + sourceMap: true + } }, - }, - { - loader: "'less-loader'", - options: { - sourceMap: true, - }, - }, + { + loader: "'less-loader'", + options: { + sourceMap: true + } + } + ); + } + break; + case "PostCSS": + this.configuration.config.topScope.push( + tooltip.postcss(), + "const autoprefixer = require('autoprefixer');", + "const precss = require('precss');", + "\n" ); - } - break; - case "PostCSS": - this.configuration.config.topScope.push( - tooltip.postcss(), - "const autoprefixer = require('autoprefixer');", - "const precss = require('precss');", - "\n", - ); - this.dependencies.push( - "style-loader", - "css-loader", - "postcss-loader", - "precss", - "autoprefixer", - ); - regExpForStyles = `${new RegExp(/\.css$/)}`; - if (this.isProd) { - ExtractUseProps.push( - { - loader: "'css-loader'", - options: { - importLoaders: 1, - sourceMap: true, + this.dependencies.push( + "style-loader", + "css-loader", + "postcss-loader", + "precss", + "autoprefixer" + ); + regExpForStyles = `${new RegExp(/\.css$/)}`; + if (this.isProd) { + ExtractUseProps.push( + { + loader: "'css-loader'", + options: { + importLoaders: 1, + sourceMap: true + } }, - }, - { - loader: "'postcss-loader'", - options: { - plugins: `function () { + { + loader: "'postcss-loader'", + options: { + plugins: `function () { return [ precss, autoprefixer ]; - }`, + }` + } + } + ); + } else { + ExtractUseProps.push( + { + loader: "'style-loader'" }, - }, - ); - } else { - ExtractUseProps.push( - { - loader: "'style-loader'", - }, - { - loader: "'css-loader'", - options: { - importLoaders: 1, - sourceMap: true, + { + loader: "'css-loader'", + options: { + importLoaders: 1, + sourceMap: true + } }, - }, - { - loader: "'postcss-loader'", - options: { - plugins: `function () { + { + loader: "'postcss-loader'", + options: { + plugins: `function () { return [ precss, autoprefixer ]; - }`, - }, - }, - ); - } - break; - case "CSS": - this.dependencies.push("style-loader", "css-loader"); - regExpForStyles = `${new RegExp(/\.css$/)}`; - if (this.isProd) { - ExtractUseProps.push({ - loader: "'css-loader'", - options: { - sourceMap: true, - }, - }); - } else { - ExtractUseProps.push( - { - loader: "'style-loader'", + }` + } + } + ); + } + break; + case "CSS": + this.dependencies.push("style-loader", "css-loader"); + regExpForStyles = `${new RegExp(/\.css$/)}`; + if (this.isProd) { + ExtractUseProps.push({ + loader: "'css-loader'", options: { - sourceMap: true, + sourceMap: true + } + }); + } else { + ExtractUseProps.push( + { + loader: "'style-loader'", + options: { + sourceMap: true + } }, - }, - { - loader: "'css-loader'", - }, - ); - } - break; - default: - regExpForStyles = null; - } - }) - .then((_: void) => { - if (this.isProd) { - // Ask if the user wants to use extractPlugin - return this.prompt([ - Input( - "extractPlugin", - "If you want to bundle your CSS files, what will you name the bundle? (press enter to skip)", - ), - ]); + { + loader: "'css-loader'" + } + ); + } + break; + default: + regExpForStyles = null; + } } - }) - .then((extractPluginAnswer: { - extractPlugin: string; - }) => { - if (regExpForStyles) { + ) + .then( + (): void => { if (this.isProd) { - const cssBundleName: string = extractPluginAnswer.extractPlugin; - this.configuration.config.topScope.push(tooltip.cssPlugin()); - this.dependencies.push("mini-css-extract-plugin"); + // Ask if the user wants to use extractPlugin + return this.prompt([ + Input( + "extractPlugin", + "If you want to bundle your CSS files, what will you name the bundle? (press enter to skip)" + ) + ]); + } + } + ) + .then( + (extractPluginAnswer: { extractPlugin: string }): void => { + if (regExpForStyles) { + if (this.isProd) { + const cssBundleName: string = extractPluginAnswer.extractPlugin; + this.configuration.config.topScope.push(tooltip.cssPlugin()); + this.dependencies.push("mini-css-extract-plugin"); - if (cssBundleName.length !== 0) { - this.configuration.config.webpackOptions.plugins.push( - // TODO: use [contenthash] after it is supported - `new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })`, - ); - } else { - this.configuration.config.webpackOptions.plugins.push( - "new MiniCssExtractPlugin({ filename:'style.css' })", - ); - } + if (cssBundleName.length !== 0) { + (this.configuration.config.webpackOptions.plugins as string[]).push( + // TODO: use [contenthash] after it is supported + `new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })` + ); + } else { + (this.configuration.config.webpackOptions.plugins as string[]).push( + "new MiniCssExtractPlugin({ filename:'style.css' })" + ); + } - ExtractUseProps.unshift({ - loader: "MiniCssExtractPlugin.loader", - }); + ExtractUseProps.unshift({ + loader: "MiniCssExtractPlugin.loader" + }); - const moduleRulesObj = { - test: regExpForStyles, - use: ExtractUseProps, - }; + const moduleRulesObj = { + test: regExpForStyles, + use: ExtractUseProps + }; - this.configuration.config.webpackOptions.module.rules.push( - moduleRulesObj, - ); - this.configuration.config.topScope.push( - "const MiniCssExtractPlugin = require('mini-css-extract-plugin');", - "\n", - ); - } else { - const moduleRulesObj: { - test: string; - use: object[]; - } = { - test: regExpForStyles, - use: ExtractUseProps, - }; + this.configuration.config.webpackOptions.module.rules.push(moduleRulesObj); + this.configuration.config.topScope.push( + "const MiniCssExtractPlugin = require('mini-css-extract-plugin');", + "\n" + ); + } else { + const moduleRulesObj: { + test: string; + use: object[]; + } = { + test: regExpForStyles, + use: ExtractUseProps + }; - this.configuration.config.webpackOptions.module.rules.push( - moduleRulesObj, - ); + this.configuration.config.webpackOptions.module.rules.push(moduleRulesObj); + } } - } - // add splitChunks options for transparency - // defaults coming from: https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks - this.configuration.config.topScope.push(tooltip.splitChunks()); - this.configuration.config.webpackOptions.optimization = { - splitChunks: { - cacheGroups: { - vendors: { - priority: -10, - test: "/[\\\\/]node_modules[\\\\/]/", + // add splitChunks options for transparency + // defaults coming from: https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks + this.configuration.config.topScope.push(tooltip.splitChunks()); + this.configuration.config.webpackOptions.optimization = { + splitChunks: { + cacheGroups: { + vendors: { + priority: -10, + test: "/[\\\\/]node_modules[\\\\/]/" + } }, - }, - chunks: "'async'", - minChunks: 1, - minSize: 30000, - // for production name is recommended to be off - name: !this.isProd, - }, - }; - done(); - }); + chunks: "'async'", + minChunks: 1, + minSize: 30000, + // for production name is recommended to be off + name: !this.isProd + } + }; + done(); + } + ); } - public installPlugins() { + public installPlugins(): void { if (this.isProd) { - this.dependencies = this.dependencies.filter( - (p: string): boolean => p !== "terser-webpack-plugin", - ); + this.dependencies = this.dependencies.filter((p: string): boolean => p !== "terser-webpack-plugin"); } else { this.configuration.config.topScope.push( tooltip.terser(), "const TerserPlugin = require('terser-webpack-plugin');", - "\n", + "\n" ); } const packager = getPackageManager(); const opts: { - dev?: boolean, - "save-dev"?: boolean, + dev?: boolean; + "save-dev"?: boolean; } = packager === "yarn" ? { dev: true } : { "save-dev": true }; this.scheduleInstallTask(packager, this.dependencies, opts); } - public writing() { + public writing(): void { this.config.set("configuration", this.configuration); } } diff --git a/packages/generators/remove-generator.ts b/packages/generators/remove-generator.ts index d81bdeeaf94..a1ab387b847 100644 --- a/packages/generators/remove-generator.ts +++ b/packages/generators/remove-generator.ts @@ -4,7 +4,7 @@ import Generator = require("yeoman-generator"); import PROP_TYPES from "@webpack-cli/utils/prop-types"; import { List } from "@webpack-cli/webpack-scaffold"; -import { IWebpackOptions } from "./types"; +import { WebpackOptions } from "./types"; /** * @@ -18,19 +18,19 @@ import { IWebpackOptions } from "./types"; export default class RemoveGenerator extends Generator { private configuration: { config: { - configName?: string, - topScope?: string[], - webpackOptions?: IWebpackOptions, - }, + configName?: string; + topScope?: string[]; + webpackOptions?: WebpackOptions; + }; }; - private webpackOptions: IWebpackOptions | string; + private webpackOptions: WebpackOptions | string; - constructor(args, opts) { + public constructor(args, opts) { super(args, opts); this.configuration = { config: { - webpackOptions: {}, - }, + webpackOptions: {} + } }; let configPath = path.resolve(process.cwd(), "webpack.config.js"); @@ -49,90 +49,96 @@ export default class RemoveGenerator extends Generator { public getModuleLoadersNames(): string[] { if (typeof this.webpackOptions === "object") { if (this.webpackOptions.module && this.webpackOptions.module.rules) { - return this.webpackOptions.module.rules.map((rule: { - loader: string; - }) => rule ? rule.loader : null); + return this.webpackOptions.module.rules.map( + (rule: { loader: string }): string | null => (rule ? rule.loader : null) + ); } } } - public prompting() { - const done: (_?: void) => void | boolean = this.async(); + public prompting(): Promise<{}> { + const done: () => void | boolean = this.async(); let propValue: object | string | boolean; - return this.prompt([ - List( - "propType", - "Which property do you want to remove?", - Array.from(this.getPropTypes()), - ), - ]) - .then(({ propType }: { propType: string }): Promise<{}> => { - if (!PROP_TYPES.has(propType)) { - console.error("Invalid webpack config prop"); - return; - } + return this.prompt([List("propType", "Which property do you want to remove?", Array.from(this.getPropTypes()))]) + .then( + ({ propType }: { propType: string }): Promise<{}> => { + if (!PROP_TYPES.has(propType)) { + console.error("Invalid webpack config prop"); + return; + } - propValue = this.webpackOptions[propType]; - if (typeof propValue === "object") { - if (Array.isArray(propValue)) { - return this.prompt([ - List( - "keyType", - `Which key do you want to remove from ${propType}?`, - Array.from(propValue), - ), - ]).then(({ keyType }: { keyType: string }): void => { - this.configuration.config.webpackOptions[propType] = [ keyType ]; - }); - } else { - return this.prompt([ - List( - "keyType", - `Which key do you want to remove from ${propType}?`, - Array.from(Object.keys(propValue)), - ), - ]) - .then(({ keyType }: { keyType: string }): Promise<{}> => { - if (propType === "module" && keyType === "rules") { - return this.prompt([ - List( - "rule", - "Which loader do you want to remove?", - Array.from(this.getModuleLoadersNames()), - ), - ]) - .then(({ rule }: { rule: string }): void => { - if (typeof this.webpackOptions === "object") { - const loaderIndex: number = this.getModuleLoadersNames().indexOf(rule); - const loader: object = this.webpackOptions.module.rules[loaderIndex]; - this.configuration.config.webpackOptions.module = { - rules: [ loader ], - }; + propValue = this.webpackOptions[propType]; + if (typeof propValue === "object") { + if (Array.isArray(propValue)) { + return this.prompt([ + List( + "keyType", + `Which key do you want to remove from ${propType}?`, + Array.from(propValue) + ) + ]).then( + ({ keyType }: { keyType: string }): void => { + this.configuration.config.webpackOptions[propType] = [keyType]; + } + ); + } else { + return this.prompt([ + List( + "keyType", + `Which key do you want to remove from ${propType}?`, + Array.from(Object.keys(propValue)) + ) + ]).then( + ({ keyType }: { keyType: string }): Promise<{}> => { + if (propType === "module" && keyType === "rules") { + return this.prompt([ + List( + "rule", + "Which loader do you want to remove?", + Array.from(this.getModuleLoadersNames()) + ) + ]).then( + ({ rule }: { rule: string }): void => { + if (typeof this.webpackOptions === "object") { + const loaderIndex: number = this.getModuleLoadersNames().indexOf( + rule + ); + const loader: object = this.webpackOptions.module.rules[ + loaderIndex + ]; + this.configuration.config.webpackOptions.module = { + rules: [loader] + }; + } } - }); - } else { - // remove the complete prop object if there is only one key - if (Object.keys(this.webpackOptions[propType]).length <= 1) { - this.configuration.config.webpackOptions[propType] = null; + ); } else { - this.configuration.config.webpackOptions[propType] = { - [keyType]: null, - }; + // remove the complete prop object if there is only one key + if (Object.keys(this.webpackOptions[propType]).length <= 1) { + this.configuration.config.webpackOptions[propType] = null; + } else { + this.configuration.config.webpackOptions[propType] = { + [keyType]: null + }; + } } } - }); + ); + } + } else { + this.configuration.config.webpackOptions[propType] = null; } - } else { - this.configuration.config.webpackOptions[propType] = null; } - }) - .then((_: void): void => { - done(); - }); + ) + .then( + (): void => { + done(); + } + ); } - public writing() { + public writing(): void { this.config.set("configuration", this.configuration); } } diff --git a/packages/generators/tslint.json b/packages/generators/tslint.json deleted file mode 100644 index 0946f20963a..00000000000 --- a/packages/generators/tslint.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../tslint.json" -} diff --git a/packages/generators/types/index.ts b/packages/generators/types/index.ts index 1adc7ee911c..449dc0cc477 100644 --- a/packages/generators/types/index.ts +++ b/packages/generators/types/index.ts @@ -1,11 +1,11 @@ -export interface ISchemaProperties { +export interface SchemaProperties { additionalProperties?: boolean; definitions?: object; properties?: object; type?: string; } -interface IWebpackResolve { +interface WebpackResolve { alias?: object; aliasFields?: string[]; cachePredicate?: Function; @@ -27,7 +27,7 @@ interface IWebpackResolve { type IRuleSetCondition = RegExp | string | Function | object; -export interface IWebpackOptions { +export interface WebpackOptions { amd?: string; bail?: boolean; cache?: boolean | object; @@ -47,10 +47,12 @@ export interface IWebpackOptions { headers?: object; logLevel?: string; clientLogLevel?: string; - overlay?: boolean | { - errors?: boolean; - warnings?: boolean; - }; + overlay?: + | boolean + | { + errors?: boolean; + warnings?: boolean; + }; progress?: boolean; key?: string | Buffer; cert?: string | Buffer; @@ -69,10 +71,12 @@ export interface IWebpackOptions { openPage?: string; compress?: boolean; proxy?: object[] | Function[]; - historyApiFallback?: boolean | { - rewrites?: object[]; - disableDotRule?: boolean; - }; + historyApiFallback?: + | boolean + | { + rewrites?: object[]; + disableDotRule?: boolean; + }; staticOptions?: object; setup?: Function; before?: Function; @@ -97,25 +101,7 @@ export interface IWebpackOptions { exprContextRegExp?: boolean | RegExp; exprContextRequest?: string; noParse?: string | string[] | Function | RegExp | RegExp[]; - rules?: Array<{ - enforce?: "pre" | "post"; - exclude?: IRuleSetCondition; - include?: IRuleSetCondition; - issuer?: IRuleSetCondition; - loader?: string | Function | object; - loaders?: Function[] | object[]; - options?: object; - parser?: object; - sideEffects?: boolean; - type?: string; - resource?: IRuleSetCondition; - resourceQuery?: IRuleSetCondition; - compiler?: IRuleSetCondition; - rules?: object[]; - use?: object | object[] | Function; - test?: IRuleSetCondition; - - }>; + rules?: Rule[]; unknownContextCritical?: boolean; unknownContextRecursive?: boolean; unknownContextRegExp?: boolean | RegExp; @@ -127,15 +113,19 @@ export interface IWebpackOptions { strictExportPresence?: boolean; strictThisContextOnImports?: boolean; }; - node?: false | true | string | { - console?: boolean | string; - process?: boolean | string; - global?: boolean; - __filename?: boolean | string; - __dirname?: boolean | string; - Buffer?: boolean | string; - setImmediate?: boolean | string; - }; + node?: + | false + | true + | string + | { + console?: boolean | string; + process?: boolean | string; + global?: boolean; + __filename?: boolean | string; + __dirname?: boolean | string; + Buffer?: boolean | string; + setImmediate?: boolean | string; + }; output?: { auxiliaryComment?: string | object; chunkFilename?: string; @@ -199,19 +189,21 @@ export interface IWebpackOptions { }; }; parallelism?: number; - performance?: false | { - assetFilter?: Function; - hints?: false | string; - maxEntrypointSize?: number; - maxAssetSize?: number; - }; - plugins?: object[] | Function[] | string[] | string | any; + performance?: + | false + | { + assetFilter?: Function; + hints?: false | string; + maxEntrypointSize?: number; + maxAssetSize?: number; + }; + plugins?: object[] | Function[] | string[] | string; profile?: boolean; recordsInputPath?: string; recordsOutputPath?: string; recordsPath?: string; - resolve?: IWebpackResolve; - resolveLoader?: IWebpackResolve; + resolve?: WebpackResolve; + resolveLoader?: WebpackResolve; stats?: string | boolean | object; target?: string | Function; watch?: boolean; @@ -221,3 +213,22 @@ export interface IWebpackOptions { poll?: boolean | number; }; } + +interface Rule { + enforce?: "pre" | "post"; + exclude?: IRuleSetCondition; + include?: IRuleSetCondition; + issuer?: IRuleSetCondition; + loader?: string | Function | object; + loaders?: Function[] | object[]; + options?: object; + parser?: object; + sideEffects?: boolean; + type?: string; + resource?: IRuleSetCondition; + resourceQuery?: IRuleSetCondition; + compiler?: IRuleSetCondition; + rules?: object[]; + use?: object | object[] | Function; + test?: IRuleSetCondition; +} diff --git a/packages/generators/types/json-loader.d.ts b/packages/generators/types/json-loader.d.ts index 0cb4cf5f44b..36396113911 100644 --- a/packages/generators/types/json-loader.d.ts +++ b/packages/generators/types/json-loader.d.ts @@ -1,4 +1,5 @@ declare module "*.json" { + // eslint-disable-next-line const value: any; export default value; } diff --git a/packages/generators/types/yeoman-generator.d.ts b/packages/generators/types/yeoman-generator.d.ts index bc2425c458f..3ca7dd82c1a 100644 --- a/packages/generators/types/yeoman-generator.d.ts +++ b/packages/generators/types/yeoman-generator.d.ts @@ -4,7 +4,7 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped /// -interface IYeoman { +interface Yeoman { public config: { set: (setProperty: string, setValue: object) => void; }; @@ -12,8 +12,8 @@ interface IYeoman { adapter: { promptModule: { registerPrompt: (promptName: string, promptModule: object) => void; - }, - }, + }; + }; }; public props: { name: string; @@ -24,16 +24,15 @@ interface IYeoman { public run(target: string, options?: object, done?: Function): IRunEnv; public scheduleInstallTask(packager: string, dependencies: string[], options?: object): void; public on(event: string, listener: Function): this; - public async(): (_?: void) => void | boolean; + public async(): () => void | boolean; public prompt(opt: IPromptOptions[]): Promise<>; public log(message: string): void; public npmInstall(packages?: string[] | string, options?: object, cb?: Function): Promise<>; - public spawnCommand(name: string, args?: string[], options?: Object): void; + public spawnCommand(name: string, args?: string[], options?: object): void; } declare module "yeoman-generator" { - - class YeomanGeneratorBase extends IYeoman { + class YeomanGeneratorBase implements Yeoman { public config: { set: (setProperty: string, setValue: object) => void; }; @@ -41,8 +40,8 @@ declare module "yeoman-generator" { adapter: { promptModule: { registerPrompt: (promptName: string, promptModule: object) => void; - }, - }, + }; + }; }; public props: { name: string; @@ -50,39 +49,42 @@ declare module "yeoman-generator" { public composeWith(namespace: string, options?: object, settings?: IComposeSetting): YeomanGeneratorBase; public destinationRoot(rootPath?: string): string; public destinationPath(...path: string[]): string; - public run(target: string, options?: object, done?: Function): IRunEnv; + public run(target: string, options?: object, done?: Function): RunEnv; public scheduleInstallTask(packager: string, dependencies: string[], options?: object): void; public on(event: string, listener: Function): this; - public async(): (_?: void) => void | boolean; - public prompt(opt: IPromptOptions[]): Promise<>; + public async(): () => void | boolean; + public prompt(opt: PromptOptions[]): Promise<>; public log(message: string): void; public npmInstall(packages?: string[] | string, options?: object, cb?: Function): Promise<>; - public spawnCommand(name: string, args?: string[], options?: Object): void; + public spawnCommand(name: string, args?: string[], options?: object): void; } - interface IRunEnv extends Object { + interface RunEnv extends object { on: (event: string, callbackFn: Function) => void; } - interface IPromptOptions { + interface PromptOptions { type?: string; name: string; - message: string | ((answers: Object) => string); - choices?: string[] | ((answers: Object) => string); - default?: string | number | boolean | string[] | number[] - | ((answers: Object) => (string | number | boolean | string[] | number[])); - validate?: ((input: string) => boolean | string); - when?: ((answers: Object) => boolean) | boolean; + message: string | ((answers: object) => string); + choices?: string[] | ((answers: object) => string); + default?: + | string + | number + | boolean + | string[] + | number[] + | ((answers: object) => string | number | boolean | string[] | number[]); + validate?: (input: string) => boolean | string; + when?: ((answers: object) => boolean) | boolean; store?: boolean; filter?: (name: string) => string; } - // tslint:disable-next-line class NamedBase extends YeomanGeneratorBase implements INamedBase { - constructor(args: string | string[], options: object); + public constructor(args: string | string[], options: object); } - // tslint:disable-next-line class Base extends NamedBase implements IBase { public static extend(protoProps: IQueueProps): YeomanGeneratorBase; } diff --git a/packages/generators/utils/entry.ts b/packages/generators/utils/entry.ts index aae92f38be1..f65af71cde8 100644 --- a/packages/generators/utils/entry.ts +++ b/packages/generators/utils/entry.ts @@ -2,7 +2,7 @@ import { InputValidate } from "@webpack-cli/webpack-scaffold"; import validate from "./validate"; -interface IEntry extends IYeoman { +interface Entry extends Yeoman { usingDefaults?: boolean; } @@ -15,9 +15,12 @@ interface IEntry extends IYeoman { * @returns {Object} An Object that holds the answers given by the user, later used to scaffold */ -export default function entry(self: IEntry, answer: { - entryType: boolean; -}): Promise<{}> { +export default function entry( + self: Entry, + answer: { + entryType: boolean; + } +): Promise<{}> { let entryIdentifiers: string[]; let result: Promise<{}>; if (answer.entryType) { @@ -26,82 +29,90 @@ export default function entry(self: IEntry, answer: { InputValidate( "multipleEntries", "Type the names you want for your modules (entry files), separated by comma [example: app,vendor]", - validate, - ), + validate + ) ]) - .then((multipleEntriesAnswer: { - multipleEntries: string, - }) => { - const webpackEntryPoint: object = {}; - entryIdentifiers = multipleEntriesAnswer.multipleEntries.split(","); + .then( + (multipleEntriesAnswer: { multipleEntries: string }): Promise => { + const webpackEntryPoint: object = {}; + entryIdentifiers = multipleEntriesAnswer.multipleEntries.split(","); - function forEachPromise(entries: string[], fn: (entryProp: string) => Promise<{} | void>) { - return entries.reduce((promise: Promise<{}>, prop: string) => { - const trimmedProp: string = prop.trim(); + function forEachPromise( + entries: string[], + fn: (entryProp: string) => Promise<{} | void> + ): Promise { + return entries.reduce((promise: Promise<{}>, prop: string): Promise => { + const trimmedProp: string = prop.trim(); - return promise.then((n: object) => { - if (n) { - Object.keys(n).forEach((val: string): void => { + return promise.then( + (n: object): Promise => { + if (n) { + Object.keys(n).forEach( + (val: string): void => { + if ( + n[val].charAt(0) !== "(" && + n[val].charAt(0) !== "[" && + !n[val].includes("function") && + !n[val].includes("path") && + !n[val].includes("process") + ) { + n[val] = `\'${n[val].replace(/"|'/g, "").concat(".js")}\'`; + } + webpackEntryPoint[val] = n[val]; + } + ); + } else { + n = {}; + } + return fn(trimmedProp); + } + ); + }, Promise.resolve()); + } + return forEachPromise( + entryIdentifiers, + (entryProp: string): Promise<{} | void> => + self.prompt([ + InputValidate( + `${entryProp}`, + `What is the location of "${entryProp}"? [example: ./src/${entryProp}]`, + validate + ) + ]) + ).then( + (entryPropAnswer: object): object => { + Object.keys(entryPropAnswer).forEach( + (val: string): void => { if ( - n[val].charAt(0) !== "(" && - n[val].charAt(0) !== "[" && - !n[val].includes("function") && - !n[val].includes("path") && - !n[val].includes("process") + entryPropAnswer[val].charAt(0) !== "(" && + entryPropAnswer[val].charAt(0) !== "[" && + !entryPropAnswer[val].includes("function") && + !entryPropAnswer[val].includes("path") && + !entryPropAnswer[val].includes("process") ) { - n[val] = `\'${n[val].replace(/"|'/g, "").concat(".js")}\'`; + entryPropAnswer[val] = `\'${entryPropAnswer[val].replace(/"|'/g, "")}\'`; } - webpackEntryPoint[val] = n[val]; - }); - } else { - n = {}; - } - return fn(trimmedProp); - }); - }, Promise.resolve()); - } - return forEachPromise(entryIdentifiers, (entryProp: string): Promise<{} | void> => - self.prompt([ - InputValidate( - `${entryProp}`, - `What is the location of "${entryProp}"? [example: ./src/${entryProp}]`, - validate, - ), - ]), - ).then((entryPropAnswer: object): object => { - Object.keys(entryPropAnswer).forEach((val: string): void => { - if ( - entryPropAnswer[val].charAt(0) !== "(" && - entryPropAnswer[val].charAt(0) !== "[" && - !entryPropAnswer[val].includes("function") && - !entryPropAnswer[val].includes("path") && - !entryPropAnswer[val].includes("process") - ) { - entryPropAnswer[val] = `\'${entryPropAnswer[val].replace(/"|'/g, "")}\'`; + webpackEntryPoint[val] = entryPropAnswer[val]; + } + ); + return webpackEntryPoint; } - webpackEntryPoint[val] = entryPropAnswer[val]; - }); - return webpackEntryPoint; - }); - }); + ); + } + ); } else { result = self - .prompt([ - InputValidate( - "singularEntry", - "Which will be your application entry point? (src/index)", - ), - ]) - .then((singularEntryAnswer: { - singularEntry: string, - }): string => { - let { singularEntry } = singularEntryAnswer; - singularEntry = `\'${singularEntry.replace(/"|'/g, "")}\'`; - if (singularEntry.length <= 0) { - self.usingDefaults = true; + .prompt([InputValidate("singularEntry", "Which will be your application entry point? (src/index)")]) + .then( + (singularEntryAnswer: { singularEntry: string }): string => { + let { singularEntry } = singularEntryAnswer; + singularEntry = `\'${singularEntry.replace(/"|'/g, "")}\'`; + if (singularEntry.length <= 0) { + self.usingDefaults = true; + } + return singularEntry; } - return singularEntry; - }); + ); } return result; } diff --git a/packages/generators/utils/module.ts b/packages/generators/utils/module.ts index 50c4b0deeea..25b846723f1 100644 --- a/packages/generators/utils/module.ts +++ b/packages/generators/utils/module.ts @@ -1,36 +1,36 @@ -interface IModule extends Object { +interface Module extends Object { include: string[]; loader: string; options: { plugins: string[]; - presets: Array>; + presets: Preset[][]; }; test: string; } +type Preset = string | object; + /** * * Returns an module.rule object that has the babel loader if invoked * * @returns {Function} A callable function that adds the babel-loader with env preset */ -export default function(): IModule { +export default function(): Module { return { include: ["path.resolve(__dirname, 'src')"], loader: "'babel-loader'", options: { - plugins: [ - "'syntax-dynamic-import'", - ], + plugins: ["'syntax-dynamic-import'"], presets: [ [ "'@babel/preset-env'", { - "'modules'": false, - }, - ], - ], + "'modules'": false + } + ] + ] }, - test: `${new RegExp(/\.js$/)}`, + test: `${new RegExp(/\.js$/)}` }; } diff --git a/packages/generators/utils/plugins.ts b/packages/generators/utils/plugins.ts index f94029dea51..6dedb1fab9e 100644 --- a/packages/generators/utils/plugins.ts +++ b/packages/generators/utils/plugins.ts @@ -6,6 +6,6 @@ * that consists of terser-webpack-plugin */ -export default function(_?: void): string[] { +export default function(): string[] { return ["new TerserPlugin()"]; } diff --git a/packages/generators/utils/tooltip.ts b/packages/generators/utils/tooltip.ts index 27580eee7ad..cd9dba4e7ec 100644 --- a/packages/generators/utils/tooltip.ts +++ b/packages/generators/utils/tooltip.ts @@ -7,7 +7,7 @@ */ export default { - cssPlugin: (_?: void): string => { + cssPlugin: (): string => { return `/* * We've enabled MiniCssExtractPlugin for you. This allows your app to * use css modules that will be moved into a separate CSS file instead of inside @@ -18,7 +18,7 @@ export default { */`; }, - splitChunks: (_?: void): string => { + splitChunks: (): string => { return `/* * SplitChunksPlugin is enabled by default and replaced * deprecated CommonsChunkPlugin. It automatically identifies modules which @@ -33,7 +33,7 @@ export default { */`; }, - postcss: (_?: void): string => { + postcss: (): string => { return `/* * We've enabled Postcss, autoprefixer and precss for you. This allows your app * to lint CSS, support variables and mixins, transpile future CSS syntax, @@ -50,7 +50,7 @@ export default { */`; }, - terser: (_?: void): string => { + terser: (): string => { return `/* * We've enabled TerserPlugin for you! This minifies your app * in order to load faster and run less javascript. @@ -58,5 +58,5 @@ export default { * https://github.com/webpack-contrib/terser-webpack-plugin * */`; - }, + } }; diff --git a/packages/info/.eslintrc b/packages/info/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/info/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/info/__tests__/.eslintrc b/packages/info/__tests__/.eslintrc new file mode 100644 index 00000000000..5d4340a351d --- /dev/null +++ b/packages/info/__tests__/.eslintrc @@ -0,0 +1,7 @@ +{ + "root": true, + "extends": ["../.eslintrc"], + "rules": { + "@typescript-eslint/explicit-function-return-type": ["off"] + } +} diff --git a/packages/info/__tests__/index.test.ts b/packages/info/__tests__/index.test.ts index fca23773708..7ddb1dae791 100644 --- a/packages/info/__tests__/index.test.ts +++ b/packages/info/__tests__/index.test.ts @@ -8,7 +8,7 @@ describe("info", () => { Browsers: ["Chrome", "Firefox", "Safari"], System: ["OS", "CPU"], npmGlobalPackages: ["webpack", "webpack-cli"], - npmPackages: "*webpack*", + npmPackages: "*webpack*" }; expect(returnedInformation).toEqual(expectedInformation); diff --git a/packages/info/index.ts b/packages/info/index.ts index 43db20f6234..68c69c9ddf8 100644 --- a/packages/info/index.ts +++ b/packages/info/index.ts @@ -5,16 +5,18 @@ import * as process from "process"; * Prints debugging information for webpack issue reporting */ -export function information() { +// TODO: define proper interface +// eslint-disable-next-line +export function information(): any { return { Binaries: ["Node", "Yarn", "npm"], Browsers: ["Chrome", "Firefox", "Safari"], System: ["OS", "CPU"], npmGlobalPackages: ["webpack", "webpack-cli"], - npmPackages: "*webpack*", + npmPackages: "*webpack*" }; } -export default async function info() { +export default async function info(): Promise { process.stdout.write(await envinfo.run(information())); } diff --git a/packages/init/.eslintrc b/packages/init/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/init/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/init/init.ts b/packages/init/init.ts index 1b60fe34c7e..b4063cd15e9 100644 --- a/packages/init/init.ts +++ b/packages/init/init.ts @@ -7,9 +7,9 @@ import propTypes from "@webpack-cli/utils/prop-types"; import astTransform from "@webpack-cli/utils/recursive-parser"; import runPrettier from "@webpack-cli/utils/run-prettier"; -import { INode } from "@webpack-cli/utils/types/NodePath"; -import { IError } from "./types"; -import { IConfiguration, IWebpackProperties } from "./types/Transform"; +import { Node } from "@webpack-cli/utils/types/NodePath"; +import { Error } from "./types"; +import { Configuration, WebpackProperties } from "./types/Transform"; /** * @@ -21,9 +21,8 @@ import { IConfiguration, IWebpackProperties } from "./types/Transform"; * @returns {Array} - An array with the transformations to be run */ -const mapOptionsToTransform = (config: IConfiguration): string[] => - Object.keys(config.webpackOptions) - .filter((key: string): boolean => propTypes.has(key)); +const mapOptionsToTransform = (config: Configuration): string[] => + Object.keys(config.webpackOptions).filter((key: string): boolean => propTypes.has(key)); /** * @@ -35,53 +34,56 @@ const mapOptionsToTransform = (config: IConfiguration): string[] => * and writes the file */ -export default function runTransform(webpackProperties: IWebpackProperties, action: string): void { +export default function runTransform(webpackProperties: WebpackProperties, action: string): void { // webpackOptions.name sent to nameTransform if match - const webpackConfig: string[] = - Object - .keys(webpackProperties) - .filter((p: string): boolean => p !== "configFile" && p !== "configPath"); + const webpackConfig: string[] = Object.keys(webpackProperties).filter( + (p: string): boolean => p !== "configFile" && p !== "configPath" + ); const initActionNotDefined = (action && action !== "init") || false; - webpackConfig.forEach((scaffoldPiece: string): Promise => { - const config: IConfiguration = webpackProperties[scaffoldPiece]; - const transformations = mapOptionsToTransform(config); - const ast = j( - initActionNotDefined - ? webpackProperties.configFile - : "module.exports = {}", - ); - const transformAction: string | null = action || null; + webpackConfig.forEach( + (scaffoldPiece: string): Promise => { + const config: Configuration = webpackProperties[scaffoldPiece]; + const transformations = mapOptionsToTransform(config); + const ast = j(initActionNotDefined ? webpackProperties.configFile : "module.exports = {}"); + const transformAction: string | null = action || null; - return pEachSeries(transformations, (f: string): boolean | INode => { - return astTransform(j, ast, config.webpackOptions[f], transformAction, f); - }) - .then((value: string[]): void | PromiseLike => { - let configurationName = "webpack.config.js"; - if (config.configName) { - configurationName = `webpack.${config.configName}.js`; + return pEachSeries( + transformations, + (f: string): boolean | Node => { + return astTransform(j, ast, config.webpackOptions[f], transformAction, f); } + ) + .then( + (): void | PromiseLike => { + let configurationName = "webpack.config.js"; + if (config.configName) { + configurationName = `webpack.${config.configName}.js`; + } - const outputPath = initActionNotDefined - ? webpackProperties.configPath - : path.join(process.cwd(), configurationName); + const outputPath = initActionNotDefined + ? webpackProperties.configPath + : path.join(process.cwd(), configurationName); - const source: string = ast.toSource({ - quote: "single", - }); + const source: string = ast.toSource({ + quote: "single" + }); - runPrettier(outputPath, source); - }) - .catch((err: IError) => { - console.error(err.message ? err.message : err); - }); - }); + runPrettier(outputPath, source); + } + ) + .catch( + (err: Error): void => { + console.error(err.message ? err.message : err); + } + ); + } + ); let successMessage: string = `Congratulations! Your new webpack configuration file has been created!`; if (initActionNotDefined && webpackProperties.config.item) { successMessage = `Congratulations! ${webpackProperties.config.item} has been ${action}ed!`; - } process.stdout.write("\n" + chalk.green(`${successMessage}\n`)); } diff --git a/packages/init/package-lock.json b/packages/init/package-lock.json index fede4727f12..1695fbbf370 100644 --- a/packages/init/package-lock.json +++ b/packages/init/package-lock.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/init", - "version": "0.1.3", + "version": "0.1.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/init/types/Transform.ts b/packages/init/types/Transform.ts index 89622b85f62..d117d93d558 100644 --- a/packages/init/types/Transform.ts +++ b/packages/init/types/Transform.ts @@ -1,14 +1,14 @@ -export interface IWebpackProperties extends Object { +export interface WebpackProperties extends Object { configFile: string; configPath: string; - webpackOptions: IConfiguration; + webpackOptions: Configuration; config: { item: string; configName: string; }; } -export interface IConfiguration extends Object { +export interface Configuration extends Object { configName: string; webpackOptions: object; topScope: string[]; diff --git a/packages/init/types/index.ts b/packages/init/types/index.ts index c0f13602567..9480d6ce9b0 100644 --- a/packages/init/types/index.ts +++ b/packages/init/types/index.ts @@ -1,3 +1,3 @@ -export interface IError { +export interface Error { message?: string; } diff --git a/packages/make/.eslintrc b/packages/make/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/make/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/migrate/.eslintrc b/packages/migrate/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/migrate/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/migrate/__tests__/.eslintrc b/packages/migrate/__tests__/.eslintrc new file mode 100644 index 00000000000..5d4340a351d --- /dev/null +++ b/packages/migrate/__tests__/.eslintrc @@ -0,0 +1,7 @@ +{ + "root": true, + "extends": ["../.eslintrc"], + "rules": { + "@typescript-eslint/explicit-function-return-type": ["off"] + } +} diff --git a/packages/migrate/bannerPlugin/bannerPlugin.ts b/packages/migrate/bannerPlugin/bannerPlugin.ts index 5d369f57ec9..422e1d40dd1 100644 --- a/packages/migrate/bannerPlugin/bannerPlugin.ts +++ b/packages/migrate/bannerPlugin/bannerPlugin.ts @@ -1,6 +1,6 @@ import * as utils from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; /** * @@ -12,24 +12,19 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @returns {Node} ast - jscodeshift ast */ -export default function(j: IJSCodeshift, ast: INode): INode { - return utils - .findPluginsByName(j, ast, ["webpack.BannerPlugin"]) - .forEach((path: INode): void => { - const args: INode[] = path.value.arguments; // any node +export default function(j: JSCodeshift, ast: Node): Node { + return utils.findPluginsByName(j, ast, ["webpack.BannerPlugin"]).forEach( + (path: Node): void => { + const args: Node[] = (path.value as Node).arguments; // any node // If the first argument is a literal replace it with object notation // See https://webpack.js.org/guides/migrating/#bannerplugin-breaking-change if (args && args.length > 1 && args[0].type === j.Literal.name) { // and remove the first argument - path.value.arguments = [path.value.arguments[1]]; - utils.createOrUpdatePluginByName( - j, - path.parent, - "webpack.BannerPlugin", - { - banner: args[0].value, - }, - ); + (path.value as Node).arguments = [(path.value as Node).arguments[1]]; + utils.createOrUpdatePluginByName(j, path.parent, "webpack.BannerPlugin", { + banner: args[0].value + }); } - }); + } + ); } diff --git a/packages/migrate/commonsChunkPlugin/commonsChunkPlugin.ts b/packages/migrate/commonsChunkPlugin/commonsChunkPlugin.ts index ea86fb371ba..62bc6a090e2 100644 --- a/packages/migrate/commonsChunkPlugin/commonsChunkPlugin.ts +++ b/packages/migrate/commonsChunkPlugin/commonsChunkPlugin.ts @@ -4,10 +4,47 @@ import { createProperty, findAndRemovePluginByName, findPluginsByName, - findRootNodesByName, + findRootNodesByName } from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; + +// merge test entry prop and function expression. case 6[x] +// TODO: set the proper type once moved to @types/jscodeshift +// eslint-disable-next-line +const mergeTestPropArrowFunction = (j, chunkKey, testFunc): any => { + return j.property( + "init", + createIdentifierOrLiteral(j, "test"), + j.arrowFunctionExpression( + [j.identifier("module")], + j.blockStatement([ + j.ifStatement( + j.callExpression( + j.memberExpression( + j.callExpression(j.memberExpression(j.identifier("module"), j.identifier("getChunks")), []), + j.identifier("some"), + false + ), + [ + j.arrowFunctionExpression( + [j.identifier("chunk")], + j.binaryExpression( + "===", + j.memberExpression(j.identifier("chunk"), j.identifier("name")), + j.literal(chunkKey) + ) + ) + ] + ), + j.returnStatement(j.literal(true)) + ), + j.variableDeclaration("const", [j.variableDeclarator(j.identifier("fn"), testFunc)]), + j.returnStatement(j.callExpression(j.identifier("fn"), [j.identifier("module")])) + ]) + ) + ); +}; /** * @@ -18,20 +55,15 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @param {Node} ast - jscodeshift ast to transform * @returns {Node} ast - jscodeshift ast */ -export default function(j: IJSCodeshift, ast: INode): INode { - const splitChunksProps: INode[] = []; - const cacheGroupsProps: INode[] = []; +export default function(j: JSCodeshift, ast: Node): Node { + const splitChunksProps: Node[] = []; + const cacheGroupsProps: Node[] = []; const optimizationProps: object = {}; - let commonCacheGroupsProps: INode[] = [ - createProperty(j, "chunks", "initial"), - createProperty(j, "enforce", true), - ]; + let commonCacheGroupsProps: Node[] = [createProperty(j, "chunks", "initial"), createProperty(j, "enforce", true)]; // find old options - const CommonsChunkPlugin: INode = findPluginsByName(j, ast, [ - "webpack.optimize.CommonsChunkPlugin", - ]); + const CommonsChunkPlugin: Node = findPluginsByName(j, ast, ["webpack.optimize.CommonsChunkPlugin"]); if (!CommonsChunkPlugin.size()) { return ast; @@ -39,64 +71,66 @@ export default function(j: IJSCodeshift, ast: INode): INode { // cache group options based on keys let cacheGroup: object = {}; - let cacheGroups: INode[] = []; + let cacheGroups: Node[] = []; // iterate each CommonsChunkPlugin instance CommonsChunkPlugin.forEach( - (path: INode): void => { - const CCPProps: INode[] = path.value.arguments[0].properties; + (path: Node): void => { + const CCPProps: Node[] = (path.value as Node).arguments[0].properties; // reset chunks from old props cacheGroup = {}; cacheGroups = []; - commonCacheGroupsProps = [ - createProperty(j, "chunks", "initial"), - createProperty(j, "enforce", true), - ]; + commonCacheGroupsProps = [createProperty(j, "chunks", "initial"), createProperty(j, "enforce", true)]; let chunkKey: string; - let chunkCount: number = 0; + let chunkCount = 0; // iterate CCP props and map SCP props CCPProps.forEach( - (p: INode): void => { + (p: Node): void => { const propKey: string = p.key.name; switch (propKey) { case "names": - p.value.elements.forEach(({ value: chunkValue }): void => { - if (chunkValue === "runtime") { - optimizationProps["runtimeChunk"] = j.objectExpression([ // tslint:disable-line - createProperty(j, "name", chunkValue), - ]); - } else { - if (!Array.isArray(cacheGroup[chunkValue])) { - cacheGroup[chunkValue] = []; - } + (p.value as Node).elements.forEach( + ({ value: chunkValue }): void => { + if (chunkValue === "runtime") { + optimizationProps["runtimeChunk"] = j.objectExpression([ + createProperty(j, "name", chunkValue) + ]); + } else { + if (!Array.isArray(cacheGroup[chunkValue as string])) { + cacheGroup[chunkValue as string] = []; + } - findRootNodesByName(j, ast, "entry").forEach( - ({ value: { value: { properties: entries }} }, - ): void => { - chunkCount = entries.length; - entries.forEach(({ key: { name: entryName }}): void => { - if (entryName === chunkValue) { - cacheGroup[chunkValue].push( - createProperty(j, "test", entryName), + findRootNodesByName(j, ast, "entry").forEach( + ({ value }): void => { + const { properties: entries } = (value as Node).value as Node; + chunkCount = entries.length; + entries.forEach( + ({ key: { name: entryName } }): void => { + if (entryName === chunkValue) { + cacheGroup[chunkValue as string].push( + createProperty(j, "test", entryName) + ); + } + } ); } - }); - }); + ); + } } - }); + ); break; - case "name": - const nameKey = p.value.value; + case "name": { + const nameKey = (p.value as Node).value as string; if (nameKey === "runtime") { - optimizationProps["runtimeChunk"] = j.objectExpression([ // tslint:disable-line - createProperty(j, "name", nameKey), + optimizationProps["runtimeChunk"] = j.objectExpression([ + createProperty(j, "name", nameKey) ]); } else { chunkKey = nameKey; @@ -106,19 +140,21 @@ export default function(j: IJSCodeshift, ast: INode): INode { } findRootNodesByName(j, ast, "entry").forEach( - ({ value: { value: { properties: entries }} }, - ): void => { - chunkCount = entries.length; - entries.forEach(({ key: { name: entryName }}): void => { - if (entryName === nameKey) { - cacheGroup[nameKey].push( - createProperty(j, "test", entryName), - ); - } - }); - }); + ({ value }): void => { + const { properties: entries } = (value as Node).value as Node; + chunkCount = entries.length; + entries.forEach( + ({ key: { name: entryName } }): void => { + if (entryName === nameKey) { + cacheGroup[nameKey].push(createProperty(j, "test", entryName)); + } + } + ); + } + ); } break; + } case "filename": if (chunkKey) { @@ -126,7 +162,7 @@ export default function(j: IJSCodeshift, ast: INode): INode { cacheGroup[chunkKey] = []; } cacheGroup[chunkKey].push( - createProperty(j, propKey, p.value.value), + createProperty(j, propKey, (p.value as Node).value as string) ); } break; @@ -135,9 +171,7 @@ export default function(j: IJSCodeshift, ast: INode): INode { if (!Array.isArray(cacheGroup[chunkKey])) { cacheGroup[chunkKey] = []; } - cacheGroup[chunkKey].push( - createProperty(j, "chunks", "async"), - ); + cacheGroup[chunkKey].push(createProperty(j, "chunks", "async")); break; case "minSize": @@ -145,177 +179,104 @@ export default function(j: IJSCodeshift, ast: INode): INode { cacheGroup[chunkKey] = []; } cacheGroup[chunkKey].push( - j.property("init", createIdentifierOrLiteral(j, propKey), p.value), + j.property("init", createIdentifierOrLiteral(j, propKey), p.value as Node) ); break; - case "minChunks" : - const { value: pathValue }: INode = p; + case "minChunks": { + const { value: pathValue }: Node = p; // minChunk is a function if ( - pathValue.type === "ArrowFunctionExpression" || - pathValue.type === "FunctionExpression" + (pathValue as Node).type === "ArrowFunctionExpression" || + (pathValue as Node).type === "FunctionExpression" ) { if (!Array.isArray(cacheGroup[chunkKey])) { cacheGroup[chunkKey] = []; } - cacheGroup[chunkKey] = cacheGroup[chunkKey].map((prop) => - prop.key.name === "test" ? mergeTestPropArrowFunction(j, chunkKey, pathValue) : prop); + // eslint-disable-next-line + cacheGroup[chunkKey] = cacheGroup[chunkKey].map( + (prop): any => + prop.key.name === "test" + ? mergeTestPropArrowFunction(j, chunkKey, pathValue) + : prop + ); } break; + } } - }, + } ); - Object.keys(cacheGroup).forEach((chunkName: string): void => { - let chunkProps: INode[] = [ - createProperty(j, "name", chunkName), - ]; + Object.keys(cacheGroup).forEach( + (chunkName: string): void => { + let chunkProps: Node[] = [createProperty(j, "name", chunkName)]; - const chunkPropsToAdd = cacheGroup[chunkName]; - const chunkPropsKeys = chunkPropsToAdd.map((prop) => prop.key.name); + const chunkPropsToAdd = cacheGroup[chunkName]; + const chunkPropsKeys = chunkPropsToAdd.map((prop): string => prop.key.name); - commonCacheGroupsProps = - commonCacheGroupsProps.filter((commonProp) => !chunkPropsKeys.includes(commonProp.key.name)); + commonCacheGroupsProps = commonCacheGroupsProps.filter( + (commonProp): boolean => !chunkPropsKeys.includes(commonProp.key.name) + ); - chunkProps.push(...commonCacheGroupsProps); + chunkProps.push(...commonCacheGroupsProps); - if (chunkCount > 1) { - chunkProps.push( - j.property( - "init", - createIdentifierOrLiteral(j, "minChunks"), - createIdentifierOrLiteral(j, chunkCount), - ), + if (chunkCount > 1) { + chunkProps.push( + j.property( + "init", + createIdentifierOrLiteral(j, "minChunks"), + createIdentifierOrLiteral(j, chunkCount) + ) + ); + } + + const chunkPropsContainTest = chunkPropsToAdd.some( + (prop): boolean => prop.key.name === "test" && prop.value.type === "Literal" ); - } - const chunkPropsContainTest = - chunkPropsToAdd.some((prop) => prop.key.name === "test" && prop.value.type === "Literal"); + if (chunkPropsContainTest) { + chunkProps = chunkProps.filter((prop): boolean => prop.key.name !== "minChunks"); + } - if (chunkPropsContainTest) { - chunkProps = chunkProps.filter((prop) => prop.key.name !== "minChunks"); - } + if (chunkPropsToAdd && Array.isArray(chunkPropsToAdd) && chunkPropsToAdd.length > 0) { + chunkProps.push(...chunkPropsToAdd); + } - if ( - chunkPropsToAdd && - Array.isArray(chunkPropsToAdd) && - chunkPropsToAdd.length > 0 - ) { - chunkProps.push(...chunkPropsToAdd); + cacheGroups.push( + j.property("init", createIdentifierOrLiteral(j, chunkName), j.objectExpression([...chunkProps])) + ); } - - cacheGroups.push( - j.property( - "init", - createIdentifierOrLiteral(j, chunkName), - j.objectExpression([...chunkProps]), - ), - ); - }); + ); if (cacheGroups.length > 0) { cacheGroupsProps.push(...cacheGroups); } - }, + } ); // Remove old plugin - const root: INode = findAndRemovePluginByName( - j, - ast, - "webpack.optimize.CommonsChunkPlugin", - ); + const root: Node = findAndRemovePluginByName(j, ast, "webpack.optimize.CommonsChunkPlugin"); - const rootProps: INode[] = [...splitChunksProps]; + const rootProps: Node[] = [...splitChunksProps]; if (cacheGroupsProps.length > 0) { rootProps.push( - j.property( - "init", - createIdentifierOrLiteral(j, "cacheGroups"), - j.objectExpression([...cacheGroupsProps]), - ), + j.property("init", createIdentifierOrLiteral(j, "cacheGroups"), j.objectExpression([...cacheGroupsProps])) ); } // Add new optimizations splitChunks option if (root) { - addOrUpdateConfigObject( - j, - root, - "optimizations", - "splitChunks", - j.objectExpression([...rootProps]), - ); + addOrUpdateConfigObject(j, root, "optimizations", "splitChunks", j.objectExpression([...rootProps])); - Object.keys(optimizationProps).forEach((key: string): void => { - addOrUpdateConfigObject( - j, - root, - "optimizations", - key, - optimizationProps[key], - ); - }); + Object.keys(optimizationProps).forEach( + (key: string): void => { + addOrUpdateConfigObject(j, root, "optimizations", key, optimizationProps[key]); + } + ); } return ast; } - -// merge test entry prop and function expression. case 6[x] -const mergeTestPropArrowFunction = (j, chunkKey, testFunc) => { - return j.property( - "init", - createIdentifierOrLiteral(j, "test"), - j.arrowFunctionExpression( - [j.identifier("module")], - j.blockStatement([ - j.ifStatement( - j.callExpression( - j.memberExpression( - j.callExpression( - j.memberExpression( - j.identifier("module"), - j.identifier("getChunks"), - ), - [], - ), - j.identifier("some"), - false, - ), - [j.arrowFunctionExpression( - [j.identifier("chunk")], - j.binaryExpression( - "===", - j.memberExpression( - j.identifier("chunk"), - j.identifier("name"), - ), - j.literal(chunkKey), - ), - )], - ), - j.returnStatement( - j.literal(true), - ), - ), - j.variableDeclaration( - "const", - [j.variableDeclarator( - j.identifier("fn"), - testFunc, - )], - ), - j.returnStatement( - j.callExpression( - j.identifier("fn"), - [j.identifier("module")], - ), - ), - ]), - ), - ); -}; diff --git a/packages/migrate/extractTextPlugin/extractTextPlugin.ts b/packages/migrate/extractTextPlugin/extractTextPlugin.ts index bf0cf39d937..fcc3931a394 100644 --- a/packages/migrate/extractTextPlugin/extractTextPlugin.ts +++ b/packages/migrate/extractTextPlugin/extractTextPlugin.ts @@ -1,6 +1,6 @@ import * as utils from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; /** * @@ -12,12 +12,12 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @returns {Boolean} isPluginInvocation - whether `node` is the invocation of the plugin denoted by `pluginName` */ -function findInvocation(j: IJSCodeshift, path: INode, pluginName: string): boolean { - let found: boolean = false; +function findInvocation(j: JSCodeshift, path: Node, pluginName: string): boolean { + let found = false; found = j(path) .find(j.MemberExpression) - .filter((p: INode): boolean => p.get("object").value.name === pluginName) + .filter((p: Node): boolean => (p.get("object").value as Node).name === pluginName) .size() > 0; return found; } @@ -31,30 +31,28 @@ function findInvocation(j: IJSCodeshift, path: INode, pluginName: string): boole * @returns {Node} ast - jscodeshift ast */ -export default function(j: IJSCodeshift, ast: INode): void | INode { - const changeArguments = (path: INode): INode => { - const args: INode[] = path.value.arguments; +export default function(j: JSCodeshift, ast: Node): void | Node { + const changeArguments = (path: Node): Node => { + const args: Node[] = (path.value as Node).arguments; - const literalArgs: INode[] = args.filter((p: INode): boolean => utils.isType(p, "Literal")); + const literalArgs: Node[] = args.filter((p: Node): boolean => utils.isType(p, "Literal")); if (literalArgs && literalArgs.length > 1) { const newArgs: object = j.objectExpression( - literalArgs.map((p: INode, index: number): INode => - utils.createProperty(j, index === 0 ? "fallback" : "use", p.value), - ), + literalArgs.map( + (p: Node, index: number): Node => utils.createProperty(j, index === 0 ? "fallback" : "use", p.value as Node) + ) ); - path.value.arguments = [newArgs]; + (path.value as Node).arguments = [newArgs]; } return path; }; - const name: string = utils.findVariableToPlugin( - j, - ast, - "extract-text-webpack-plugin", - ); - if (!name) { return ast; } + const name: string = utils.findVariableToPlugin(j, ast, "extract-text-webpack-plugin"); + if (!name) { + return ast; + } return ast .find(j.CallExpression) - .filter((p: INode): boolean => findInvocation(j, p, name)) + .filter((p: Node): boolean => findInvocation(j, p, name)) .forEach(changeArguments); } diff --git a/packages/migrate/index.ts b/packages/migrate/index.ts index 2a34c453438..09e6bcee346 100644 --- a/packages/migrate/index.ts +++ b/packages/migrate/index.ts @@ -10,19 +10,20 @@ import { validate, WebpackOptionsValidationError } from "webpack"; import runPrettier from "@webpack-cli/utils/run-prettier"; import { transformations } from "./migrate"; -import { IJSCodeshift, INode } from "./types/NodePath"; +import { Node } from "./types/NodePath"; +import jscodeshift from "jscodeshift"; declare var process: { cwd: Function; webpackModule: { validate: Function; - /* tslint:disable */ WebpackOptionsValidationError: { - new: (errors: string[]) => { + new: ( + errors: string[] + ) => { message: string; }; }; - /* tslint:enable */ }; stdout: { write: Function; @@ -30,6 +31,167 @@ declare var process: { exitCode: number; }; +/** + * + * Runs migration on a given configuration using AST's and promises + * to sequentially transform a configuration file. + * + * @param {String} currentConfigPath - input path for config + * @param {String} outputConfigPath - output path for config + * @returns {Promise} Runs the migration using a promise that + * will throw any errors during each transform or output if the + * user decides to abort the migration + */ + +function runMigration(currentConfigPath: string, outputConfigPath: string): Promise | void { + const recastOptions: object = { + quote: "single" + }; + + const tasks: Listr = new Listr([ + { + task: (ctx: Node): string | void | Listr | Promise<{}> => + new pLazy( + ( + resolve: (value?: object) => void, + reject: (reason?: string | object, err?: object) => void + ): void => { + fs.readFile( + currentConfigPath, + "utf8", + (err: object, content: string): void => { + if (err) { + reject(err); + } + try { + ctx.source = content; + ctx.ast = jscodeshift(content); + resolve(); + } catch (err) { + reject("Error generating AST", err); + } + } + ); + } + ), + title: "Reading webpack config" + }, + { + task: (ctx: Node): string | void | Listr | Promise<{}> => { + return new Listr( + Object.keys(transformations).map( + ( + key: string + ): { + task: () => string; + title: string; + } => { + const transform: Function = transformations[key]; + return { + task: (): string => transform(ctx.ast, ctx.source), + title: key + }; + } + ) + ); + }, + title: "Migrating config to newest version" + } + ]); + + tasks + .run() + .then( + (ctx: Node): void | Promise => { + const result: string = ctx.ast.toSource(recastOptions); + const diffOutput: diff.IDiffResult[] = diff.diffLines(ctx.source, result); + + diffOutput.forEach( + (diffLine: diff.IDiffResult): void => { + if (diffLine.added) { + process.stdout.write(chalk.green(`+ ${diffLine.value}`)); + } else if (diffLine.removed) { + process.stdout.write(chalk.red(`- ${diffLine.value}`)); + } + } + ); + + return inquirer + .prompt([ + { + default: "Y", + message: "Are you sure these changes are fine?", + name: "confirmMigration", + type: "confirm" + } + ]) + .then( + (answers: { confirmMigration: boolean }): Promise<{}> => { + if (answers.confirmMigration) { + return inquirer.prompt([ + { + default: "Y", + message: + "Do you want to validate your configuration? " + + "(If you're using webpack merge, validation isn't useful)", + name: "confirmValidation", + type: "confirm" + } + ]); + } else { + console.error(chalk.red("✖ Migration aborted")); + } + } + ) + .then( + (answer: { confirmValidation: boolean }): void => { + if (!answer) { + return; + } + + runPrettier( + outputConfigPath, + result, + (err: object): void => { + if (err) { + throw err; + } + } + ); + + if (answer.confirmValidation) { + const webpackOptionsValidationErrors: string[] = validate(require(outputConfigPath)); + + if (webpackOptionsValidationErrors.length) { + console.error(chalk.red("\n✖ Your configuration validation wasn't successful \n")); + console.error( + new WebpackOptionsValidationError(webpackOptionsValidationErrors).message + ); + } + } + + console.info(chalk.green(`\n✔︎ New webpack config file is at ${outputConfigPath}.`)); + console.info( + chalk.green( + "✔︎ Heads up! Updating to the latest version could contain breaking changes." + ) + ); + + console.info(chalk.green("✔︎ Plugin and loader dependencies may need to be updated.")); + } + ); + } + ) + .catch( + (err: object): void => { + const errMsg = "\n ✖ ︎Migration aborted due to some errors: \n"; + console.error(chalk.red(errMsg)); + console.error(err); + process.exitCode = 1; + } + ); +} + /** * * Runs migration on a given configuration using AST's and promises @@ -62,179 +224,25 @@ export default function migrate(...args: string[]): void | Promise { "Do you want to use your existing webpack " + "configuration?", name: "confirmPath", - type: "confirm", - }, + type: "confirm" + } ]) - .then((ans: { - confirmPath: boolean; - }): void | Promise => { - if (!ans.confirmPath) { - console.error(chalk.red("✖ ︎Migration aborted due no output path")); - return; + .then( + (ans: { confirmPath: boolean }): void | Promise => { + if (!ans.confirmPath) { + console.error(chalk.red("✖ ︎Migration aborted due no output path")); + return; + } + outputConfigPath = path.resolve(process.cwd(), filePaths[0]); + return runMigration(currentConfigPath, outputConfigPath); } - outputConfigPath = path.resolve(process.cwd(), filePaths[0]); - return runMigration(currentConfigPath, outputConfigPath); - }) - .catch((err: object): void => { - console.error(err); - }); + ) + .catch( + (err: object): void => { + console.error(err); + } + ); } outputConfigPath = path.resolve(process.cwd(), filePaths[1]); return runMigration(currentConfigPath, outputConfigPath); } - -/** - * - * Runs migration on a given configuration using AST's and promises - * to sequentially transform a configuration file. - * - * @param {String} currentConfigPath - input path for config - * @param {String} outputConfigPath - output path for config - * @returns {Promise} Runs the migration using a promise that - * will throw any errors during each transform or output if the - * user decides to abort the migration - */ - -function runMigration(currentConfigPath: string, outputConfigPath: string): Promise | void { - const recastOptions: object = { - quote: "single", - }; - - const tasks: Listr = new Listr([ - { - task: (ctx: INode): string | void | Listr | Promise<{}> => - new pLazy(( - resolve: (value?: object) => void, - reject: (reason?: string | object, err?: object) => void, - ) => { - fs.readFile(currentConfigPath, "utf8", (err: object, content: string) => { - if (err) { - reject(err); - } - try { - const jscodeshift: IJSCodeshift = require("jscodeshift"); - ctx.source = content; - ctx.ast = jscodeshift(content); - resolve(); - } catch (err) { - reject("Error generating AST", err); - } - }); - }), - title: "Reading webpack config", - }, - { - task: (ctx: INode): string | void | Listr | Promise => { - return new Listr( - Object.keys(transformations).map((key: string): { - task: (_?: void) => string; - title: string; - } => { - const transform: Function = transformations[key]; - return { - task: (_?: void) => transform(ctx.ast, ctx.source), - title: key, - }; - }), - ); - }, - title: "Migrating config to newest version", - }, - ]); - - tasks - .run() - .then((ctx: INode): void | Promise => { - const result: string = ctx.ast.toSource(recastOptions); - const diffOutput: diff.IDiffResult[] = diff.diffLines(ctx.source, result); - - diffOutput.forEach((diffLine: diff.IDiffResult): void => { - if (diffLine.added) { - process.stdout.write(chalk.green(`+ ${diffLine.value}`)); - } else if (diffLine.removed) { - process.stdout.write(chalk.red(`- ${diffLine.value}`)); - } - }); - - return inquirer - .prompt([ - { - default: "Y", - message: "Are you sure these changes are fine?", - name: "confirmMigration", - type: "confirm", - }, - ]) - .then((answers: { - confirmMigration: boolean; - }): Promise<{}> => { - if (answers.confirmMigration) { - return inquirer.prompt([ - { - default: "Y", - message: - "Do you want to validate your configuration? " + - "(If you're using webpack merge, validation isn't useful)", - name: "confirmValidation", - type: "confirm", - }, - ]); - } else { - console.error(chalk.red("✖ Migration aborted")); - } - }) - .then((answer: { - confirmValidation: boolean; - }): void => { - if (!answer) { return; } - - runPrettier(outputConfigPath, result, (err: object): void => { - if (err) { - throw err; - } - }); - - if (answer.confirmValidation) { - const webpackOptionsValidationErrors: string[] = validate( - require(outputConfigPath), - ); - - if (webpackOptionsValidationErrors.length) { - console.error( - chalk.red( - "\n✖ Your configuration validation wasn't successful \n", - ), - ); - console.error( - new WebpackOptionsValidationError( - webpackOptionsValidationErrors, - ).message, - ); - } - } - - console.info( - chalk.green( - `\n✔︎ New webpack config file is at ${outputConfigPath}.`, - ), - ); - console.info( - chalk.green( - "✔︎ Heads up! Updating to the latest version could contain breaking changes.", - ), - ); - - console.info( - chalk.green( - "✔︎ Plugin and loader dependencies may need to be updated.", - ), - ); - }); - }) - .catch((err: object): void => { - const errMsg = "\n ✖ ︎Migration aborted due to some errors: \n"; - console.error(chalk.red(errMsg)); - console.error(err); - process.exitCode = 1; - }); -} diff --git a/packages/migrate/loaderOptionsPlugin/loaderOptionsPlugin.ts b/packages/migrate/loaderOptionsPlugin/loaderOptionsPlugin.ts index 681ff9579cd..b16a927d46b 100644 --- a/packages/migrate/loaderOptionsPlugin/loaderOptionsPlugin.ts +++ b/packages/migrate/loaderOptionsPlugin/loaderOptionsPlugin.ts @@ -1,14 +1,10 @@ import isEmpty = require("lodash/isEmpty"); -import { - createOrUpdatePluginByName, - findPluginsByName, - safeTraverse, -} from "@webpack-cli/utils/ast-utils"; +import { createOrUpdatePluginByName, findPluginsByName, safeTraverse } from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; -interface ILoaderOptions { +interface LoaderOptions { debug?: boolean; minimize?: boolean; } @@ -23,18 +19,18 @@ interface ILoaderOptions { * */ -export default function(j: IJSCodeshift, ast: INode): INode { - const loaderOptions: ILoaderOptions = {}; +export default function(j: JSCodeshift, ast: Node): Node { + const loaderOptions: LoaderOptions = {}; // If there is debug: true, set debug: true in the plugin if (ast.find(j.Identifier, { name: "debug" }).size()) { loaderOptions.debug = true; - ast - .find(j.Identifier, { name: "debug" }) - .forEach((p: INode): void => { + ast.find(j.Identifier, { name: "debug" }).forEach( + (p: Node): void => { p.parent.prune(); - }); + } + ); } // If there is UglifyJsPlugin, set minimize: true @@ -44,18 +40,12 @@ export default function(j: IJSCodeshift, ast: INode): INode { return ast .find(j.ArrayExpression) - .filter( - (path: INode): boolean => - safeTraverse(path, ["parent", "value", "key", "name"]) === "plugins", - ) - .forEach((path: INode): void => { - if (!isEmpty(loaderOptions)) { - createOrUpdatePluginByName( - j, - path, - "webpack.LoaderOptionsPlugin", - loaderOptions, - ); + .filter((path: Node): boolean => safeTraverse(path, ["parent", "value", "key", "name"]) === "plugins") + .forEach( + (path: Node): void => { + if (!isEmpty(loaderOptions)) { + createOrUpdatePluginByName(j, path, "webpack.LoaderOptionsPlugin", loaderOptions); + } } - }); + ); } diff --git a/packages/migrate/loaders/loaders.ts b/packages/migrate/loaders/loaders.ts index e3b5b920fbb..0f5452c2ac1 100644 --- a/packages/migrate/loaders/loaders.ts +++ b/packages/migrate/loaders/loaders.ts @@ -1,6 +1,6 @@ import * as utils from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; /** * @@ -13,7 +13,7 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @returns {Node} ast - jscodeshift ast */ -export default function(j: IJSCodeshift, ast: INode): INode { +export default function(j: JSCodeshift, ast: Node): Node { /** * Creates an Array expression out of loaders string * @@ -54,46 +54,46 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} path - object expression ast with array expression instead of loaders string */ - const createArrayExpressionFromArray = (path: INode): INode => { - const value: INode = path.value; + const createArrayExpressionFromArray = (path: Node): Node => { + const value: Node = (path.value as Node); // Find paths with `loaders` keys in the given Object - const paths: INode[] = value.properties.filter((prop: INode): boolean => - prop.key.name.startsWith("loader"), - ); + const paths: Node[] = value.properties.filter((prop: Node): boolean => prop.key.name.startsWith("loader")); // For each pair of key and value - paths.forEach((pair: INode): void => { - // Replace 'loaders' Identifier with 'use' - pair.key.name = "use"; - // If the value is an Array - if (pair.value.type === j.ArrayExpression.name) { - // replace its elements - const pairValue: INode = pair.value; - pair.value = j.arrayExpression( - pairValue.elements.map((arrElement: INode): INode => { - // If items of the array are Strings - if (arrElement.type === j.Literal.name) { - // Replace with `{ loader: LOADER }` Object - return j.objectExpression([ - utils.createProperty(j, "loader", arrElement.value), - ]); - } - // otherwise keep the existing element - return arrElement; - }), - ); - // If the value is String of loaders like 'style!css' - } else if (pair.value.type === j.Literal.name) { - // Replace it with Array expression of loaders - const literalValue: INode = pair.value; - pair.value = j.arrayExpression( - literalValue.value.split("!").map((loader: string): INode => { - return j.objectExpression([ - utils.createProperty(j, "loader", loader), - ]); - }), - ); + paths.forEach( + (pair: Node): void => { + // Replace 'loaders' Identifier with 'use' + pair.key.name = "use"; + // If the value is an Array + if ((pair.value as Node).type === j.ArrayExpression.name) { + // replace its elements + const pairValue = pair.value as Node; + pair.value = j.arrayExpression( + pairValue.elements.map( + (arrElement: Node): Node => { + // If items of the array are Strings + if (arrElement.type === j.Literal.name) { + // Replace with `{ loader: LOADER }` Object + return j.objectExpression([utils.createProperty(j, "loader", (arrElement.value as Node))]); + } + // otherwise keep the existing element + return arrElement; + } + ) + ); + // If the value is String of loaders like 'style!css' + } else if ((pair.value as Node).type === j.Literal.name) { + // Replace it with Array expression of loaders + const literalValue = pair.value as Node; + pair.value = j.arrayExpression( + (literalValue.value as string).split("!").map( + (loader: string): Node => { + return j.objectExpression([utils.createProperty(j, "loader", loader)]); + } + ) + ); + } } - }); + ); return path; }; @@ -105,26 +105,24 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} objectExpression - an new object expression ast containing the query parameters */ - const createLoaderWithQuery = (p: INode): INode => { - const properties: INode[] = p.value.properties; + const createLoaderWithQuery = (p: Node): Node => { + const properties: Node[] = (p.value as Node).properties; const loaderValue: string = properties.reduce( - (val: string, prop: INode): string => (prop.key.name === "loader" ? prop.value.value : val), - "", + (val: string, prop: Node): string => (prop.key.name === "loader" ? (prop.value as Node).value as string: val), + "" ); const loader: string = loaderValue.split("?")[0]; const query: string = loaderValue.split("?")[1]; - const options: INode[] = query.split("&").map((option: string): INode => { - const param: string[] = option.split("="); - const key: string = param[0]; - const val: string | boolean = param[1] || true; // No value in query string means it is truthy value - return j.objectProperty(j.identifier(key), utils.createLiteral(j, val)); - }); - const loaderProp: INode = utils.createProperty(j, "loader", loader); - const queryProp: INode = j.property( - "init", - j.identifier("options"), - j.objectExpression(options), + const options: Node[] = query.split("&").map( + (option: string): Node => { + const param: string[] = option.split("="); + const key: string = param[0]; + const val: string | boolean = param[1] || true; // No value in query string means it is truthy value + return j.objectProperty(j.identifier(key), utils.createLiteral(j, val)); + } ); + const loaderProp: Node = utils.createProperty(j, "loader", loader); + const queryProp: Node = j.property("init", j.identifier("options"), j.objectExpression(options)); return j.objectExpression([loaderProp, queryProp]); }; @@ -136,11 +134,10 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Boolean} hasLoaderQueryString - whether the loader object contains a query string */ - const findLoaderWithQueryString = (p: INode): boolean => { - return p.value.properties.reduce((predicate: boolean, prop: INode): boolean => { + const findLoaderWithQueryString = (p: Node): boolean => { + return (p.value as Node).properties.reduce((predicate: boolean, prop: Node): boolean => { return ( - (utils.safeTraverse(prop, ["value", "value", "indexOf"]) && - prop.value.value.indexOf("?") > -1) || + (utils.safeTraverse(prop, ["value", "value", "indexOf"]) && ((prop.value as Node).value as string).indexOf("?") > -1) || predicate ); }, false); @@ -155,16 +152,9 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Boolean} isLoadersProp - whether the identifier is the `loaders` prop in the `module` object */ - const checkForLoader = (path: INode): boolean => - path.value.name === "loaders" && - utils.safeTraverse(path, [ - "parent", - "parent", - "parent", - "node", - "key", - "name", - ]) === "module"; + const checkForLoader = (path: Node): boolean => + (path.value as Node).name === "loaders" && + utils.safeTraverse(path, ["parent", "parent", "parent", "node", "key", "name"]) === "module"; /** * Puts pre- or postLoader into `loaders` object and adds the appropriate `enforce` property @@ -173,32 +163,36 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} p - object expression with a `loaders` object and appropriate `enforce` properties */ - const fitIntoLoaders = (p: INode): INode => { - let loaders: INode = null; - p.value.properties.map((prop: INode): void => { - const keyName = prop.key.name; - if (keyName === "loaders") { - loaders = prop.value; + const fitIntoLoaders = (p: Node): Node => { + let loaders: Node = null; + (p.value as Node).properties.map( + (prop: Node): void => { + const keyName = prop.key.name; + if (keyName === "loaders") { + loaders = prop.value as Node; + } } - }); - p.value.properties.map((prop: INode): void => { - const keyName = prop.key.name; - if (keyName !== "loaders") { - const enforceVal: string = keyName === "preLoaders" ? "pre" : "post"; - prop.value.elements.map((elem: INode): void => { - elem.properties.push(utils.createProperty(j, "enforce", enforceVal)); - if (loaders && loaders.type === "ArrayExpression") { - loaders.elements.push(elem); - } else { - prop.key.name = "loaders"; - } - }); + ); + (p.value as Node).properties.map( + (prop: Node): void => { + const keyName = prop.key.name; + if (keyName !== "loaders") { + const enforceVal: string = keyName === "preLoaders" ? "pre" : "post"; + (prop.value as Node).elements.map( + (elem: Node): void => { + elem.properties.push(utils.createProperty(j, "enforce", enforceVal)); + if (loaders && loaders.type === "ArrayExpression") { + loaders.elements.push(elem); + } else { + prop.key.name = "loaders"; + } + } + ); + } } - }); + ); if (loaders) { - p.value.properties = p.value.properties.filter( - (prop: INode): boolean => prop.key.name === "loaders", - ); + (p.value as Node).properties = (p.value as Node).properties.filter((prop: Node): boolean => prop.key.name === "loaders"); } return p; }; @@ -209,10 +203,10 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} ast - jscodeshift ast */ - const prepostLoaders = (_?: void): INode => + const prepostLoaders = (): Node => ast .find(j.ObjectExpression) - .filter((p: INode): boolean => utils.findObjWithOneOfKeys(p, ["preLoaders", "postLoaders"])) + .filter((p: Node): boolean => utils.findObjWithOneOfKeys(p, ["preLoaders", "postLoaders"])) .forEach(fitIntoLoaders); /** @@ -221,11 +215,11 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} ast - jscodeshift ast */ - const loadersToRules = (_?: void): INode => + const loadersToRules = (): Node => ast .find(j.Identifier) .filter(checkForLoader) - .forEach((p: INode): string => (p.value.name = "rules")); + .forEach((p: Node): string => ((p.value as Node).name = "rules")); /** * Convert `loader` and `loaders` to Array of {Rule.Use} @@ -233,19 +227,13 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} ast - jscodeshift ast */ - const loadersToArrayExpression = (_?: void): INode | void => + const loadersToArrayExpression = (): Node | void => ast .find(j.ObjectExpression) - .filter((path: INode): boolean => utils.findObjWithOneOfKeys(path, ["loader", "loaders"])) + .filter((path: Node): boolean => utils.findObjWithOneOfKeys(path, ["loader", "loaders"])) .filter( - (path: INode): boolean => - utils.safeTraverse(path, [ - "parent", - "parent", - "node", - "key", - "name", - ]) === "rules", + (path: Node): boolean => + utils.safeTraverse(path, ["parent", "parent", "node", "key", "name"]) === "rules" ) .forEach(createArrayExpressionFromArray); @@ -271,10 +259,10 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} ast - jscodeshift ast */ - const loaderWithQueryParam = (_?: void): INode => + const loaderWithQueryParam = (): Node => ast .find(j.ObjectExpression) - .filter((p: INode): boolean => utils.findObjWithOneOfKeys(p, ["loader"])) + .filter((p: Node): boolean => utils.findObjWithOneOfKeys(p, ["loader"])) .filter(findLoaderWithQueryString) .replaceWith(createLoaderWithQuery); @@ -295,10 +283,10 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} ast - jscodeshift ast */ - const loaderWithQueryProp = (_?: void): INode => + const loaderWithQueryProp = (): Node => ast .find(j.Identifier) - .filter((p: INode): boolean => p.value.name === "query") + .filter((p: Node): boolean => (p.value as Node).name === "query") .replaceWith(j.identifier("options")); /** @@ -308,18 +296,22 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} ast - jscodeshift ast */ - const addLoaderSuffix = (_?: void): INode => - ast.find(j.ObjectExpression).forEach((path: INode): void => { - path.value.properties.forEach((prop: INode): void => { - if ( - prop.key.name === "loader" && - utils.safeTraverse(prop, ["value", "value"]) && - !prop.value.value.endsWith("-loader") - ) { - prop.value = j.literal(prop.value.value + "-loader"); - } - }); - }); + const addLoaderSuffix = (): Node => + ast.find(j.ObjectExpression).forEach( + (path: Node): void => { + (path.value as Node).properties.forEach( + (prop: Node): void => { + if ( + prop.key.name === "loader" && + utils.safeTraverse(prop, ["value", "value"]) && + !((prop.value as Node).value as string).endsWith("-loader") + ) { + prop.value = j.literal((prop.value as Node).value as string + "-loader"); + } + } + ); + } + ); /** * @@ -329,26 +321,28 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} objectExpression - an use object expression ast containing the options and loader */ - const fitOptionsToUse = (p: INode): INode => { - let options: INode = null; - p.value.properties.forEach((prop: INode): void => { - const keyName: string = prop.key.name; - if (keyName === "options") { - options = prop; + const fitOptionsToUse = (p: Node): Node => { + let options: Node = null; + (p.value as Node).properties.forEach( + (prop: Node): void => { + const keyName: string = prop.key.name; + if (keyName === "options") { + options = prop; + } } - }); + ); if (options) { - p.value.properties = p.value.properties.filter( - (prop: INode): boolean => prop.key.name !== "options", - ); + (p.value as Node).properties = (p.value as Node).properties.filter((prop: Node): boolean => prop.key.name !== "options"); - p.value.properties.forEach((prop: INode): void => { - const keyName = prop.key.name; - if (keyName === "use") { - prop.value.elements[0].properties.push(options); + (p.value as Node).properties.forEach( + (prop: Node): void => { + const keyName = prop.key.name; + if (keyName === "use") { + (prop.value as Node).elements[0].properties.push(options); + } } - }); + ); } return p; @@ -360,10 +354,10 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {Node} ast - jscodeshift ast */ - const moveOptionsToUse = (_?: void): INode => + const moveOptionsToUse = (): Node => ast .find(j.ObjectExpression) - .filter((p: INode): boolean => utils.findObjWithOneOfKeys(p, ["use"])) + .filter((p: Node): boolean => utils.findObjWithOneOfKeys(p, ["use"])) .forEach(fitOptionsToUse); const transforms = [ @@ -373,7 +367,7 @@ export default function(j: IJSCodeshift, ast: INode): INode { loaderWithQueryParam, loaderWithQueryProp, addLoaderSuffix, - moveOptionsToUse, + moveOptionsToUse ]; transforms.forEach((t: Function): void => t()); diff --git a/packages/migrate/migrate.ts b/packages/migrate/migrate.ts index 09baf1b087d..bfed7bee56d 100644 --- a/packages/migrate/migrate.ts +++ b/packages/migrate/migrate.ts @@ -11,13 +11,12 @@ import noEmitOnErrorsPluginTransform from "./noEmitOnErrorsPlugin/noEmitOnErrors import removeDeprecatedPluginsTransform from "./removeDeprecatedPlugins/removeDeprecatedPlugins"; import removeJsonLoaderTransform from "./removeJsonLoader/removeJsonLoader"; import resolveTransform from "./resolve/resolve"; -import { INode } from "./types/NodePath"; import uglifyJsPluginTransform from "./uglifyJsPlugin/uglifyJsPlugin"; -interface ITransformsObject { +interface TransformsObject { bannerPluginTransform: object; commonsChunkPluginTransform?: object; - extractTextPluginTransform: object; /* tslint:disable */ + extractTextPluginTransform: object; loaderOptionsPluginTransform: object; loadersTransform: object; noEmitOnErrorsPluginTransform: object; @@ -27,8 +26,7 @@ interface ITransformsObject { uglifyJsPluginTransform: object; } -/* tslint:disable object-literal-sort-keys */ -const transformsObject: ITransformsObject = { +const transformsObject: TransformsObject = { loadersTransform, resolveTransform, removeJsonLoaderTransform, @@ -38,10 +36,10 @@ const transformsObject: ITransformsObject = { extractTextPluginTransform, noEmitOnErrorsPluginTransform, removeDeprecatedPluginsTransform, - commonsChunkPluginTransform, + commonsChunkPluginTransform }; -interface ILazyTransformObject { +interface LazyTransformObject { loadersTransform?: (ast: object, source: string) => pLazy<{}>; resolveTransform?: (ast: object, source: string) => pLazy<{}>; removeJsonLoaderTransform?: (ast: object, source: string) => pLazy<{}>; @@ -53,16 +51,6 @@ interface ILazyTransformObject { removeDeprecatedPluginsTransform?: (ast: object, source: string) => pLazy<{}>; commonsChunkPluginTransform?: (ast: object, source: string) => pLazy<{}>; } -/* tslint:enable object-literal-sort-keys */ - -export const transformations: ILazyTransformObject = - Object - .keys(transformsObject) - .reduce((res: object, key: string): ILazyTransformObject => { - res[key] = (ast: object, source: string) => - transformSingleAST(ast, source, transformsObject[key]); - return res; - }, {}); /** * @@ -78,23 +66,29 @@ export const transformations: ILazyTransformObject = export const transformSingleAST = ( ast: object, source: string, - transformFunction: (jscodeshift: object, ast: object, source: string) => object) - : pLazy<{}> => { - - return new pLazy(( - resolve: (value?: {} | PromiseLike<{}>) => void, - reject: (reason?: object) => void, - ): void => { - setTimeout((_?: void): void => { - try { - resolve(transformFunction(jscodeshift, ast, source)); - } catch (err) { - reject(err); - } - }, 0); - }); + transformFunction: (jscodeshift: object, ast: object, source: string) => object +): pLazy<{}> => { + return new pLazy( + (resolve: (value?: {} | PromiseLike<{}>) => void, reject: (reason?: object) => void): void => { + setTimeout((): void => { + try { + resolve(transformFunction(jscodeshift, ast, source)); + } catch (err) { + reject(err); + } + }, 0); + } + ); }; +export const transformations: LazyTransformObject = Object.keys(transformsObject).reduce( + (res: object, key: string): LazyTransformObject => { + res[key] = (ast: object, source: string): object => transformSingleAST(ast, source, transformsObject[key]); + return res; + }, + {} +); + /** * * Transforms a given piece of source code by applying selected transformations to the AST. @@ -111,24 +105,26 @@ export const transformSingleAST = ( export const transform = ( source: string, transforms?: Iterable>, - options?: object) - : Promise => { - - const ast: INode = jscodeshift(source); + options?: object +): Promise => { + const ast = jscodeshift(source); const recastOptions: object = Object.assign( { - quote: "single", + quote: "single" }, - options, + options ); - transforms = - transforms || Object.keys(transformations).map((k: string): Function => transformations[k]); + transforms = transforms || Object.keys(transformations).map((k: string): Function => transformations[k]); - return pEachSeries(transforms, (f: Function) => f(ast, source)) - .then((value: Function[]): string | PromiseLike => { - return ast.toSource(recastOptions); - }) - .catch((err: object) => { - console.error(err); - }); + return pEachSeries(transforms, (f: Function): void => f(ast, source)) + .then( + (): string | PromiseLike => { + return ast.toSource(recastOptions); + } + ) + .catch( + (err: object): void => { + console.error(err); + } + ); }; diff --git a/packages/migrate/moduleConcatenationPlugin/moduleConcatenationPlugin.ts b/packages/migrate/moduleConcatenationPlugin/moduleConcatenationPlugin.ts index 21750bc8663..658d2c1359f 100644 --- a/packages/migrate/moduleConcatenationPlugin/moduleConcatenationPlugin.ts +++ b/packages/migrate/moduleConcatenationPlugin/moduleConcatenationPlugin.ts @@ -1,9 +1,6 @@ -import { - addOrUpdateConfigObject, - findAndRemovePluginByName, -} from "@webpack-cli/utils/ast-utils"; +import { addOrUpdateConfigObject, findAndRemovePluginByName } from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; /** * @@ -14,23 +11,13 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @param {Node} ast - jscodeshift ast to transform * @returns {Node} ast - jscodeshift ast */ -export default function(j: IJSCodeshift, ast: INode): INode { +export default function(j: JSCodeshift, ast: Node): Node { // Remove old plugin - const root: INode = findAndRemovePluginByName( - j, - ast, - "webpack.optimize.ModuleConcatenationPlugin", - ); + const root: Node = findAndRemovePluginByName(j, ast, "webpack.optimize.ModuleConcatenationPlugin"); // Add new optimizations option if (root) { - addOrUpdateConfigObject( - j, - root, - "optimizations", - "concatenateModules", - j.booleanLiteral(true), - ); + addOrUpdateConfigObject(j, root, "optimizations", "concatenateModules", j.booleanLiteral(true)); } return ast; diff --git a/packages/migrate/namedModulesPlugin/namedModulesPlugin.ts b/packages/migrate/namedModulesPlugin/namedModulesPlugin.ts index 98c7e2ceb9c..34faab3cd4b 100644 --- a/packages/migrate/namedModulesPlugin/namedModulesPlugin.ts +++ b/packages/migrate/namedModulesPlugin/namedModulesPlugin.ts @@ -1,9 +1,6 @@ -import { - addOrUpdateConfigObject, - findAndRemovePluginByName, -} from "@webpack-cli/utils/ast-utils"; +import { addOrUpdateConfigObject, findAndRemovePluginByName } from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; /** * @@ -14,19 +11,13 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @param {Node} ast - jscodeshift ast to transform * @returns {Node} ast - jscodeshift ast */ -export default function(j: IJSCodeshift, ast: INode): INode { +export default function(j: JSCodeshift, ast: Node): Node { // Remove old plugin - const root: INode = findAndRemovePluginByName(j, ast, "webpack.NamedModulesPlugin"); + const root: Node = findAndRemovePluginByName(j, ast, "webpack.NamedModulesPlugin"); // Add new optimizations option if (root) { - addOrUpdateConfigObject( - j, - root, - "optimizations", - "namedModules", - j.booleanLiteral(true), - ); + addOrUpdateConfigObject(j, root, "optimizations", "namedModules", j.booleanLiteral(true)); } return ast; diff --git a/packages/migrate/noEmitOnErrorsPlugin/noEmitOnErrorsPlugin.ts b/packages/migrate/noEmitOnErrorsPlugin/noEmitOnErrorsPlugin.ts index c45ce923185..567d688a47c 100644 --- a/packages/migrate/noEmitOnErrorsPlugin/noEmitOnErrorsPlugin.ts +++ b/packages/migrate/noEmitOnErrorsPlugin/noEmitOnErrorsPlugin.ts @@ -1,8 +1,5 @@ -import { - addOrUpdateConfigObject, - findAndRemovePluginByName, -} from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { addOrUpdateConfigObject, findAndRemovePluginByName } from "@webpack-cli/utils/ast-utils"; +import { JSCodeshift, Node } from "../types/NodePath"; /** * @@ -13,23 +10,13 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @param {Node} ast - jscodeshift ast to transform * @returns {Node} ast - jscodeshift ast */ -export default function(j: IJSCodeshift, ast: INode): INode { +export default function(j: JSCodeshift, ast: Node): Node { // Remove old plugin - const root: INode = findAndRemovePluginByName( - j, - ast, - "webpack.NoEmitOnErrorsPlugin", - ); + const root: Node = findAndRemovePluginByName(j, ast, "webpack.NoEmitOnErrorsPlugin"); // Add new optimizations option if (root) { - addOrUpdateConfigObject( - j, - root, - "optimizations", - "noEmitOnErrors", - j.booleanLiteral(true), - ); + addOrUpdateConfigObject(j, root, "optimizations", "noEmitOnErrors", j.booleanLiteral(true)); } return ast; diff --git a/packages/migrate/outputPath/outputPath.ts b/packages/migrate/outputPath/outputPath.ts index 5e88223d808..e3d1c0bd087 100644 --- a/packages/migrate/outputPath/outputPath.ts +++ b/packages/migrate/outputPath/outputPath.ts @@ -1,6 +1,20 @@ import * as utils from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; + + +function replaceWithPath( + j: JSCodeshift, + p: Node, + pathVarName: string, +): Node { + const convertedPath: Node = j.callExpression( + j.memberExpression(j.identifier(pathVarName), j.identifier("join"), false), + [j.identifier("__dirname"), p.value as Node], + ); + + return convertedPath; +} /** * @@ -10,18 +24,17 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @param {Node} ast - jscodeshift ast to transform * @returns {Node} ast - jscodeshift ast */ - -export default function(j: IJSCodeshift, ast: INode): INode | void { - const literalOutputPath: INode = ast +export default function(j: JSCodeshift, ast: Node): Node | void { + const literalOutputPath: Node = ast .find(j.ObjectExpression) .filter( - (p: INode): boolean => + (p: Node): boolean => utils.safeTraverse(p, ["parentPath", "value", "key", "name"]) === "output", ) .find(j.Property) .filter( - (p: INode): boolean => + (p: Node): boolean => utils.safeTraverse(p, ["value", "key", "name"]) === "path" && utils.safeTraverse(p, ["value", "value", "type"]) === "Literal", ); @@ -29,18 +42,20 @@ export default function(j: IJSCodeshift, ast: INode): INode | void { if (literalOutputPath) { let pathVarName = "path"; let isPathPresent = false; - const pathDeclaration: INode = ast + const pathDeclaration: Node = ast .find(j.VariableDeclarator) .filter( - (p: INode): boolean => + (p: Node): boolean => utils.safeTraverse(p, ["value", "init", "callee", "name"]) === "require", ) .filter( - (p: INode): boolean => + (p: Node): boolean => utils.safeTraverse(p, ["value", "init", "arguments"]) && - p.value.init.arguments.reduce( - (isPresent: boolean, a: INode): boolean => { + // TODO: to fix when we have proper typing (@types/jscodeshift) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (p.value as any).init.arguments.reduce( + (isPresent: boolean, a: Node): boolean => { return (a.type === "Literal" && a.value === "path") || isPresent; }, false, @@ -50,23 +65,23 @@ export default function(j: IJSCodeshift, ast: INode): INode | void { if (pathDeclaration) { isPathPresent = true; pathDeclaration.forEach( - (p: INode): void => { - pathVarName = utils.safeTraverse(p, ["value", "id", "name"]); + (p: Node): void => { + pathVarName = utils.safeTraverse(p, ["value", "id", "name"]) as string; }, ); } const finalPathName = pathVarName; literalOutputPath .find(j.Literal) - .replaceWith((p: INode): INode => replaceWithPath(j, p, finalPathName)); + .replaceWith((p: Node): Node => replaceWithPath(j, p, finalPathName)); if (!isPathPresent) { - const pathRequire: INode = utils.getRequire(j, "path", "path"); + const pathRequire: Node = utils.getRequire(j, "path", "path"); return ast .find(j.Program) .replaceWith( - (p: INode): INode => - j.program([].concat(pathRequire).concat(p.value.body)), + (p: Node): Node => + j.program([].concat(pathRequire).concat((p.value as Node).body)), ); } } @@ -74,15 +89,3 @@ export default function(j: IJSCodeshift, ast: INode): INode | void { return ast; } -function replaceWithPath( - j: IJSCodeshift, - p: INode, - pathVarName: string, -): INode { - const convertedPath: INode = j.callExpression( - j.memberExpression(j.identifier(pathVarName), j.identifier("join"), false), - [j.identifier("__dirname"), p.value], - ); - - return convertedPath; -} diff --git a/packages/migrate/package-lock.json b/packages/migrate/package-lock.json index 4c6b238c144..c70bc03abc9 100644 --- a/packages/migrate/package-lock.json +++ b/packages/migrate/package-lock.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/migrate", - "version": "0.1.3", + "version": "0.1.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -67,7 +67,7 @@ }, "@types/listr": { "version": "0.13.0", - "resolved": "http://registry.npmjs.org/@types/listr/-/listr-0.13.0.tgz", + "resolved": "https://registry.npmjs.org/@types/listr/-/listr-0.13.0.tgz", "integrity": "sha512-8DOy0JCGwwAf76xmU0sRzSZCWKSPPA9djRcTYTsyqBPnMdGOjZ5tjmNswC4J9mgKZudte2tuTo1l14R1/t5l/g==", "dev": true, "requires": { @@ -100,7 +100,7 @@ }, "@types/rx": { "version": "4.1.1", - "resolved": "http://registry.npmjs.org/@types/rx/-/rx-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/@types/rx/-/rx-4.1.1.tgz", "integrity": "sha1-WY/JSla67ZdfGUV04PVy/Y5iekg=", "dev": true, "requires": { @@ -488,7 +488,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==" }, "ansi-regex": { @@ -840,7 +840,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -991,7 +991,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -1011,7 +1011,7 @@ }, "supports-color": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" } } @@ -1245,7 +1245,7 @@ }, "babel-plugin-istanbul": { "version": "4.1.6", - "resolved": "http://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==", "dev": true, "requires": { @@ -1308,7 +1308,7 @@ }, "babel-plugin-syntax-object-rest-spread": { "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=" }, "babel-plugin-syntax-trailing-function-commas": { @@ -1943,7 +1943,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -1980,7 +1980,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2023,7 +2023,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2458,7 +2458,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -2640,7 +2640,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -2653,7 +2653,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -2894,7 +2894,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -3152,7 +3152,7 @@ }, "expand-range": { "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "resolved": "http://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "requires": { "fill-range": "^2.1.0" @@ -4381,7 +4381,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -4434,7 +4434,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -4504,7 +4504,7 @@ }, "is-generator-fn": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/is-generator-fn/-/is-generator-fn-1.0.0.tgz", "integrity": "sha1-lp1J4bszKfa7fwkIm+JleLLd1Go=", "dev": true }, @@ -5412,7 +5412,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -5441,7 +5441,7 @@ }, "supports-color": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" } } @@ -5551,7 +5551,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -5571,7 +5571,7 @@ }, "supports-color": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" } } @@ -5849,7 +5849,7 @@ }, "mute-stream": { "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "resolved": "http://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, "nan": { @@ -6327,7 +6327,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -7151,7 +7151,7 @@ }, "resolve": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true }, @@ -7864,7 +7864,7 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -7929,7 +7929,7 @@ }, "stream-browserify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { @@ -8008,7 +8008,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -8284,7 +8284,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -8547,7 +8547,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -8715,7 +8715,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -8735,7 +8735,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { diff --git a/packages/migrate/removeDeprecatedPlugins/removeDeprecatedPlugins.ts b/packages/migrate/removeDeprecatedPlugins/removeDeprecatedPlugins.ts index 01030eb7686..e7f7518bdad 100644 --- a/packages/migrate/removeDeprecatedPlugins/removeDeprecatedPlugins.ts +++ b/packages/migrate/removeDeprecatedPlugins/removeDeprecatedPlugins.ts @@ -2,7 +2,7 @@ import chalk from "chalk"; import * as utils from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; /** * @@ -14,37 +14,34 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @returns {Node} ast - jscodeshift ast */ -export default function(j: IJSCodeshift, ast: INode) { - // List of deprecated plugins to remove - // each item refers to webpack.optimize.[NAME] construct - const deprecatedPlugingsList: string[] = [ - "webpack.optimize.OccurrenceOrderPlugin", - "webpack.optimize.DedupePlugin", - ]; +export default function(j: JSCodeshift, ast: Node): Node { + // List of deprecated plugins to remove + // each item refers to webpack.optimize.[NAME] construct + const deprecatedPlugingsList: string[] = [ + "webpack.optimize.OccurrenceOrderPlugin", + "webpack.optimize.DedupePlugin" + ]; - return utils.findPluginsByName(j, ast, deprecatedPlugingsList).forEach( - (path: INode): void => { - // For now we only support the case where plugins are defined in an Array - const arrayPath: INode = utils.safeTraverse(path, ["parent", "value"]); + return utils.findPluginsByName(j, ast, deprecatedPlugingsList).forEach( + (path: Node): void => { + // For now we only support the case where plugins are defined in an Array + const arrayPath = utils.safeTraverse(path, ["parent", "value"]) as Node; - if (arrayPath && utils.isType(arrayPath, "ArrayExpression")) { - // Check how many plugins are defined and - // if there is only last plugin left remove `plugins: []` node - const arrayElementsPath: INode[] = utils.safeTraverse(arrayPath, [ - "elements", - ]); - if (arrayElementsPath && arrayElementsPath.length === 1) { - j(path.parent.parent).remove(); - } else { - j(path).remove(); - } - } else { - console.error(` + if (arrayPath && utils.isType(arrayPath, "ArrayExpression")) { + // Check how many plugins are defined and + // if there is only last plugin left remove `plugins: []` node + // + const arrayElementsPath = utils.safeTraverse(arrayPath, ["elements"]) as Node[]; + if (arrayElementsPath && arrayElementsPath.length === 1) { + j(path.parent.parent).remove(); + } else { + j(path).remove(); + } + } else { + console.error(` ${chalk.red("Please remove deprecated plugins manually. ")} -See ${chalk.underline( - "https://webpack.js.org/guides/migrating/", - )} for more information.`); +See ${chalk.underline("https://webpack.js.org/guides/migrating/")} for more information.`); + } } - }, - ); + ); } diff --git a/packages/migrate/removeJsonLoader/removeJsonLoader.ts b/packages/migrate/removeJsonLoader/removeJsonLoader.ts index 88d7a4fae5f..1fcd9c379cb 100644 --- a/packages/migrate/removeJsonLoader/removeJsonLoader.ts +++ b/packages/migrate/removeJsonLoader/removeJsonLoader.ts @@ -1,6 +1,8 @@ import * as utils from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; + +type TransformCallback = (astNode: Node) => void; /** * @@ -11,7 +13,7 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @returns {Node} ast - jscodeshift ast */ -export default function(j: IJSCodeshift, ast: INode): INode { +export default function(j: JSCodeshift, ast: Node): Node { /** * * Remove the loader with name `name` from the given NodePath @@ -21,14 +23,16 @@ export default function(j: IJSCodeshift, ast: INode): INode { * @returns {void} */ - function removeLoaderByName(path: INode, name: string): void { - const loadersNode: INode = path.value.value; + function removeLoaderByName(path: Node, name: string): void { + const loadersNode = (path.value as Node).value as Node; switch (loadersNode.type) { case j.ArrayExpression.name: { - const loaders: string[] = loadersNode.elements.map((p: INode): string => { - return utils.safeTraverse(p, ["properties", "0", "value", "value"]); - }); + const loaders: Node[] = loadersNode.elements.map( + (p: Node): Node => { + return utils.safeTraverse(p, ["properties", "0", "value", "value"]) as Node; + } + ); const loaderIndex: number = loaders.indexOf(name); if (loaders.length && loaderIndex > -1) { @@ -55,19 +59,19 @@ export default function(j: IJSCodeshift, ast: INode): INode { } } - function removeLoaders(astNode: INode) { + function removeLoaders(astNode: Node): void { astNode .find(j.Property, { key: { name: "use" } }) - .forEach((path: INode): void => removeLoaderByName(path, "json-loader")); + .forEach((path: Node): void => removeLoaderByName(path, "json-loader")); astNode .find(j.Property, { key: { name: "loader" } }) - .forEach((path: INode): void => removeLoaderByName(path, "json-loader")); + .forEach((path: Node): void => removeLoaderByName(path, "json-loader")); } - const transforms: Array<(astNode: INode) => void> = [removeLoaders]; + const transforms: TransformCallback[] = [removeLoaders]; - transforms.forEach((t: (astNode: INode) => void): void => t(ast)); + transforms.forEach((t: TransformCallback): void => t(ast)); return ast; } diff --git a/packages/migrate/resolve/resolve.ts b/packages/migrate/resolve/resolve.ts index c79f5cca4c3..be60e4e69b0 100644 --- a/packages/migrate/resolve/resolve.ts +++ b/packages/migrate/resolve/resolve.ts @@ -1,4 +1,4 @@ -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; /** * @@ -9,22 +9,19 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @returns {Node} ast - jscodeshift ast */ -export default function transformer(j: IJSCodeshift, ast: INode): INode { - - const getRootVal = (p: INode): INode => { - return p.node.value.properties.filter((prop: INode): boolean => prop.key.name === "root")[0]; +export default function transformer(j: JSCodeshift, ast: Node): Node { + const getRootVal = (p: Node): Node => { + return (p.node.value as Node).properties.filter((prop: Node): boolean => prop.key.name === "root")[0]; }; - const getRootIndex = (p: INode): number => { - return p.node.value.properties.reduce((rootIndex: number, prop: INode, index: number): number => { + const getRootIndex = (p: Node): number => { + return (p.node.value as Node).properties.reduce((rootIndex: number, prop: Node, index: number): number => { return prop.key.name === "root" ? index : rootIndex; }, -1); }; - const isModulePresent = (p: INode): INode | false => { - const modules: INode[] = p.node.value.properties.filter( - (prop: INode): boolean => prop.key.name === "modules", - ); + const isModulePresent = (p: Node): Node | false => { + const modules: Node[] = (p.node.value as Node).properties.filter((prop: Node): boolean => prop.key.name === "modules"); return modules.length > 0 && modules[0]; }; @@ -38,46 +35,42 @@ export default function transformer(j: IJSCodeshift, ast: INode): INode { * @returns {Node} ast - ast node */ - const createModuleArray = (p: INode): INode => { - - const rootVal: INode = getRootVal(p); + const createModuleArray = (p: Node): Node => { + const rootVal: Node = getRootVal(p); - let modulesVal: INode[] = null; + let modulesVal: Node[] = null; - if (rootVal.value.type === "ArrayExpression") { - modulesVal = rootVal.value.elements; + if ((rootVal.value as Node).type === "ArrayExpression") { + modulesVal = (rootVal.value as Node).elements; } else { - modulesVal = [rootVal.value]; + modulesVal = [rootVal.value as Node]; } - let module: INode | false = isModulePresent(p); + let module: Node | false = isModulePresent(p); if (!module) { - module = j.property( - "init", - j.identifier("modules"), - j.arrayExpression(modulesVal), - ); - p.node.value.properties = p.node.value.properties.concat([module]); + module = j.property("init", j.identifier("modules"), j.arrayExpression(modulesVal)); + (p.node.value as Node).properties = (p.node.value as Node).properties.concat([module]); } else { - module.value.elements = module.value.elements.concat(modulesVal); + (module.value as Node).elements = (module.value as Node).elements.concat(modulesVal); } const rootIndex: number = getRootIndex(p); - p.node.value.properties.splice(rootIndex, 1); + (p.node.value as Node).properties.splice(rootIndex, 1); return p; }; return ast .find(j.Property) - .filter((p: INode): boolean => { - return ( - p.node.key.name === "resolve" && - p.node.value.properties.filter((prop: INode): boolean => prop.key.name === "root") - .length === 1 - ); - }) + .filter( + (p: Node): boolean => { + return ( + p.node.key.name === "resolve" && + (p.node.value as Node).properties.filter((prop: Node): boolean => prop.key.name === "root").length === 1 + ); + } + ) .forEach(createModuleArray); } diff --git a/packages/migrate/types/NodePath.ts b/packages/migrate/types/NodePath.ts index e40bdf3cba8..1b828378ddb 100644 --- a/packages/migrate/types/NodePath.ts +++ b/packages/migrate/types/NodePath.ts @@ -1,100 +1,99 @@ -export interface INode extends Object { +export interface Node extends Object { id?: { name: string; }; - arguments?: INode[]; - body?: INode[]; - elements?: INode[]; + arguments?: Node[]; + body?: Node[]; + elements?: Node[]; expression?: { left: { - computed: boolean, - object: INode, - property: INode, - type: string, - }, - operator: string, - right: INode, - type: string, + computed: boolean; + object: Node; + property: Node; + type: string; + }; + operator: string; + right: Node; + type: string; + value?: string; }; - filter?: (p: (p: INode) => boolean) => INode; - find?: (objectExpression: object, filterExpression?: object) => INode; - forEach?: (p: (p: INode) => void) => INode; - get?: (property: string) => INode; - remove?: (_?: void) => void; - nodes?: (_?: void) => INode[]; - pop?: (_?: void) => INode; + filter?: (p: (p: Node) => boolean) => Node; + find?: (objectExpression: object, filterExpression?: object) => Node; + forEach?: (p: (p: Node) => void) => Node; + get?: (property: string) => Node; + remove?: () => void; + nodes?: () => Node[]; + pop?: () => Node; key?: { name: string; - value: INode | string; + value: Node | string; }; - node?: INode; + node?: Node; name?: string; object?: object; - parent?: INode; - properties?: INode[]; - property?: INode; + parent?: Node; + properties?: Node[]; + property?: Node; prune?: Function; - replaceWith?: (objectExpression: object) => INode; - size?: (_?: void) => number; + replaceWith?: (objectExpression: object) => Node; + size?: () => number; type?: string; - value?: INode | string | any; - toSource?: (object: { - quote?: string, - }) => string; + value?: Node | string | Node[]; + toSource?: ( + object: { + quote?: string; + } + ) => string; source?: string; - ast?: INode; - rules?: IModuleRule[]; - __paths?: INode[]; + ast?: Node; + rules?: ModuleRule[]; + + declarations?: Node[]; + + __paths?: Node[]; } -interface IModuleRule { +interface ModuleRule { loader?: string; } -interface IExpressionObject { +interface ExpressionObject { name?: string; } -export interface IJSCodeshift extends Object { - (source?: INode | string): INode; - withParser?: (parser: string) => IJSCodeshift; - identifier?: (key: string) => INode; - literal?: (key: valueType) => INode; - memberExpression?: (node1: INode, node2: INode, bool?: boolean) => INode; - objectProperty?: (key: INode, property: valueType) => INode; - objectExpression?: (properties: INode[]) => INode; - newExpression?: (expression: INode, args: INode[]) => INode; - callExpression?: (expression: INode, args: INode[]) => INode; - variableDeclarator?: (key: INode, args: INode) => INode; - variableDeclaration?: (key: string, args: INode[]) => INode; - arrayExpression?: (args?: INode[]) => INode; - property?: (type: string, key: INode, value: INode) => INode; - program?: (nodes: INode[]) => INode; - booleanLiteral?: (bool: boolean) => INode; - arrowFunctionExpression?: (params: INode[], body: INode, exp: INode) => INode; - blockStatement?: (body: INode[]) => INode; - ifStatement?: (test: INode, consequent: INode, alternate?: INode) => INode; - returnStatement?: (arg: INode) => INode; - binaryExpression?: (operator: string, left: INode, right: INode) => INode; - - Property?: IExpressionObject; - NewExpression?: IExpressionObject; - CallExpression?: IExpressionObject; - VariableDeclarator?: IExpressionObject; - Identifier?: IExpressionObject; - Literal?: IExpressionObject; - ArrayExpression?: IExpressionObject; - MemberExpression?: IExpressionObject; - FunctionExpression?: IExpressionObject; - ObjectExpression?: IExpressionObject; - BlockStatement?: IExpressionObject; - Program?: IExpressionObject; - ArrowFunctionExpression?: IExpressionObject; +export interface JSCodeshift extends Object { + (source?: Node | string): Node; + withParser?: (parser: string) => JSCodeshift; + identifier?: (key: string) => Node; + literal?: (key: valueType) => Node; + memberExpression?: (node1: Node, node2: Node, bool?: boolean) => Node; + objectProperty?: (key: Node, property: valueType) => Node; + objectExpression?: (properties: Node[]) => Node; + newExpression?: (expression: Node, args: Node[]) => Node; + callExpression?: (expression: Node, args: Node[]) => Node; + variableDeclarator?: (key: Node, args: Node) => Node; + variableDeclaration?: (key: string, args: Node[]) => Node; + arrayExpression?: (args?: Node[]) => Node; + property?: (type: string, key: Node, value: Node) => Node; + program?: (nodes: Node[]) => Node; + booleanLiteral?: (bool: boolean) => Node; + Property?: ExpressionObject; + NewExpression?: ExpressionObject; + CallExpression?: ExpressionObject; + VariableDeclarator?: ExpressionObject; + Identifier?: ExpressionObject; + Literal?: ExpressionObject; + ArrayExpression?: ExpressionObject; + MemberExpression?: ExpressionObject; + FunctionExpression?: ExpressionObject; + ObjectExpression?: ExpressionObject; + BlockStatement?: ExpressionObject; + Program?: ExpressionObject; filters?: { VariableDeclarator: { - requiresModule: Function, - }, + requiresModule: Function; + }; }; } -export type valueType = string | number | boolean | any[] | INode | null; +export type valueType = string | number | boolean | Node | null; diff --git a/packages/migrate/uglifyJsPlugin/uglifyJsPlugin.ts b/packages/migrate/uglifyJsPlugin/uglifyJsPlugin.ts index ef74dec417f..71980715a60 100644 --- a/packages/migrate/uglifyJsPlugin/uglifyJsPlugin.ts +++ b/packages/migrate/uglifyJsPlugin/uglifyJsPlugin.ts @@ -3,10 +3,10 @@ import { findPluginsArrayAndRemoveIfEmpty, findPluginsByName, getRequire, - safeTraverse, + safeTraverse } from "@webpack-cli/utils/ast-utils"; -import { IJSCodeshift, INode } from "../types/NodePath"; +import { JSCodeshift, Node } from "../types/NodePath"; /** * @@ -23,36 +23,33 @@ import { IJSCodeshift, INode } from "../types/NodePath"; * @returns {Node} ast - jscodeshift ast */ -export default function(j: IJSCodeshift, ast: INode): INode { - +export default function(j: JSCodeshift, ast: Node): Node { let pluginVariableAssignment: string = null; - const searchForRequirePlugin: INode = ast + const searchForRequirePlugin: Node = ast .find(j.VariableDeclarator) - .filter( - j.filters.VariableDeclarator.requiresModule("uglifyjs-webpack-plugin"), - ); + .filter(j.filters.VariableDeclarator.requiresModule("uglifyjs-webpack-plugin")); /** * Look for a variable declaration which requires uglifyjs-webpack-plugin * saves the name of this variable. */ - searchForRequirePlugin.forEach((node: INode): void => { - pluginVariableAssignment = node.value.id.name; - }); + searchForRequirePlugin.forEach( + (node: Node): void => { + pluginVariableAssignment = (node.value as Node).id.name; + } + ); - pluginVariableAssignment = !pluginVariableAssignment - ? "webpack.optimize.UglifyJsPlugin" - : pluginVariableAssignment; + pluginVariableAssignment = !pluginVariableAssignment ? "webpack.optimize.UglifyJsPlugin" : pluginVariableAssignment; - findPluginsByName(j, ast, [pluginVariableAssignment]) - .forEach((node: INode): void => { + findPluginsByName(j, ast, [pluginVariableAssignment]).forEach( + (node: Node): void => { let expressionContent: object = null; - const configBody: INode = safeTraverse(node, ["parent", "parent", "parent"]); + const configBody = safeTraverse(node, ["parent", "parent", "parent"]); // options passed to plugin - const pluginOptions: INode[] = node.value.arguments; + const pluginOptions: Node[] = (node.value as Node).arguments; /** * check if there are any options passed to UglifyWebpackPlugin @@ -61,70 +58,55 @@ export default function(j: IJSCodeshift, ast: INode): INode { */ if (pluginOptions.length) { /* - * If user is using UglifyJsPlugin directly from webpack - * transformation must: - * - remove it - * - add require for terser-webpack-plugin - * - add to minimizer - */ + * If user is using UglifyJsPlugin directly from webpack + * transformation must: + * - remove it + * - add require for terser-webpack-plugin + * - add to minimizer + */ if (pluginVariableAssignment) { // remove require for uglify-webpack-plugin searchForRequirePlugin.remove(); // create require for terser-webpack-plugin - const pathRequire: INode = getRequire( - j, - "TerserPlugin", - "terser-webpack-plugin", - ); + const pathRequire: Node = getRequire(j, "TerserPlugin", "terser-webpack-plugin"); // prepend to source code. - ast - .find(j.Program) - .replaceWith((p: INode): INode => - j.program([].concat(pathRequire).concat(p.value.body)), - ); + ast.find(j.Program).replaceWith( + (p: Node): Node => j.program([].concat(pathRequire).concat((p.value as Node).body)) + ); expressionContent = j.property( "init", j.identifier("minimizer"), - j.arrayExpression([ - j.newExpression(j.identifier("TerserPlugin"), [pluginOptions[0]]), - ]), + j.arrayExpression([j.newExpression(j.identifier("TerserPlugin"), [pluginOptions[0]])]) ); } else { - expressionContent = j.property( - "init", - j.identifier("minimizer"), - j.arrayExpression([node.value]), - ); + expressionContent = j.property("init", j.identifier("minimizer"), j.arrayExpression([node.value as Node])); } } else { - searchForRequirePlugin.forEach((n: INode): void => j(n).remove()); + searchForRequirePlugin.forEach((n: Node): void => j(n).remove()); } const minimizeProperty = createProperty(j, "minimize", "true"); // creates optimization property at the body of the config. if (expressionContent) { - configBody.value.properties.push( + ((configBody as Node).value as Node).properties.push( j.property( "init", j.identifier("optimization"), - j.objectExpression([minimizeProperty, expressionContent]), - ), + j.objectExpression([minimizeProperty, expressionContent]) + ) ); } else { - configBody.value.properties.push( - j.property( - "init", - j.identifier("optimization"), - j.objectExpression([minimizeProperty]), - ), + ((configBody as Node).value as Node).properties.push( + j.property("init", j.identifier("optimization"), j.objectExpression([minimizeProperty])) ); } // remove the old Uglify plugin from Plugins array. j(node).remove(); - }); + } + ); findPluginsArrayAndRemoveIfEmpty(j, ast); diff --git a/packages/remove/.eslintrc b/packages/remove/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/remove/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/serve/.eslintrc b/packages/serve/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/serve/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/serve/index.ts b/packages/serve/index.ts index 950ae78d3b7..8ae5a867368 100644 --- a/packages/serve/index.ts +++ b/packages/serve/index.ts @@ -17,7 +17,7 @@ import { List } from "@webpack-cli/webpack-scaffold"; const spawnNPMWithArg = (cmd: string): SpawnSyncReturns => spawn.sync("npm", ["install", "webpack-dev-server", cmd], { - stdio: "inherit", + stdio: "inherit" }); /** @@ -30,7 +30,7 @@ const spawnNPMWithArg = (cmd: string): SpawnSyncReturns => const spawnYarnWithArg = (cmd: string): SpawnSyncReturns => spawn.sync("yarn", ["add", "webpack-dev-server", cmd], { - stdio: "inherit", + stdio: "inherit" }); /** @@ -51,40 +51,34 @@ const getRootPathModule = (dep: string): string => path.resolve(process.cwd(), d * @returns {Function} invokes the devServer API */ -export default function serve(...args: string[]) { +export default function serve(): Promise { const packageJSONPath = getRootPathModule("package.json"); if (!packageJSONPath) { - console.error( - "\n", - chalk.red("✖ Could not find your package.json file"), - "\n", - ); + console.error("\n", chalk.red("✖ Could not find your package.json file"), "\n"); process.exit(1); } + // TODO: to refactor this dynamic require and use import() + // eslint-disable-next-line const packageJSON: object = require(packageJSONPath); /* * We gotta do this, cause some configs might not have devdep, * dep or optional dep, so we'd need sanity checks for each - */ + */ const hasDevServerDep: string[] = packageJSON - ? Object.keys(packageJSON).filter((p: string) => packageJSON[p]["webpack-dev-server"]) + ? Object.keys(packageJSON).filter((p: string): boolean => packageJSON[p]["webpack-dev-server"]) : []; if (hasDevServerDep.length) { - const WDSPath: string = getRootPathModule( - "node_modules/webpack-dev-server/bin/webpack-dev-server.js", - ); + const WDSPath: string = getRootPathModule("node_modules/webpack-dev-server/bin/webpack-dev-server.js"); if (!WDSPath) { console.error( "\n", - chalk.red( - "✖ Could not find the webpack-dev-server dependency in node_modules root path", - ), + chalk.red("✖ Could not find the webpack-dev-server dependency in node_modules root path") ); console.info( chalk.bold.green(" ✔︎"), "Try this command:", - chalk.bold.green("rm -rf node_modules && npm install"), + chalk.bold.green("rm -rf node_modules && npm install") ); process.exit(1); } @@ -92,14 +86,12 @@ export default function serve(...args: string[]) { } else { process.stdout.write( "\n" + - chalk.bold( - "✖ We didn't find any webpack-dev-server dependency in your project,", - ) + + chalk.bold("✖ We didn't find any webpack-dev-server dependency in your project,") + "\n" + chalk.bold.green(" 'webpack serve'") + " " + chalk.bold("requires you to have it installed ") + - "\n\n", + "\n\n" ); return inquirer .prompt([ @@ -107,65 +99,68 @@ export default function serve(...args: string[]) { default: "Y", message: "Do you want to install it? (default: Y)", name: "confirmDevserver", - type: "confirm", - }, + type: "confirm" + } ]) - .then((answer: { - confirmDevserver: boolean, - }) => { - if (answer.confirmDevserver) { - return inquirer - .prompt( - List( - "confirmDepType", - "What kind of dependency do you want it to be under? (default: devDependency)", - ["devDependency", "optionalDependency", "dependency"], - ), - ) - .then((depTypeAns: { - confirmDepType: string; - }) => { - const packager: string = getRootPathModule("package-lock.json") - ? "npm" - : "yarn"; - let spawnAction: (_?: void) => SpawnSyncReturns; - if (depTypeAns.confirmDepType === "devDependency") { - if (packager === "yarn") { - spawnAction = (_?: void) => spawnYarnWithArg("--dev"); - } else { - spawnAction = (_?: void) => spawnNPMWithArg("--save-dev"); - } - } - if (depTypeAns.confirmDepType === "dependency") { - if (packager === "yarn") { - spawnAction = (_?: void) => spawnYarnWithArg(" "); - } else { - spawnAction = (_?: void) => spawnNPMWithArg("--save"); + .then( + (answer: { confirmDevserver: boolean }): Promise => { + if (answer.confirmDevserver) { + return inquirer + .prompt( + List( + "confirmDepType", + "What kind of dependency do you want it to be under? (default: devDependency)", + ["devDependency", "optionalDependency", "dependency"] + ) + ) + .then( + (depTypeAns: { confirmDepType: string }): Promise => { + const packager: string = getRootPathModule("package-lock.json") ? "npm" : "yarn"; + let spawnAction: () => SpawnSyncReturns; + if (depTypeAns.confirmDepType === "devDependency") { + if (packager === "yarn") { + spawnAction = (): SpawnSyncReturns => spawnYarnWithArg("--dev"); + } else { + spawnAction = (): SpawnSyncReturns => spawnNPMWithArg("--save-dev"); + } + } + if (depTypeAns.confirmDepType === "dependency") { + if (packager === "yarn") { + spawnAction = (): SpawnSyncReturns => spawnYarnWithArg(" "); + } else { + spawnAction = (): SpawnSyncReturns => spawnNPMWithArg("--save"); + } + } + if (depTypeAns.confirmDepType === "optionalDependency") { + if (packager === "yarn") { + spawnAction = (): SpawnSyncReturns => + spawnYarnWithArg("--optional"); + } else { + spawnAction = (): SpawnSyncReturns => + spawnNPMWithArg("--save-optional"); + } + } + return processPromise(spawnAction()).then( + (): Promise => { + // Recursion doesn't work well with require call being cached + delete require.cache[require.resolve(packageJSONPath)]; + return serve(); + } + ); } - } - if (depTypeAns.confirmDepType === "optionalDependency") { - if (packager === "yarn") { - spawnAction = (_?: void) => spawnYarnWithArg("--optional"); - } else { - spawnAction = (_?: void) => spawnNPMWithArg("--save-optional"); - } - } - return processPromise(spawnAction()) - .then((_: void) => { - // Recursion doesn't work well with require call being cached - delete require.cache[require.resolve(packageJSONPath)]; - return serve(); - }); - }); - } else { - console.error(chalk.bold.red("✖ Serve aborted due cancelling")); + ); + } else { + console.error(chalk.bold.red("✖ Serve aborted due cancelling")); + process.exitCode = 1; + } + } + ) + .catch( + (err: object): void => { + console.error(chalk.red("✖ Serve aborted due to some errors")); + console.error(err); process.exitCode = 1; } - }) - .catch((err: object) => { - console.error(chalk.red("✖ Serve aborted due to some errors")); - console.error(err); - process.exitCode = 1; - }); + ); } } diff --git a/packages/update/.eslintrc b/packages/update/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/update/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/utils/.eslintrc b/packages/utils/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/utils/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/utils/__tests__/.eslintrc b/packages/utils/__tests__/.eslintrc new file mode 100644 index 00000000000..5d4340a351d --- /dev/null +++ b/packages/utils/__tests__/.eslintrc @@ -0,0 +1,7 @@ +{ + "root": true, + "extends": ["../.eslintrc"], + "rules": { + "@typescript-eslint/explicit-function-return-type": ["off"] + } +} diff --git a/packages/utils/__tests__/ast-utils.test.ts b/packages/utils/__tests__/ast-utils.test.ts index b9c8e34576c..6883531049f 100644 --- a/packages/utils/__tests__/ast-utils.test.ts +++ b/packages/utils/__tests__/ast-utils.test.ts @@ -2,7 +2,7 @@ import * as j from "jscodeshift/dist/core"; import * as utils from "../ast-utils"; -import { INode } from "../types/NodePath"; +import { Node } from "../types/NodePath"; describe("utils", () => { describe("createProperty", () => { @@ -106,7 +106,7 @@ const a = { plugs: [] } describe("createOrUpdatePluginByName", () => { it("should create a new plugin without arguments", () => { const ast = j("{ plugins: [] }"); - ast.find(j.ArrayExpression).forEach((node: INode) => { + ast.find(j.ArrayExpression).forEach((node: Node) => { utils.createOrUpdatePluginByName(j, node, "Plugin"); }); expect(ast.toSource()).toMatchSnapshot(); @@ -114,9 +114,9 @@ const a = { plugs: [] } it("should create a new plugin with arguments", () => { const ast = j("{ plugins: [] }"); - ast.find(j.ArrayExpression).forEach((node: INode) => { + ast.find(j.ArrayExpression).forEach((node: Node) => { utils.createOrUpdatePluginByName(j, node, "Plugin", { - foo: "bar", + foo: "bar" }); }); expect(ast.toSource()).toMatchSnapshot(); @@ -124,9 +124,9 @@ const a = { plugs: [] } it("should add an object as an argument", () => { const ast = j("[new Plugin()]"); - ast.find(j.ArrayExpression).forEach((node: INode) => { + ast.find(j.ArrayExpression).forEach((node: Node) => { utils.createOrUpdatePluginByName(j, node, "Plugin", { - foo: true, + foo: true }); }); expect(ast.toSource()).toMatchSnapshot(); @@ -134,13 +134,13 @@ const a = { plugs: [] } it("should merge options objects", () => { const ast = j("[new Plugin({ foo: true })]"); - ast.find(j.ArrayExpression).forEach((node: INode) => { + ast.find(j.ArrayExpression).forEach((node: Node) => { utils.createOrUpdatePluginByName(j, node, "Plugin", { bar: "baz", - foo: false, + foo: false }); utils.createOrUpdatePluginByName(j, node, "Plugin", { - "baz-long": true, + "baz-long": true }); }); expect(ast.toSource()).toMatchSnapshot(); @@ -192,8 +192,8 @@ const a = { plugs: [] } expect( ast .find(j.ObjectExpression) - .filter((p) => utils.findObjWithOneOfKeys(p, ["a"])) - .size(), + .filter(p => utils.findObjWithOneOfKeys(p, ["a"])) + .size() ).toEqual(1); }); }); @@ -208,12 +208,12 @@ const a = { plugs: [] } describe("safeTraverse", () => { it("should safe traverse", () => { const testObject = { - type: "NodeType", + type: "NodeType" }; const p = { foo: { - bar: testObject, - }, + bar: testObject + } }; const require = utils.safeTraverse(p, ["foo", "bar"]); expect(require).toEqual(testObject); @@ -221,14 +221,14 @@ const a = { plugs: [] } it("should safe traverse thrice", () => { const type = { - type: "NodeType", + type: "NodeType" }; const p = { parent: { value: { - value: type, - }, - }, + value: type + } + } }; const traversedValue = utils.safeTraverse(p, ["parent", "value", "value"]); expect(traversedValue).toEqual(type); @@ -241,9 +241,9 @@ const a = { plugs: [] } const p = { value: { value: { - type: NODE_TYPE, - }, - }, + type: NODE_TYPE + } + } }; const typeValue = utils.safeTraverseAndGetType(p); expect(typeValue).toEqual(NODE_TYPE); @@ -254,9 +254,9 @@ const a = { plugs: [] } const p = { foo: { bar: { - type: NODE_TYPE, - }, - }, + type: NODE_TYPE + } + } }; const typeValue = utils.safeTraverseAndGetType(p); expect(typeValue).toEqual(false); @@ -273,14 +273,14 @@ const a = { plugs: [] } super: [ "yeah", { - loader: "'eslint-loader'", - }, - ], + loader: "'eslint-loader'" + } + ] }; const root = ast.find(j.ObjectExpression); - root.forEach((p) => { + root.forEach(p => { utils.addProperty(j, p, "entry", propertyValue); }); @@ -306,14 +306,14 @@ const a = { plugs: [] } super: [ "yeah", { - loader: "'eslint-loader'", - }, - ], + loader: "'eslint-loader'" + } + ] }; const root = ast.find(j.ObjectExpression); - utils.findRootNodesByName(j, root, "entry").forEach((p: INode) => { + utils.findRootNodesByName(j, root, "entry").forEach((p: Node) => { j(p).replaceWith(utils.addProperty(j, p, "entry", propertyValue, "add")); }); diff --git a/packages/utils/__tests__/package-manager.test.ts b/packages/utils/__tests__/package-manager.test.ts index 036f10604bb..baa4204822a 100644 --- a/packages/utils/__tests__/package-manager.test.ts +++ b/packages/utils/__tests__/package-manager.test.ts @@ -7,7 +7,9 @@ jest.mock("cross-spawn"); jest.mock("fs"); describe("package-manager", () => { + // eslint-disable-next-line const spawn = require("cross-spawn"); + // eslint-disable-next-line const fs = require("fs"); const defaultSyncResult = { @@ -50,7 +52,7 @@ describe("package-manager", () => { fs.existsSync.mockReturnValueOnce(true); } - spawn.sync.mockReturnValue(defaultSyncResult); + jest.spyOn(spawn, "sync").mockReturnValue(defaultSyncResult); it("should return 'yarn' from getPackageManager if it's installed", () => { expect(packageManager.getPackageManager()).toEqual("yarn"); diff --git a/packages/utils/__tests__/resolve-packages.test.ts b/packages/utils/__tests__/resolve-packages.test.ts index d571dc01762..765b907f1d2 100644 --- a/packages/utils/__tests__/resolve-packages.test.ts +++ b/packages/utils/__tests__/resolve-packages.test.ts @@ -7,7 +7,7 @@ function mockPromise(value) { const mockedPromise = { then(callback) { return mockPromise(callback(value)); - }, + } }; return isValueAPromise ? value : mockedPromise; @@ -18,14 +18,14 @@ function spawnChild(pkg) { function getLoc(option) { const packageModule = []; - option.filter((pkg) => { - mockPromise(spawnChild(pkg)).then((_) => { + option.filter(pkg => { + mockPromise(spawnChild(pkg)).then(() => { try { const loc = path.join("..", "..", "node_modules", pkg); packageModule.push(loc); } catch (err) { throw new Error( - "Package wasn't validated correctly.." + "Submit an issue for " + pkg + " if this persists", + "Package wasn't validated correctly.." + "Submit an issue for " + pkg + " if this persists" ); } }); @@ -60,7 +60,7 @@ describe("resolve-packages", () => { moduleLoc = getLoc(["webpack-scaffold-ylvis", "webpack-scaffold-noop"]); expect(moduleLoc).toEqual([ path.normalize("../../node_modules/webpack-scaffold-ylvis"), - path.normalize("../../node_modules/webpack-scaffold-noop"), + path.normalize("../../node_modules/webpack-scaffold-noop") ]); }); }); diff --git a/packages/utils/ast-utils.ts b/packages/utils/ast-utils.ts index 53e2e60e685..7a6fed5a657 100644 --- a/packages/utils/ast-utils.ts +++ b/packages/utils/ast-utils.ts @@ -1,4 +1,4 @@ -import { IJSCodeshift, INode, valueType } from "./types/NodePath"; +import { JSCodeshift, Node, valueType } from "./types/NodePath"; import * as validateIdentifier from "./validate-identifier"; /** @@ -6,11 +6,13 @@ import * as validateIdentifier from "./validate-identifier"; * Traverse safely over a path object for array for paths * @param {Object} obj - Object on which we traverse * @param {Array} paths - Array of strings containing the traversal path - * @returns {Any} Value at given traversal path + * @returns {Node} Value at given traversal path */ -function safeTraverse(obj: INode, paths: string[]): any { - let val: INode = obj; +function safeTraverse(obj: Node, paths: string[]): Node | Node[] { + // TODO: to revisit the type of this function, it's not clear what should return. + // Sometimes its return type is used as string + let val: Node = obj; let idx = 0; while (idx < paths.length) { @@ -30,8 +32,8 @@ function safeTraverse(obj: INode, paths: string[]): any { * @returns {String|Boolean} type at given path. */ -function safeTraverseAndGetType(path: INode): string | boolean { - const pathValue: INode = safeTraverse(path, ["value", "value"]); +function safeTraverseAndGetType(path: Node): string | boolean { + const pathValue = safeTraverse(path, ["value", "value"]) as Node; return pathValue ? pathValue.type : false; } @@ -41,17 +43,15 @@ function safeTraverseAndGetType(path: INode): string | boolean { * @returns {String} member expression string. */ -function memberExpressionToPathString(path: INode): string { +function memberExpressionToPathString(path: Node): string { if (path && path.object) { - return [memberExpressionToPathString(path.object), path.property.name].join( - ".", - ); + return [memberExpressionToPathString(path.object), path.property.name].join("."); } return path.name; } // Convert Array like ['webpack', 'optimize', 'DedupePlugin'] to nested MemberExpressions -function pathsToMemberExpression(j: IJSCodeshift, paths: string[]): INode { +function pathsToMemberExpression(j: JSCodeshift, paths: string[]): Node { if (!paths.length) { return null; } else if (paths.length === 1) { @@ -59,10 +59,7 @@ function pathsToMemberExpression(j: IJSCodeshift, paths: string[]): INode { } else { const first: string[] = paths.slice(0, 1); const rest: string[] = paths.slice(1); - return j.memberExpression( - pathsToMemberExpression(j, rest), - pathsToMemberExpression(j, first), - ); + return j.memberExpression(pathsToMemberExpression(j, rest), pathsToMemberExpression(j, first)); } } @@ -77,15 +74,14 @@ function pathsToMemberExpression(j: IJSCodeshift, paths: string[]): INode { * @returns {Node} Node that has the pluginName */ -function findPluginsByName(j: IJSCodeshift, node: INode, pluginNamesArray: string[]): INode { - return node - .find(j.NewExpression) - .filter((path: INode): boolean => { +function findPluginsByName(j: JSCodeshift, node: Node, pluginNamesArray: string[]): Node { + return node.find(j.NewExpression).filter( + (path: Node): boolean => { return pluginNamesArray.some( - (plugin: string) => - memberExpressionToPathString(path.get("callee").value) === plugin, + (plugin: string): boolean => memberExpressionToPathString(path.get("callee").value as Node) === plugin ); - }); + } + ); } /** @@ -95,20 +91,15 @@ function findPluginsByName(j: IJSCodeshift, node: INode, pluginNamesArray: strin * @returns {Node} rootNode modified AST. */ -function findPluginsArrayAndRemoveIfEmpty(j: IJSCodeshift, rootNode: INode): INode { - return rootNode - .find(j.Identifier, { name: "plugins" }) - .forEach((node: INode) => { - const elements: INode[] = safeTraverse(node, [ - "parent", - "value", - "value", - "elements", - ]); +function findPluginsArrayAndRemoveIfEmpty(j: JSCodeshift, rootNode: Node): Node { + return rootNode.find(j.Identifier, { name: "plugins" }).forEach( + (node: Node): void => { + const elements = safeTraverse(node, ["parent", "value", "value", "elements"]) as Node[]; if (!elements.length) { j(node.parent).remove(); } - }); + } + ); } /** @@ -121,51 +112,10 @@ function findPluginsArrayAndRemoveIfEmpty(j: IJSCodeshift, rootNode: INode): INo * @returns {Node} found node and */ -function findRootNodesByName(j: IJSCodeshift, node: INode, propName: string): INode { +function findRootNodesByName(j: JSCodeshift, node: Node, propName: string): Node { return node.find(j.Property, { key: { name: propName } }); } -/** - * - * Creates an Object's property with a given key and value - * - * @param {any} j — jscodeshift API - * @param {String | Number} key - Property key - * @param {String | Number | Boolean} value - Property value - * @returns {Node} - */ - -function createProperty(j: IJSCodeshift, key: string | number, value: valueType): INode { - return j.property( - "init", - createIdentifierOrLiteral(j, key), - createLiteral(j, value), - ); -} - -/** - * - * Creates an appropriate literal property - * - * @param {any} j — jscodeshift API - * @param {String | Boolean | Number} val - * @returns {Node} - */ - -function createLiteral(j: IJSCodeshift, val: valueType): INode { - let literalVal: valueType = val; - // We'll need String to native type conversions - if (typeof val === "string") { - // 'true' => true - if (val === "true") { literalVal = true; } - // 'false' => false - if (val === "false") { literalVal = false; } - // '1' => 1 - if (!isNaN(Number(val))) { literalVal = Number(val); } - } - return j.literal(literalVal); -} - /** * * Creates an appropriate identifier or literal property @@ -175,12 +125,12 @@ function createLiteral(j: IJSCodeshift, val: valueType): INode { * @returns {Node} */ -function createIdentifierOrLiteral(j: IJSCodeshift, val: valueType): INode { +function createIdentifierOrLiteral(j: JSCodeshift, val: valueType): Node { // IPath | IPath doesn't work, find another way let literalVal = val; // We'll need String to native type conversions if (!Array.isArray(val)) { - if (typeof val === "string" || typeof val === "object" && val.__paths) { + if (typeof val === "string" || (typeof val === "object" && val.__paths)) { // 'true' => true if (val === "true") { literalVal = true; @@ -197,7 +147,7 @@ function createIdentifierOrLiteral(j: IJSCodeshift, val: valueType): INode { return j.literal(literalVal); } if (typeof val === "object" && val.__paths) { - const regExpVal = val.__paths[0].value.program.body[0].expression; + const regExpVal = ((val.__paths[0].value as JSCodeshift).program as Node).body[0].expression; return j.literal(regExpVal.value); } else if (typeof literalVal === "string") { // Use identifier instead @@ -214,6 +164,49 @@ function createIdentifierOrLiteral(j: IJSCodeshift, val: valueType): INode { return j.literal(literalVal); } +/** + * + * Creates an appropriate literal property + * + * @param {any} j — jscodeshift API + * @param {String | Boolean | Number} val + * @returns {Node} + */ + +function createLiteral(j: JSCodeshift, val: valueType): Node { + let literalVal: valueType = val; + // We'll need String to native type conversions + if (typeof val === "string") { + // 'true' => true + if (val === "true") { + literalVal = true; + } + // 'false' => false + if (val === "false") { + literalVal = false; + } + // '1' => 1 + if (!isNaN(Number(val))) { + literalVal = Number(val); + } + } + return j.literal(literalVal); +} + +/** + * + * Creates an Object's property with a given key and value + * + * @param {any} j — jscodeshift API + * @param {String | Number} key - Property key + * @param {String | Number | Boolean} value - Property value + * @returns {Node} + */ + +function createProperty(j: JSCodeshift, key: string | number, value: valueType): Node { + return j.property("init", createIdentifierOrLiteral(j, key), createLiteral(j, value)); +} + /** * * Adds or updates the value of a key within a root @@ -228,35 +221,32 @@ function createIdentifierOrLiteral(j: IJSCodeshift, val: valueType): INode { */ function addOrUpdateConfigObject( - j: IJSCodeshift, rootNode: INode, configProperty: string, key: string, value: valueType, + j: JSCodeshift, + rootNode: Node, + configProperty: string, + key: string, + value: valueType ): void { - - const propertyExists = rootNode.properties.filter( - (node: INode): boolean => node.key.name === configProperty, - ).length; + const propertyExists = rootNode.properties.filter((node: Node): boolean => node.key.name === configProperty).length; if (propertyExists) { rootNode.properties - .filter((path: INode): boolean => path.key.name === configProperty) - .forEach((path: INode) => { - const newProperties = path.value.properties.filter( - (p: INode) => p.key.name !== key, - ); - newProperties.push( - j.objectProperty( - j.identifier(key), value, - ), - ); - path.value.properties = newProperties; - }); + .filter((path: Node): boolean => path.key.name === configProperty) + .forEach( + (path: Node): void => { + const newProperties = (path.value as Node).properties.filter( + (p: Node): boolean => p.key.name !== key + ); + newProperties.push(j.objectProperty(j.identifier(key), value)); + (path.value as Node).properties = newProperties; + } + ); } else { rootNode.properties.push( j.objectProperty( j.identifier(configProperty), - j.objectExpression( - [j.objectProperty(j.identifier(key), value)], - ), - ), + j.objectExpression([j.objectProperty(j.identifier(key), value)]) + ) ); } } @@ -272,20 +262,22 @@ function addOrUpdateConfigObject( * @returns {Node | Void} - path to the root webpack configuration object if plugin is found */ -function findAndRemovePluginByName(j: IJSCodeshift, node: INode, pluginName: string): INode { - let rootPath: INode; +function findAndRemovePluginByName(j: JSCodeshift, node: Node, pluginName: string): Node { + let rootPath: Node; findPluginsByName(j, node, [pluginName]) - .filter((path: INode): boolean => safeTraverse(path, ["parent", "value"])) - .forEach((path: INode) => { - rootPath = safeTraverse(path, ["parent", "parent", "parent", "value"]); - const arrayPath: INode = path.parent.value; - if (arrayPath.elements && arrayPath.elements.length === 1) { - j(path.parent.parent).remove(); - } else { - j(path).remove(); + .filter((path: Node): boolean => !!safeTraverse(path, ["parent", "value"])) + .forEach( + (path: Node): void => { + rootPath = safeTraverse(path, ["parent", "parent", "parent", "value"]) as Node; + const arrayPath = path.parent.value as Node; + if (arrayPath.elements && arrayPath.elements.length === 1) { + j(path.parent.parent).remove(); + } else { + j(path).remove(); + } } - }); + ); return rootPath; } @@ -303,62 +295,68 @@ function findAndRemovePluginByName(j: IJSCodeshift, node: INode, pluginName: str * @returns {Void} */ -function createOrUpdatePluginByName(j: IJSCodeshift, rootNodePath: INode, pluginName: string, options?: object): void { - const pluginInstancePath: INode = findPluginsByName(j, j(rootNodePath), [ - pluginName, - ]); - let optionsProps: INode[]; +function createOrUpdatePluginByName(j: JSCodeshift, rootNodePath: Node, pluginName: string, options?: object): void { + const pluginInstancePath: Node = findPluginsByName(j, j(rootNodePath), [pluginName]); + let optionsProps: Node[]; if (options) { - optionsProps = Object.keys(options).map((key: string) => { - return createProperty(j, key, options[key]); - }); + optionsProps = Object.keys(options).map( + (key: string): Node => { + return createProperty(j, key, options[key]); + } + ); } // If plugin declaration already exist if (pluginInstancePath.size()) { - pluginInstancePath.forEach((path: INode) => { - // There are options we want to pass as argument - if (optionsProps) { - const args: INode[] = path.value.arguments; - if (args.length) { - // Plugin is called with object as arguments - // we will merge those objects - const currentProps: INode = j(path) - .find(j.ObjectExpression) - .get("properties"); - - optionsProps.forEach((opt: INode) => { - // Search for same keys in the existing object - const existingProps = j(currentProps) - .find(j.Identifier) - .filter((p: INode): boolean => opt.key.value === p.value.name); - - if (existingProps.size()) { - // Replacing values for the same key - existingProps.forEach((p: INode) => { - j(p.parent).replaceWith(opt); - }); - } else { - // Adding new key:values - currentProps.value.push(opt); - } - }); - } else { - // Plugin is called without arguments - args.push(j.objectExpression(optionsProps)); + pluginInstancePath.forEach( + (path: Node): void => { + // There are options we want to pass as argument + if (optionsProps) { + const args: Node[] = (path.value as Node).arguments; + if (args.length) { + // Plugin is called with object as arguments + // we will merge those objects + const currentProps: Node = j(path) + .find(j.ObjectExpression) + .get("properties"); + + optionsProps.forEach( + (opt: Node): void => { + // Search for same keys in the existing object + const existingProps = j(currentProps) + .find(j.Identifier) + .filter((p: Node): boolean => opt.key.value === (p.value as Node).name); + + if (existingProps.size()) { + // Replacing values for the same key + existingProps.forEach( + (p: Node): void => { + j(p.parent).replaceWith(opt); + } + ); + } else { + // Adding new key:values + (currentProps.value as Node[]).push(opt); + } + } + ); + } else { + // Plugin is called without arguments + args.push(j.objectExpression(optionsProps)); + } } } - }); + ); } else { - let argumentsArray: INode[] = []; + let argumentsArray: Node[] = []; if (optionsProps) { argumentsArray = [j.objectExpression(optionsProps)]; } const loaderPluginInstance = j.newExpression( pathsToMemberExpression(j, pluginName.split(".").reverse()), - argumentsArray, + argumentsArray ); - rootNodePath.value.elements.push(loaderPluginInstance); + (rootNodePath.value as Node).elements.push(loaderPluginInstance); } } @@ -373,12 +371,14 @@ function createOrUpdatePluginByName(j: IJSCodeshift, rootNodePath: INode, plugin * @returns {String} variable name - ex. 'const s = require(s) gives "s"` */ -function findVariableToPlugin(j: IJSCodeshift, rootNode: INode, pluginPackageName: string): string { - const moduleVarNames: INode[] = rootNode +function findVariableToPlugin(j: JSCodeshift, rootNode: Node, pluginPackageName: string): string { + const moduleVarNames: Node[] = rootNode .find(j.VariableDeclarator) .filter(j.filters.VariableDeclarator.requiresModule(pluginPackageName)) .nodes(); - if (moduleVarNames.length === 0) { return null; } + if (moduleVarNames.length === 0) { + return null; + } return moduleVarNames.pop().id.name; } @@ -390,12 +390,12 @@ function findVariableToPlugin(j: IJSCodeshift, rootNode: INode, pluginPackageNam * @returns {Boolean} */ -function isType(path: INode, type: string): boolean { +function isType(path: Node, type: string): boolean { return path.type === type; } -function findObjWithOneOfKeys(p: INode, keyNames: string[]) { - return p.value.properties.reduce((predicate: boolean, prop: INode) => { +function findObjWithOneOfKeys(p: Node, keyNames: string[]): boolean { + return (p.value as Node).properties.reduce((predicate: boolean, prop: Node): boolean => { const name: string = prop.key.name; return keyNames.indexOf(name) > -1 || predicate; }, false); @@ -410,12 +410,12 @@ function findObjWithOneOfKeys(p: INode, keyNames: string[]) { * @returns {Node} - the created ast */ -function getRequire(j: IJSCodeshift, constName: string, packagePath: string): INode { +function getRequire(j: JSCodeshift, constName: string, packagePath: string): Node { return j.variableDeclaration("const", [ j.variableDeclarator( j.identifier(constName), - j.callExpression(j.identifier("require"), [j.literal(packagePath)]), - ), + j.callExpression(j.identifier("require"), [j.literal(packagePath)]) + ) ]); } @@ -430,32 +430,33 @@ function getRequire(j: IJSCodeshift, constName: string, packagePath: string): IN * @returns {Node} - the created ast */ -function addProperty(j: IJSCodeshift, p: INode, key: string, value: valueType, action?: string): INode { +function addProperty(j: JSCodeshift, p: Node, key: string, value: valueType, action?: string): Node { if (!p) { return; } let valForNode: valueType; if (Array.isArray(value)) { - let arrExp: INode = j.arrayExpression([]); + let arrExp: Node = j.arrayExpression([]); if (safeTraverseAndGetType(p) === "ArrayExpression") { - arrExp = p.value.value; + arrExp = (p.value as Node).value as Node; } - value.forEach((val: valueType) => { - addProperty(j, arrExp, null, val); - }); + value.forEach( + (val: valueType): void => { + addProperty(j, arrExp, null, val); + } + ); valForNode = arrExp; - } else if ( - typeof value === "object" && - !(value.__paths || value instanceof RegExp) - ) { - let objectExp: INode = j.objectExpression([]); + } else if (typeof value === "object" && !(value.__paths || value instanceof RegExp)) { + let objectExp: Node = j.objectExpression([]); if (safeTraverseAndGetType(p) === "ObjectExpression") { - objectExp = p.value.value; + objectExp = (p.value as Node).value as Node; } // object -> loop through it - Object.keys(value).forEach((prop: string) => { - addProperty(j, objectExp, prop, value[prop]); - }); + Object.keys(value).forEach( + (prop: string): void => { + addProperty(j, objectExp, prop, value[prop]); + } + ); valForNode = objectExp; } else { valForNode = createIdentifierOrLiteral(j, value); @@ -468,14 +469,16 @@ function addProperty(j: IJSCodeshift, p: INode, key: string, value: valueType, a } // we only return the generated pushVal which will be replace the node path - if (action === "add") { return pushVal; } + if (action === "add") { + return pushVal; + } if (p.properties) { p.properties.push(pushVal); return p; } - if (p.value && p.value.properties) { - p.value.properties.push(pushVal); + if (p.value && (p.value as Node).properties) { + (p.value as Node).properties.push(pushVal); return p; } if (p.elements) { @@ -495,8 +498,7 @@ function addProperty(j: IJSCodeshift, p: INode, key: string, value: valueType, a * @returns {Node} - the created ast */ -function removeProperty(j: IJSCodeshift, ast: INode, key: string, value: valueType): INode { - +function removeProperty(j: JSCodeshift, ast: Node, key: string, value: valueType): Node { if (typeof value === "object" && !Array.isArray(value)) { // override for module.rules / loaders if (key === "module" && value.rules) { @@ -504,12 +506,14 @@ function removeProperty(j: IJSCodeshift, ast: INode, key: string, value: valueTy .find(j.Property, { value: { type: "Literal", - value: value.rules[0].loader, - }, + value: value.rules[0].loader + } }) - .forEach((p: INode) => { - j(p.parent).remove(); - }); + .forEach( + (p: Node): void => { + j(p.parent).remove(); + } + ); } } @@ -517,14 +521,16 @@ function removeProperty(j: IJSCodeshift, ast: INode, key: string, value: valueTy if (Array.isArray(value)) { return ast .find(j.Literal, { - value: value[0], + value: value[0] }) - .forEach((p: INode) => { - const configKey = safeTraverse(p, ["parent", "parent", "node", "key", "name"]); - if (configKey === key) { - j(p).remove(); + .forEach( + (p: Node): void => { + const configKey = safeTraverse(p, ["parent", "parent", "node", "key", "name"]); + if (configKey === key) { + j(p).remove(); + } } - }); + ); } // value => literal string / boolean / nested object @@ -533,19 +539,23 @@ function removeProperty(j: IJSCodeshift, ast: INode, key: string, value: valueTy objKeyToRemove = key; } else if (typeof value === "object") { for (const innerKey in value) { - if (value[innerKey] === null) { objKeyToRemove = innerKey; } + if (value[innerKey] === null) { + objKeyToRemove = innerKey; + } } } return ast .find(j.Property, { key: { name: objKeyToRemove, - type: "Identifier", - }, + type: "Identifier" + } }) - .forEach((p: INode) => { - j(p).remove(); - }); + .forEach( + (p: Node): void => { + j(p).remove(); + } + ); } /** @@ -560,20 +570,23 @@ function removeProperty(j: IJSCodeshift, ast: INode, key: string, value: valueTy * @returns ast - jscodeshift API */ -function parseTopScope(j: IJSCodeshift, ast: INode, value: string[], action: string): boolean | INode { - function createTopScopeProperty(p: INode): boolean { - value.forEach((n: string) => { - if ( - !p.value.body[0].declarations || - n.indexOf(p.value.body[0].declarations[0].id.name) <= 0 - ) { - p.value.body.splice(-1, 0, n); + // eslint-disable-next-line @typescript-eslint/no-unused-vars +function parseTopScope(j: JSCodeshift, ast: Node, value: string[], action: string): boolean | Node { + function createTopScopeProperty(p: Node): boolean { + value.forEach( + (n: string): void => { + if ( + !(p.value as Node).body[0].declarations || + n.indexOf((p.value as Node).body[0].declarations[0].id.name) <= 0 + ) { + (p.value as Node).body.splice(-1, 0, n); + } } - }); + ); return false; // TODO: debug later } if (value) { - return ast.find(j.Program).filter((p: INode): boolean => createTopScopeProperty(p)); + return ast.find(j.Program).filter((p: Node): boolean => createTopScopeProperty(p)); } else { return ast; } @@ -591,36 +604,36 @@ function parseTopScope(j: IJSCodeshift, ast: INode, value: string[], action: str * @returns ast - jscodeshift API */ -function parseMerge(j: IJSCodeshift, ast: INode, value: string, action: string): boolean | INode { - function createMergeProperty(p: INode) { +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function parseMerge(j: JSCodeshift, ast: Node, value: string, action: string): boolean | Node { + function createMergeProperty(p: Node): boolean { // FIXME Use j.callExp() - const exportsDecl: INode[] = p.value.body.map((n: INode) => { - if (n.expression) { - return n.expression.right; + const exportsDecl: Node[] = (p.value as Node).body.map( + (n: Node): Node => { + if (n.expression) { + return n.expression.right; + } } - }); + ); const bodyLength = exportsDecl.length; - const newVal: INode = {}; + const newVal: Node = {}; newVal.type = "ExpressionStatement"; newVal.expression = { left: { computed: false, object: j.identifier("module"), property: j.identifier("exports"), - type: "MemberExpression", + type: "MemberExpression" }, operator: "=", - right: j.callExpression(j.identifier("merge"), [ - j.identifier(value), - exportsDecl.pop(), - ]), - type: "AssignmentExpression", + right: j.callExpression(j.identifier("merge"), [j.identifier(value), exportsDecl.pop()]), + type: "AssignmentExpression" }; - p.value.body[bodyLength - 1] = newVal; + (p.value as Node).body[bodyLength - 1] = newVal; return false; // TODO: debug later } if (value) { - return ast.find(j.Program).filter((p: INode): boolean => createMergeProperty(p)); + return ast.find(j.Program).filter((p: Node): boolean => createMergeProperty(p)); } else { return ast; } @@ -645,5 +658,5 @@ export { addProperty, removeProperty, parseTopScope, - parseMerge, + parseMerge }; diff --git a/packages/utils/copy-utils.ts b/packages/utils/copy-utils.ts index 5f78d92b3d4..4a593e95ae0 100644 --- a/packages/utils/copy-utils.ts +++ b/packages/utils/copy-utils.ts @@ -1,6 +1,6 @@ import * as path from "path"; -interface IGenerator { +interface Generator { fs: { copy(from: string, to: string, options?: object): void; copyTpl(from: string, to: string, context: object, templateOptions?: object, copyOptions?: object): void; @@ -16,10 +16,9 @@ interface IGenerator { * @param {string} templateDir Absolute path to template directory * @returns {Function} A curried function that takes a file path and copies it */ -export const generatorCopy = ( - generator, - templateDir: string, -): (filePath: string) => void => (filePath: string): void => { +export const generatorCopy = (generator, templateDir: string): ((filePath: string) => void) => ( + filePath: string +): void => { const sourceParts = templateDir.split(path.delimiter); sourceParts.push.apply(sourceParts, filePath.split("/")); const targetParts = path.dirname(filePath).split("/"); @@ -27,7 +26,7 @@ export const generatorCopy = ( generator.fs.copy( path.join.apply(null, sourceParts), - generator.destinationPath(path.join.apply(null, targetParts)), + generator.destinationPath(path.join.apply(null, targetParts)) ); }; @@ -45,8 +44,8 @@ export const generatorCopy = ( export const generatorCopyTpl = ( generator, templateDir: string, - templateData: object, -): (filePath: string) => void => (filePath: string): void => { + templateData: object +): ((filePath: string) => void) => (filePath: string): void => { const sourceParts = templateDir.split(path.delimiter); sourceParts.push.apply(sourceParts, filePath.split("/")); const targetParts = path.dirname(filePath).split("/"); @@ -55,6 +54,6 @@ export const generatorCopyTpl = ( generator.fs.copyTpl( path.join.apply(null, sourceParts), generator.destinationPath(path.join.apply(null, targetParts)), - templateData, + templateData ); }; diff --git a/packages/utils/defineTest.ts b/packages/utils/defineTest.ts index a5ee041d26f..350e03a05ba 100644 --- a/packages/utils/defineTest.ts +++ b/packages/utils/defineTest.ts @@ -1,27 +1,28 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ import * as fs from "fs"; import * as path from "path"; -import { IJSCodeshift, INode } from "./types/NodePath"; +import { JSCodeshift, Node } from "./types/NodePath"; -interface IModule { - ( - jscodeshift: IJSCodeshift, - ast: INode, - initOptions: string | boolean | object, - action: string, - transformName?: string, - ): INode; - default: transformType; - parser: string; +interface Module { + ( + jscodeshift: JSCodeshift, + ast: Node, + initOptions: string | boolean | object, + action: string, + transformName?: string + ): Node; + default: transformType; + parser: string; } type transformType = ( - jscodeshift: IJSCodeshift, - ast: INode, - initOptions: string | boolean | object, - action: object | string, - transformName?: string, -) => INode; + jscodeshift: JSCodeshift, + ast: Node, + initOptions: string | boolean | object, + action: object | string, + transformName?: string +) => Node; /** * Utility function to run a jscodeshift script within a unit test. @@ -48,54 +49,45 @@ type transformType = ( * @return {Function} Function that fires of the transforms */ function runSingleTransform( - dirName: string, - transformName: string, - testFilePrefix: string, - initOptions: object | boolean | string, - action: object | string, + dirName: string, + transformName: string, + testFilePrefix: string, + initOptions: object | boolean | string, + action: object | string ): string { - if (!testFilePrefix) { - testFilePrefix = transformName; - } - const fixtureDir = path.join( - dirName, - "__tests__", - "__testfixtures__", - ); - const inputPath = path.join(fixtureDir, `${testFilePrefix}.input.js`); - const source = fs.readFileSync(inputPath, "utf8"); + if (!testFilePrefix) { + testFilePrefix = transformName; + } + const fixtureDir = path.join(dirName, "__tests__", "__testfixtures__"); + const inputPath = path.join(fixtureDir, `${testFilePrefix}.input.js`); + const source = fs.readFileSync(inputPath, "utf8"); - let module: IModule; - // Assumes transform and test are on the same level - if (action) { - module = require(path.join(dirName, "recursive-parser.ts")); - } else { - module = require(path.join(dirName, `${transformName}.ts`)); - } - // Handle ES6 modules using default export for the transform - const transform = module.default ? module.default : module; + let module: Module; + // Assumes transform and test are on the same level + if (action) { + module = require(path.join(dirName, "recursive-parser.ts")); + } else { + module = require(path.join(dirName, `${transformName}.ts`)); + } + // Handle ES6 modules using default export for the transform + const transform = module.default ? module.default : module; - // Jest resets the module registry after each test, so we need to always get - // a fresh copy of jscodeshift on every test run. - let jscodeshift: IJSCodeshift = require("jscodeshift/dist/core"); - if (module.parser) { - jscodeshift = jscodeshift.withParser(module.parser); - } - const ast: INode = jscodeshift(source); - if (initOptions || typeof initOptions === "boolean") { - return transform( - jscodeshift, - ast, - initOptions, - action, - transformName, - ).toSource({ - quote: "single", - }); - } - return transform(jscodeshift, ast, source, action).toSource({ - quote: "single", - }); + // Jest resets the module registry after each test, so we need to always get + // a fresh copy of jscodeshift on every test run. + // eslint-disable-next-line + let jscodeshift: JSCodeshift = require("jscodeshift/dist/core"); + if (module.parser) { + jscodeshift = jscodeshift.withParser(module.parser); + } + const ast: Node = jscodeshift(source); + if (initOptions || typeof initOptions === "boolean") { + return transform(jscodeshift, ast, initOptions, action, transformName).toSource({ + quote: "single" + }); + } + return transform(jscodeshift, ast, source, action).toSource({ + quote: "single" + }); } /** @@ -116,19 +108,19 @@ function runSingleTransform( * @return {Void} Jest makes sure to execute the globally defined functions */ export default function defineTest( - dirName: string, - transformName: string, - testFilePrefix?: string, - transformObject?: object | string, - action?: object | string, + dirName: string, + transformName: string, + testFilePrefix?: string, + transformObject?: object | string, + action?: object | string ): void { - const testName: string = testFilePrefix - ? `transforms correctly using "${testFilePrefix}" data` - : "transforms correctly"; - describe(transformName, () => { - it(testName, () => { - const output = runSingleTransform(dirName, transformName, testFilePrefix, transformObject, action); - expect(output).toMatchSnapshot(); - }); - }); + const testName: string = testFilePrefix + ? `transforms correctly using "${testFilePrefix}" data` + : "transforms correctly"; + describe(transformName, () => { + it(testName, () => { + const output = runSingleTransform(dirName, transformName, testFilePrefix, transformObject, action); + expect(output).toMatchSnapshot(); + }); + }); } diff --git a/packages/utils/modify-config-helper.ts b/packages/utils/modify-config-helper.ts index 219a45ae5fe..c457a115e26 100644 --- a/packages/utils/modify-config-helper.ts +++ b/packages/utils/modify-config-helper.ts @@ -6,9 +6,9 @@ import * as yeoman from "yeoman-environment"; import Generator = require("yeoman-generator"); import runTransform from "./scaffold"; -import { IGenerator, IYeoman } from "./types/Yeoman"; +import { YeoGenerator } from "./types/Yeoman"; -export interface IConfig extends Object { +export interface Config extends Object { item?: { name: string; }; @@ -18,10 +18,10 @@ export interface IConfig extends Object { webpackOptions: object; } -export interface ITransformConfig extends Object { +export interface TransformConfig extends Object { configPath?: string; configFile?: string; - config?: IConfig; + config?: Config; } const DEFAULT_WEBPACK_CONFIG_FILENAME = "webpack.config.js"; @@ -40,11 +40,10 @@ const DEFAULT_WEBPACK_CONFIG_FILENAME = "webpack.config.js"; export default function modifyHelperUtil( action: string, - generator: IGenerator, + generator: YeoGenerator, configFile: string = DEFAULT_WEBPACK_CONFIG_FILENAME, - packages?: string[]) - : Function { - + packages?: string[] +): Function { let configPath: string | null = null; if (action !== "init") { @@ -57,7 +56,7 @@ export default function modifyHelperUtil( chalk.green(" SUCCESS ") + "Found config " + chalk.cyan(configFile + "\n") + - "\n", + "\n" ); } else { process.stdout.write( @@ -68,7 +67,7 @@ export default function modifyHelperUtil( " not found. Please specify a valid path to your webpack config like " + chalk.white("$ ") + chalk.cyan(`webpack-cli ${action} webpack.dev.js`) + - "\n", + "\n" ); return; } @@ -79,58 +78,68 @@ export default function modifyHelperUtil( if (!generator) { generator = class extends Generator { - public initializing() { - packages.forEach((pkgPath: string) => { - return (this as IGenerator).composeWith(require.resolve(pkgPath)); - }); + public initializing(): void { + packages.forEach( + (pkgPath: string): void => { + return (this as YeoGenerator).composeWith(require.resolve(pkgPath)); + } + ); } }; } env.registerStub(generator, generatorName); - env.run(generatorName).then((_: void) => { - let configModule: object; - try { - const confPath = path.resolve(process.cwd(), ".yo-rc.json"); - configModule = require(confPath); - // Change structure of the config to be transformed - const tmpConfig: object = {}; - Object.keys(configModule).forEach((prop: string): void => { - const configs = Object.keys(configModule[prop].configuration); - configs.forEach((conf: string): void => { - tmpConfig[conf] = configModule[prop].configuration[conf]; - }); - }); - configModule = tmpConfig; - } catch (err) { - console.error( - chalk.red("\nCould not find a yeoman configuration file.\n"), - ); - console.error( - chalk.red( - "\nPlease make sure to use 'this.config.set('configuration', this.configuration);' at the end of the generator.\n", - ), - ); - Error.stackTraceLimit = 0; - process.exitCode = -1; - } - const transformConfig: ITransformConfig = Object.assign( - { - configFile: !configPath ? null : fs.readFileSync(configPath, "utf8"), - configPath, - }, - configModule, - ); - return runTransform(transformConfig, action); - }).catch((err) => { - console.error( - chalk.red( - ` + env.run(generatorName) + .then( + (): void => { + let configModule: object; + try { + const confPath = path.resolve(process.cwd(), ".yo-rc.json"); + configModule = require(confPath); + // Change structure of the config to be transformed + const tmpConfig: object = {}; + Object.keys(configModule).forEach( + (prop: string): void => { + const configs = Object.keys(configModule[prop].configuration); + configs.forEach( + (conf: string): void => { + tmpConfig[conf] = configModule[prop].configuration[conf]; + } + ); + } + ); + configModule = tmpConfig; + } catch (err) { + console.error(chalk.red("\nCould not find a yeoman configuration file.\n")); + console.error( + chalk.red( + "\nPlease make sure to use 'this.config.set('configuration', this.configuration);' at the end of the generator.\n" + ) + ); + Error.stackTraceLimit = 0; + process.exitCode = -1; + } + const transformConfig: TransformConfig = Object.assign( + { + configFile: !configPath ? null : fs.readFileSync(configPath, "utf8"), + configPath + }, + configModule + ); + return runTransform(transformConfig, action); + } + ) + .catch( + (err): void => { + console.error( + chalk.red( + ` Unexpected Error please file an issue here https://github.com/webpack/webpack-cli/issues/new?template=Bug_report.md - `, - ), + ` + ) + ); + console.error(err); + } ); - console.error(err); - }); } diff --git a/packages/utils/npm-exists.ts b/packages/utils/npm-exists.ts index 2abc29b40c3..0db70f11883 100644 --- a/packages/utils/npm-exists.ts +++ b/packages/utils/npm-exists.ts @@ -1,5 +1,7 @@ import * as got from "got"; +// TODO: to understand the type +// eslint-disable-next-line const constant = (value: boolean) => (res: got.Response): boolean | PromiseLike => value; /** @@ -11,11 +13,13 @@ const constant = (value: boolean) => (res: got.Response): boolean | Prom * based on if it exists or not */ +// TODO: figure out the correct type here +// eslint-disable-next-line export default function npmExists(moduleName: string): Promise { const hostname = "https://www.npmjs.org"; const pkgUrl = `${hostname}/package/${moduleName}`; return got(pkgUrl, { - method: "HEAD", + method: "HEAD" }) .then(constant(true)) .catch(constant(false)); diff --git a/packages/utils/npm-packages-exists.ts b/packages/utils/npm-packages-exists.ts index a1987c3332d..529da51ad2f 100644 --- a/packages/utils/npm-packages-exists.ts +++ b/packages/utils/npm-packages-exists.ts @@ -18,46 +18,52 @@ const WEBPACK_SCAFFOLD_PREFIX = "webpack-scaffold"; export default function npmPackagesExists(pkg: string[]): void { const acceptedPackages: string[] = []; - function resolvePackagesIfReady() { + function resolvePackagesIfReady(): void | Function { if (acceptedPackages.length === pkg.length) { return resolvePackages(acceptedPackages); } } - pkg.forEach((scaffold: string): void => { - if (isLocalPath(scaffold)) { - // If the scaffold is a path to a local folder, no name validation is necessary. - acceptedPackages.push(scaffold); - resolvePackagesIfReady(); - return; - } + pkg.forEach( + (scaffold: string): void => { + if (isLocalPath(scaffold)) { + // If the scaffold is a path to a local folder, no name validation is necessary. + acceptedPackages.push(scaffold); + resolvePackagesIfReady(); + return; + } - // The scaffold is on npm; validate name and existence - if ( - scaffold.length <= WEBPACK_SCAFFOLD_PREFIX.length || - scaffold.slice(0, WEBPACK_SCAFFOLD_PREFIX.length) !== WEBPACK_SCAFFOLD_PREFIX - ) { - throw new TypeError( - chalk.bold(`${scaffold} isn't a valid name.\n`) + - chalk.red( - `\nIt should be prefixed with '${WEBPACK_SCAFFOLD_PREFIX}', but have different suffix.\n`, - ), - ); - } + // The scaffold is on npm; validate name and existence + if ( + scaffold.length <= WEBPACK_SCAFFOLD_PREFIX.length || + scaffold.slice(0, WEBPACK_SCAFFOLD_PREFIX.length) !== WEBPACK_SCAFFOLD_PREFIX + ) { + throw new TypeError( + chalk.bold(`${scaffold} isn't a valid name.\n`) + + chalk.red( + `\nIt should be prefixed with '${WEBPACK_SCAFFOLD_PREFIX}', but have different suffix.\n` + ) + ); + } - npmExists(scaffold) - .then((moduleExists: boolean) => { - if (moduleExists) { - acceptedPackages.push(scaffold); - } else { - Error.stackTraceLimit = 0; - throw new TypeError(`Cannot resolve location of package ${scaffold}.`); - } - }) - .catch((err: IError) => { - console.error(err.stack || err); - process.exit(0); - }) - .then(resolvePackagesIfReady); - }); + npmExists(scaffold) + .then( + (moduleExists: boolean): void => { + if (moduleExists) { + acceptedPackages.push(scaffold); + } else { + Error.stackTraceLimit = 0; + throw new TypeError(`Cannot resolve location of package ${scaffold}.`); + } + } + ) + .catch( + (err: Error): void => { + console.error(err.stack || err); + process.exit(0); + } + ) + .then(resolvePackagesIfReady); + } + ); } diff --git a/packages/utils/package-lock.json b/packages/utils/package-lock.json index 5c04b28b295..be49655ae02 100644 --- a/packages/utils/package-lock.json +++ b/packages/utils/package-lock.json @@ -149,7 +149,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==" }, "ansi-regex": { @@ -465,7 +465,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" }, "array-union": { @@ -1619,7 +1619,7 @@ "dependencies": { "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" } } @@ -2942,7 +2942,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2960,11 +2961,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2977,15 +2980,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3088,7 +3094,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3098,6 +3105,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3110,17 +3118,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -3137,6 +3148,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3209,7 +3221,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3219,6 +3232,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3294,7 +3308,8 @@ }, "safe-buffer": { "version": "5.1.1", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3324,6 +3339,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3341,6 +3357,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3379,11 +3396,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.0.2", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -3545,7 +3564,7 @@ }, "globby": { "version": "8.0.1", - "resolved": "http://registry.npmjs.org/globby/-/globby-8.0.1.tgz", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.1.tgz", "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", "requires": { "array-union": "^1.0.1", @@ -4728,7 +4747,7 @@ }, "jsesc": { "version": "1.3.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=" }, "json-buffer": { @@ -5395,7 +5414,7 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-locale": { @@ -5410,7 +5429,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "p-cancelable": { @@ -5516,7 +5535,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { @@ -6049,7 +6068,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "requires": { "ret": "~0.1.10" @@ -6711,7 +6730,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "supports-color": { @@ -6729,7 +6748,7 @@ }, "temp": { "version": "0.8.3", - "resolved": "http://registry.npmjs.org/temp/-/temp-0.8.3.tgz", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", "requires": { "os-tmpdir": "^1.0.0", diff --git a/packages/utils/package-manager.ts b/packages/utils/package-manager.ts index 0a48e062607..888b974ef18 100644 --- a/packages/utils/package-manager.ts +++ b/packages/utils/package-manager.ts @@ -4,16 +4,11 @@ import * as spawn from "cross-spawn"; import * as fs from "fs"; import * as path from "path"; -interface ISpawnFunctions { +interface SpawnFunctions { npm: (pkg: string, isNew: boolean) => SpawnSyncReturns; yarn: (pkg: string, isNew: boolean) => SpawnSyncReturns; } -const SPAWN_FUNCTIONS: ISpawnFunctions = { - npm: spawnNPM, - yarn: spawnYarn, -}; - /** * * Spawns a new process using npm @@ -25,7 +20,7 @@ const SPAWN_FUNCTIONS: ISpawnFunctions = { function spawnNPM(pkg: string, isNew: boolean): SpawnSyncReturns { return spawn.sync("npm", [isNew ? "install" : "update", "-g", pkg], { - stdio: "inherit", + stdio: "inherit" }); } @@ -40,25 +35,14 @@ function spawnNPM(pkg: string, isNew: boolean): SpawnSyncReturns { function spawnYarn(pkg: string, isNew: boolean): SpawnSyncReturns { return spawn.sync("yarn", ["global", isNew ? "add" : "upgrade", pkg], { - stdio: "inherit", + stdio: "inherit" }); } -/** - * - * Spawns a new process that installs the scaffold/dependency - * - * @param {String} pkg - The dependency to be installed - * @returns {Function} spawn - Installs the package - */ - -export function spawnChild(pkg: string): SpawnSyncReturns { - const rootPath: string = getPathToGlobalPackages(); - const pkgPath: string = path.resolve(rootPath, pkg); - const packageManager: string = getPackageManager(); - const isNew: boolean = !fs.existsSync(pkgPath); - return SPAWN_FUNCTIONS[packageManager](pkg, isNew); -} +const SPAWN_FUNCTIONS: SpawnFunctions = { + npm: spawnNPM, + yarn: spawnYarn +}; /** * @@ -69,9 +53,7 @@ export function spawnChild(pkg: string): SpawnSyncReturns { */ export function getPackageManager(): string { - const hasLocalNPM = fs.existsSync( - path.resolve(process.cwd(), "package-lock.json"), - ); + const hasLocalNPM = fs.existsSync(path.resolve(process.cwd(), "package-lock.json")); const hasLocalYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock")); if (hasLocalNPM) { return "npm"; @@ -109,3 +91,18 @@ export function getPathToGlobalPackages(): string { return require("global-modules"); } +/** + * + * Spawns a new process that installs the scaffold/dependency + * + * @param {String} pkg - The dependency to be installed + * @returns {SpawnSyncReturns} spawn - Installs the package + */ +export function spawnChild(pkg: string): SpawnSyncReturns { + const rootPath: string = getPathToGlobalPackages(); + const pkgPath: string = path.resolve(rootPath, pkg); + const packageManager: string = getPackageManager(); + const isNew: boolean = !fs.existsSync(pkgPath); + + return SPAWN_FUNCTIONS[packageManager](pkg, isNew); +} diff --git a/packages/utils/recursive-parser.ts b/packages/utils/recursive-parser.ts index f6c9f03b7eb..a6312798f5b 100644 --- a/packages/utils/recursive-parser.ts +++ b/packages/utils/recursive-parser.ts @@ -1,9 +1,13 @@ import * as utils from "./ast-utils"; -import { IJSCodeshift, INode, valueType } from "./types/NodePath"; +import { JSCodeshift, Node, valueType } from "./types/NodePath"; export default function recursiveTransform( - j: IJSCodeshift, ast: INode, key: string, value: valueType, action: string, -): boolean | INode { + j: JSCodeshift, + ast: Node, + key: string, + value: valueType, + action: string +): boolean | Node { if (key === "topScope") { if (Array.isArray(value)) { return utils.parseTopScope(j, ast, value, action); @@ -15,46 +19,39 @@ export default function recursiveTransform( return utils.parseMerge(j, ast, value, action); } } - const node: INode = utils.findRootNodesByName(j, ast, key); + const node: Node = utils.findRootNodesByName(j, ast, key); // get module.exports prop const root = ast .find(j.ObjectExpression) - .filter((p: INode): boolean => { - return ( - utils.safeTraverse(p, [ - "parentPath", - "value", - "left", - "object", - "name", - ]) === "module" && - utils.safeTraverse(p, [ - "parentPath", - "value", - "left", - "property", - "name", - ]) === "exports" - ); - }) - .filter((p: INode): boolean => p.value.properties); + .filter( + (p: Node): boolean => { + return ( + utils.safeTraverse(p, ["parentPath", "value", "left", "object", "name"]) === "module" && + utils.safeTraverse(p, ["parentPath", "value", "left", "property", "name"]) === "exports" + ); + } + ) + .filter((p: Node): boolean => !!(p.value as Node).properties); if (node.size() !== 0) { if (action === "add") { - return utils.findRootNodesByName(j, root, key) - .forEach((p: INode) => { + return utils.findRootNodesByName(j, root, key).forEach( + (p: Node): void => { j(p).replaceWith(utils.addProperty(j, p, key, value, action)); - }); + } + ); } else if (action === "remove") { return utils.removeProperty(j, root, key, value); } } else { - return root.forEach((p: INode) => { - if (value) { - // init, add new property - utils.addProperty(j, p, key, value, null); + return root.forEach( + (p: Node): void => { + if (value) { + // init, add new property + utils.addProperty(j, p, key, value, null); + } } - }); + ); } } diff --git a/packages/utils/resolve-packages.ts b/packages/utils/resolve-packages.ts index b16823509a9..c5dabe60474 100644 --- a/packages/utils/resolve-packages.ts +++ b/packages/utils/resolve-packages.ts @@ -6,7 +6,7 @@ import modifyConfigHelper from "./modify-config-helper"; import { getPathToGlobalPackages } from "./package-manager"; import { spawnChild } from "./package-manager"; -interface IChildProcess { +interface ChildProcess { status: number; } @@ -18,14 +18,16 @@ interface IChildProcess { * @returns {Promise} promise - Returns a promise to the installation */ -export function processPromise(child: IChildProcess): Promise { - return new Promise((resolve: (_?: void) => void, reject: (_?: void) => void) => { - if (child.status !== 0) { - reject(); - } else { - resolve(); +export function processPromise(child: ChildProcess): Promise { + return new Promise( + (resolve: () => void, reject: () => void): void => { + if (child.status !== 0) { + reject(); + } else { + resolve(); + } } - }); + ); } /** @@ -48,46 +50,52 @@ export function resolvePackages(pkg: string[]): Function | void { } } - pkg.forEach((scaffold: string) => { - // Resolve paths to modules on local filesystem - if (isLocalPath(scaffold)) { - let absolutePath: string = scaffold; - - try { - absolutePath = path.resolve(process.cwd(), scaffold); - require.resolve(absolutePath); - packageLocations.push(absolutePath); - } catch (err) { - console.error(`Cannot find a generator at ${absolutePath}.`); - console.error("\nReason:\n"); - console.error(chalk.bold.red(err)); - process.exitCode = 1; - } - - invokeGeneratorIfReady(); - return; - } + pkg.forEach( + (scaffold: string): void => { + // Resolve paths to modules on local filesystem + if (isLocalPath(scaffold)) { + let absolutePath: string = scaffold; - // Resolve modules on npm registry - processPromise(spawnChild(scaffold)) - .then((_: void) => { try { - const globalPath: string = getPathToGlobalPackages(); - packageLocations.push(path.resolve(globalPath, scaffold)); + absolutePath = path.resolve(process.cwd(), scaffold); + require.resolve(absolutePath); + packageLocations.push(absolutePath); } catch (err) { - console.error("Package wasn't validated correctly.."); - console.error("Submit an issue for", pkg, "if this persists"); - console.error("\nReason: \n"); + console.error(`Cannot find a generator at ${absolutePath}.`); + console.error("\nReason:\n"); console.error(chalk.bold.red(err)); process.exitCode = 1; } - }) - .catch((err: string) => { - console.error("Package couldn't be installed, aborting.."); - console.error("\nReason: \n"); - console.error(chalk.bold.red(err)); - process.exitCode = 1; - }) - .then(invokeGeneratorIfReady); - }); + + invokeGeneratorIfReady(); + return; + } + + // Resolve modules on npm registry + processPromise(spawnChild(scaffold)) + .then( + (): void => { + try { + const globalPath: string = getPathToGlobalPackages(); + packageLocations.push(path.resolve(globalPath, scaffold)); + } catch (err) { + console.error("Package wasn't validated correctly.."); + console.error("Submit an issue for", pkg, "if this persists"); + console.error("\nReason: \n"); + console.error(chalk.bold.red(err)); + process.exitCode = 1; + } + } + ) + .catch( + (err: string): void => { + console.error("Package couldn't be installed, aborting.."); + console.error("\nReason: \n"); + console.error(chalk.bold.red(err)); + process.exitCode = 1; + } + ) + .then(invokeGeneratorIfReady); + } + ); } diff --git a/packages/utils/scaffold.ts b/packages/utils/scaffold.ts index a3add9ee769..2d2ece8a390 100644 --- a/packages/utils/scaffold.ts +++ b/packages/utils/scaffold.ts @@ -4,12 +4,12 @@ import pEachSeries = require("p-each-series"); import * as path from "path"; import { findProjectRoot } from "./find-root"; -import { IError } from "../init/types"; -import { IConfig, ITransformConfig } from "./modify-config-helper"; +import { Error } from "../init/types"; +import { Config, TransformConfig } from "./modify-config-helper"; import propTypes from "./prop-types"; import astTransform from "./recursive-parser"; import runPrettier from "./run-prettier"; -import { INode } from "./types/NodePath"; +import { Node } from "./types/NodePath"; /** * @@ -20,7 +20,7 @@ import { INode } from "./types/NodePath"; * @returns {Array} - An array with keys on which transformations need to be run */ -function mapOptionsToTransform(config: IConfig): string[] { +function mapOptionsToTransform(config: Config): string[] { return Object.keys(config.webpackOptions).filter((k: string): boolean => propTypes.has(k)); } @@ -34,72 +34,73 @@ function mapOptionsToTransform(config: IConfig): string[] { * and writes the file */ -export default function runTransform(transformConfig: ITransformConfig, action: string): void { +export default function runTransform(transformConfig: TransformConfig, action: string): void { // webpackOptions.name sent to nameTransform if match - const webpackConfig = Object.keys(transformConfig).filter((p: string): boolean => { - return p !== "configFile" && p !== "configPath"; - }); + const webpackConfig = Object.keys(transformConfig).filter( + (p: string): boolean => { + return p !== "configFile" && p !== "configPath"; + } + ); const initActionNotDefined = action && action !== "init" ? true : false; - webpackConfig.forEach((scaffoldPiece: string) => { - const config: IConfig = transformConfig[scaffoldPiece]; + webpackConfig.forEach( + (scaffoldPiece: string): Promise => { + const config: Config = transformConfig[scaffoldPiece]; - const transformations = mapOptionsToTransform(config); + const transformations = mapOptionsToTransform(config); - if (config.topScope && transformations.indexOf("topScope") === -1) { - transformations.push("topScope"); - } + if (config.topScope && transformations.indexOf("topScope") === -1) { + transformations.push("topScope"); + } - if (config.merge) { - transformations.push("merge"); - } + if (config.merge) { + transformations.push("merge"); + } - const ast: INode = j( - initActionNotDefined - ? transformConfig.configFile - : "module.exports = {}", - ); + const ast: Node = j(initActionNotDefined ? transformConfig.configFile : "module.exports = {}"); - const transformAction: string = action || null; + const transformAction: string = action || null; - return pEachSeries(transformations, (f: string): boolean | INode => { - if (f === "merge" || f === "topScope") { - return astTransform(j, ast, f, config[f], transformAction); - } - return astTransform(j, ast, f, config.webpackOptions[f], transformAction); - }) - .then((value: string[]): void | PromiseLike => { - let configurationName: string; - if (!config.configName) { - configurationName = "webpack.config.js"; - } else { - configurationName = "webpack." + config.configName + ".js"; + return pEachSeries( + transformations, + (f: string): boolean | Node => { + if (f === "merge" || f === "topScope") { + // TODO: typing here is difficult to understand + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return astTransform(j, ast, f, config[f] as any, transformAction); + } + return astTransform(j, ast, f, config.webpackOptions[f], transformAction); } + ) + .then( + (): void | PromiseLike => { + let configurationName: string; + if (!config.configName) { + configurationName = "webpack.config.js"; + } else { + configurationName = "webpack." + config.configName + ".js"; + } - const projectRoot = findProjectRoot(); - const outputPath: string = initActionNotDefined - ? transformConfig.configPath - : path.join(projectRoot || process.cwd(), configurationName); - const source: string = ast.toSource({ - quote: "single", - }); - runPrettier(outputPath, source); - - }) - .catch((err: IError) => { - console.error(err.message ? err.message : err); - }); - }); + const projectRoot = findProjectRoot(); + const outputPath: string = initActionNotDefined + ? transformConfig.configPath + : path.join(projectRoot || process.cwd(), configurationName); + const source: string = ast.toSource({ + quote: "single" + }); + runPrettier(outputPath, source); + } + ) + .catch( + (err: Error): void => { + console.error(err.message ? err.message : err); + } + ); + } + ); let successMessage: string = `Congratulations! Your new webpack configuration file has been created!\n`; if (initActionNotDefined && transformConfig.config.item) { - successMessage = `Congratulations! ${ - transformConfig.config.item - } has been ${action}ed!\n`; + successMessage = `Congratulations! ${transformConfig.config.item} has been ${action}ed!\n`; } - process.stdout.write( - "\n" + - chalk.green( - successMessage, - ), - ); + process.stdout.write("\n" + chalk.green(successMessage)); } diff --git a/packages/utils/types/NodePath.ts b/packages/utils/types/NodePath.ts index d082ddc6587..1b828378ddb 100644 --- a/packages/utils/types/NodePath.ts +++ b/packages/utils/types/NodePath.ts @@ -1,93 +1,99 @@ -export interface INode extends Object { +export interface Node extends Object { id?: { name: string; }; - arguments?: INode[]; - body?: INode[]; - elements?: INode[]; + arguments?: Node[]; + body?: Node[]; + elements?: Node[]; expression?: { left: { - computed: boolean, - object: INode, - property: INode, - type: string, - }, - operator: string, - right: INode, - type: string, + computed: boolean; + object: Node; + property: Node; + type: string; + }; + operator: string; + right: Node; + type: string; + value?: string; }; - filter?: (p: (p: INode) => boolean) => INode; - find?: (objectExpression: object, filterExpression?: object) => INode; - forEach?: (p: (p: INode) => void) => INode; - get?: (property: string) => INode; - remove?: (_?: void) => void; - nodes?: (_?: void) => INode[]; - pop?: (_?: void) => INode; + filter?: (p: (p: Node) => boolean) => Node; + find?: (objectExpression: object, filterExpression?: object) => Node; + forEach?: (p: (p: Node) => void) => Node; + get?: (property: string) => Node; + remove?: () => void; + nodes?: () => Node[]; + pop?: () => Node; key?: { name: string; - value: INode | string; + value: Node | string; }; - node?: INode; + node?: Node; name?: string; object?: object; - parent?: INode; - properties?: INode[]; - property?: INode; + parent?: Node; + properties?: Node[]; + property?: Node; prune?: Function; - replaceWith?: (objectExpression: object) => INode; - size?: (_?: void) => number; + replaceWith?: (objectExpression: object) => Node; + size?: () => number; type?: string; - value?: INode | string | any; - toSource?: (object: { - quote?: string, - }) => string; + value?: Node | string | Node[]; + toSource?: ( + object: { + quote?: string; + } + ) => string; source?: string; - ast?: INode; - rules?: IModuleRule[]; - __paths?: INode[]; + ast?: Node; + rules?: ModuleRule[]; + + declarations?: Node[]; + + __paths?: Node[]; } -interface IModuleRule { +interface ModuleRule { loader?: string; } -interface IExpressionObject { +interface ExpressionObject { name?: string; } -export interface IJSCodeshift extends Object { - (source?: INode | string): INode; - withParser?: (parser: string) => IJSCodeshift; - identifier?: (key: string) => INode; - literal?: (key: valueType) => INode; - memberExpression?: (node1: INode, node2: INode, bool?: boolean) => INode; - objectProperty?: (key: INode, property: valueType) => INode; - objectExpression?: (properties: INode[]) => INode; - newExpression?: (expression: INode, args: INode[]) => INode; - callExpression?: (expression: INode, args: INode[]) => INode; - variableDeclarator?: (key: INode, args: INode) => INode; - variableDeclaration?: (key: string, args: INode[]) => INode; - arrayExpression?: (args?: INode[]) => INode; - property?: (type: string, key: INode, value: INode) => INode; - program?: (nodes: INode[]) => INode; - booleanLiteral?: (bool: boolean) => INode; - Property?: IExpressionObject; - NewExpression?: IExpressionObject; - CallExpression?: IExpressionObject; - VariableDeclarator?: IExpressionObject; - Identifier?: IExpressionObject; - Literal?: IExpressionObject; - ArrayExpression?: IExpressionObject; - MemberExpression?: IExpressionObject; - FunctionExpression?: IExpressionObject; - ObjectExpression?: IExpressionObject; - BlockStatement?: IExpressionObject; - Program?: IExpressionObject; +export interface JSCodeshift extends Object { + (source?: Node | string): Node; + withParser?: (parser: string) => JSCodeshift; + identifier?: (key: string) => Node; + literal?: (key: valueType) => Node; + memberExpression?: (node1: Node, node2: Node, bool?: boolean) => Node; + objectProperty?: (key: Node, property: valueType) => Node; + objectExpression?: (properties: Node[]) => Node; + newExpression?: (expression: Node, args: Node[]) => Node; + callExpression?: (expression: Node, args: Node[]) => Node; + variableDeclarator?: (key: Node, args: Node) => Node; + variableDeclaration?: (key: string, args: Node[]) => Node; + arrayExpression?: (args?: Node[]) => Node; + property?: (type: string, key: Node, value: Node) => Node; + program?: (nodes: Node[]) => Node; + booleanLiteral?: (bool: boolean) => Node; + Property?: ExpressionObject; + NewExpression?: ExpressionObject; + CallExpression?: ExpressionObject; + VariableDeclarator?: ExpressionObject; + Identifier?: ExpressionObject; + Literal?: ExpressionObject; + ArrayExpression?: ExpressionObject; + MemberExpression?: ExpressionObject; + FunctionExpression?: ExpressionObject; + ObjectExpression?: ExpressionObject; + BlockStatement?: ExpressionObject; + Program?: ExpressionObject; filters?: { VariableDeclarator: { - requiresModule: Function, - }, + requiresModule: Function; + }; }; } -export type valueType = string | number | boolean | any[] | INode | null; +export type valueType = string | number | boolean | Node | null; diff --git a/packages/utils/types/Yeoman.ts b/packages/utils/types/Yeoman.ts index 4c56a771c00..16615324c03 100644 --- a/packages/utils/types/Yeoman.ts +++ b/packages/utils/types/Yeoman.ts @@ -1,12 +1,12 @@ -interface IRunEnv extends Object { +interface RunEnv extends Object { on?: (event: string, callbackFn: Function) => void; } -export interface IYeoman extends Object { - registerStub?(generator: IGenerator, namespace: string): void; - run?(target: string, options?: object, done?: Function): IRunEnv; +export interface Yeoman extends Object { + registerStub?(generator: YeoGenerator, namespace: string): void; + run?(target: string, options?: object, done?: Function): RunEnv; } -export interface IGenerator extends Object { +export interface YeoGenerator extends Object { composeWith?: (path: string) => void; } diff --git a/packages/utils/types/index.ts b/packages/utils/types/index.ts index fd94138d988..dba558d3ccf 100644 --- a/packages/utils/types/index.ts +++ b/packages/utils/types/index.ts @@ -1,4 +1,4 @@ -interface IError { - stack?: object; - message?: string; +interface Error { + stack?: string; + message: string; } diff --git a/packages/utils/validate-identifier.ts b/packages/utils/validate-identifier.ts index b3d0b155acf..272b01f364a 100644 --- a/packages/utils/validate-identifier.ts +++ b/packages/utils/validate-identifier.ts @@ -8,13 +8,7 @@ function isKeyword(code: string): boolean { case 2: return code === "if" || code === "in" || code === "do"; case 3: - return ( - code === "var" || - code === "for" || - code === "new" || - code === "try" || - code === "let" - ); + return code === "var" || code === "for" || code === "new" || code === "try" || code === "let"; case 4: return ( code === "this" || @@ -61,9 +55,7 @@ function isKeyword(code: string): boolean { case 8: return code === "function" || code === "continue" || code === "debugger"; case 9: - return ( - code === "protected" || code === "interface" || code === "arguments" - ); + return code === "protected" || code === "interface" || code === "arguments"; case 10: return code === "instanceof" || code === "implements"; default: @@ -77,19 +69,13 @@ function isKeyword(code: string): boolean { // are only applied when a character is found to actually have a // code point above 128. -/* tslint:disable: max-line-length */ let nonASCIIidentifierStartChars = -"\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b2\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua7ad\ua7b0\ua7b1\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab5f\uab64\uab65\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; + "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b2\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua7ad\ua7b0\ua7b1\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab5f\uab64\uab65\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; let nonASCIIidentifierChars = -"\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19b0-\u19c0\u19c8\u19c9\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2d\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; -/* tslint:enable: max-line-length */ + "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19b0-\u19c0\u19c8\u19c9\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2d\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; -const nonASCIIidentifierStart: RegExp = new RegExp( - "[" + nonASCIIidentifierStartChars + "]", -); -const nonASCIIidentifier: RegExp = new RegExp( - "[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]", -); +const nonASCIIidentifierStart: RegExp = new RegExp("[" + nonASCIIidentifierStartChars + "]"); +const nonASCIIidentifier: RegExp = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); nonASCIIidentifierStartChars = nonASCIIidentifierChars = null; @@ -425,7 +411,7 @@ const astralIdentifierStartCodes = [ 12, 221, 16355, - 541, + 541 ]; const astralIdentifierCodes = [ @@ -548,7 +534,7 @@ const astralIdentifierCodes = [ 4305, 6, 792618, - 239, + 239 ]; // This has a complexity linear to the value of the code. The @@ -558,10 +544,14 @@ function isInAstralSet(code: number, set: number[]): boolean { let pos = 0x10000; for (let i = 0; i < set.length; i += 2) { pos += set[i]; - if (pos > code) { return false; } + if (pos > code) { + return false; + } pos += set[i + 1]; - if (pos >= code) { return true; } + if (pos >= code) { + return true; + } } } @@ -569,16 +559,49 @@ function isInAstralSet(code: number, set: number[]): boolean { function isIdentifierStart(code: string): boolean { const c: number = code.charCodeAt(0); - if (c < 65) { return c === 36; } - if (c < 91) { return true; } - if (c < 97) { return c === 95; } - if (c < 123) { return true; } + if (c < 65) { + return c === 36; + } + if (c < 91) { + return true; + } + if (c < 97) { + return c === 95; + } + if (c < 123) { + return true; + } if (c <= 0xffff) { return c >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(c)); } return isInAstralSet(c, astralIdentifierStartCodes); } +function validationChar(charCode: number): boolean { + if (charCode < 48) { + return charCode === 36; + } + if (charCode < 58) { + return true; + } + if (charCode < 65) { + return false; + } + if (charCode < 91) { + return true; + } + if (charCode < 97) { + return charCode === 95; + } + if (charCode < 123) { + return true; + } + if (charCode <= 0xffff) { + return charCode >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(charCode)); + } + return isInAstralSet(charCode, astralIdentifierStartCodes) || isInAstralSet(charCode, astralIdentifierCodes); +} + // Test whether a given character is part of an identifier. function isIdentifierChar(code: string): boolean { @@ -595,24 +618,4 @@ function isIdentifierChar(code: string): boolean { } } -function validationChar(charCode: number): boolean { - if (charCode < 48) { return charCode === 36; } - if (charCode < 58) { return true; } - if (charCode < 65) { return false; } - if (charCode < 91) { return true; } - if (charCode < 97) { return charCode === 95; } - if (charCode < 123) { return true; } - if (charCode <= 0xffff) { - return charCode >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(charCode)); - } - return ( - isInAstralSet(charCode, astralIdentifierStartCodes) || - isInAstralSet(charCode, astralIdentifierCodes) - ); -} - -export { - isIdentifierChar, - isIdentifierStart, - isKeyword, -}; +export { isIdentifierChar, isIdentifierStart, isKeyword }; diff --git a/packages/webpack-scaffold/.eslintrc b/packages/webpack-scaffold/.eslintrc new file mode 100644 index 00000000000..365d9f85d71 --- /dev/null +++ b/packages/webpack-scaffold/.eslintrc @@ -0,0 +1,11 @@ +{ + "root": true, + "extends": [ + "../../.eslintrc.js", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"] +} diff --git a/packages/webpack-scaffold/__tests__/.eslintrc b/packages/webpack-scaffold/__tests__/.eslintrc new file mode 100644 index 00000000000..5d4340a351d --- /dev/null +++ b/packages/webpack-scaffold/__tests__/.eslintrc @@ -0,0 +1,7 @@ +{ + "root": true, + "extends": ["../.eslintrc"], + "rules": { + "@typescript-eslint/explicit-function-return-type": ["off"] + } +} diff --git a/packages/webpack-scaffold/index.ts b/packages/webpack-scaffold/index.ts index e666071046d..66303f5d989 100755 --- a/packages/webpack-scaffold/index.ts +++ b/packages/webpack-scaffold/index.ts @@ -1,23 +1,28 @@ import * as jscodeshift from "jscodeshift"; -export interface IInquirerScaffoldObject { +export interface InquirerScaffoldObject { type?: string; name: string; message: string; - choices?: ((answers: Object) => string) | string[]; - default?: string | number | boolean | string[] | number[] - | ((answers: Object) => (string | number | boolean | string[] | number[])); - validate?: ((input: string) => boolean | string); - when?: ((answers: Object) => boolean) | boolean; + choices?: ((answers: Record) => string) | string[]; + default?: + | string + | number + | boolean + | string[] + | number[] + | ((answers: Record) => string | number | boolean | string[] | number[]); + validate?: (input: string) => boolean | string; + when?: ((answers: Record) => boolean) | boolean; store?: boolean; filter?: (name: string) => string; } -export interface IInquirerList extends IInquirerScaffoldObject { +export interface InquirerList extends InquirerScaffoldObject { choices?: string[]; } -export interface IInquirerInput extends IInquirerScaffoldObject { +export interface InquirerInput extends InquirerScaffoldObject { validate?: (input: string) => string | boolean; } @@ -33,9 +38,11 @@ export function createDynamicPromise(arrOrString: string[] | string): string { if (Array.isArray(arrOrString)) { return ( "() => new Promise((resolve) => resolve([" + - arrOrString.map((func: string): string => { - return "'" + func + "'"; - }) + + arrOrString.map( + (func: string): string => { + return "'" + func + "'"; + } + ) + "]))" ); } else { @@ -67,63 +74,68 @@ export function createRequire(val: string): string { return `const ${val} = require('${val}');`; } -export function List(name: string, message: string, choices: string[]): IInquirerList { +export function List(name: string, message: string, choices: string[]): InquirerList { return { choices, message, name, - type: "list", + type: "list" }; } -export function RawList(name: string, message: string, choices: string[]): IInquirerList { +export function RawList(name: string, message: string, choices: string[]): InquirerList { return { choices, message, name, - type: "rawlist", + type: "rawlist" }; } -export function CheckList(name: string, message: string, choices: string[]): IInquirerList { +export function CheckList(name: string, message: string, choices: string[]): InquirerList { return { choices, message, name, - type: "checkbox", + type: "checkbox" }; } -export function Input(name: string, message: string): IInquirerInput { +export function Input(name: string, message: string): InquirerInput { return { message, name, - type: "input", + type: "input" }; } -export function InputValidate(name: string, message: string, cb?: (input: string) => string | boolean): IInquirerInput { +export function InputValidate(name: string, message: string, cb?: (input: string) => string | boolean): InquirerInput { return { message, name, type: "input", - validate: cb, + validate: cb }; } -export function Confirm(name: string, message: string, defaultChoice: boolean = true): IInquirerScaffoldObject { +export function Confirm(name: string, message: string, defaultChoice: boolean = true): InquirerScaffoldObject { return { default: defaultChoice, message, name, - type: "confirm", + type: "confirm" }; } -export function AutoComplete(name: string, message: string, options: object = {}) { - return Object.assign({ - message, - name, - type: "autocomplete", - }, options); +// TODO: to understand this type +// eslint-disable-next-line +export function AutoComplete(name: string, message: string, options: object = {}): any { + return Object.assign( + { + message, + name, + type: "autocomplete" + }, + options + ); } diff --git a/packages/webpack-scaffold/package-lock.json b/packages/webpack-scaffold/package-lock.json index 9ef7540efc7..c7674e406ae 100644 --- a/packages/webpack-scaffold/package-lock.json +++ b/packages/webpack-scaffold/package-lock.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/webpack-scaffold", - "version": "0.1.3", + "version": "0.1.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1248,7 +1248,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "parse-glob": { @@ -1439,7 +1439,7 @@ }, "temp": { "version": "0.8.3", - "resolved": "http://registry.npmjs.org/temp/-/temp-0.8.3.tgz", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", "requires": { "os-tmpdir": "^1.0.0", diff --git a/tsconfig.packages.json b/tsconfig.packages.json index 87eae9c0e2a..3f4af061bd6 100644 --- a/tsconfig.packages.json +++ b/tsconfig.packages.json @@ -1,5 +1,5 @@ { - "extends": "tsconfig.base.json", + "extends": "./tsconfig.base.json", "compilerOptions": { "declaration": true } diff --git a/tslint.json b/tslint.json deleted file mode 100644 index ee672ce2d93..00000000000 --- a/tslint.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], - "jsRules": {}, - "rules": { - "indent": [ - true, - "tabs", - 4 - ], - "ban-types": [ - false - ], - "no-console": [true, "log"] - }, - "rulesDirectory": [], - "linterOptions": { - "exclude": [ - "node_modules/**", - "packages/*/node_modules/**" - ] - } -}