diff --git a/packages/addon-shim/.eslintrc.js b/packages/addon-shim/.eslintrc.js index 0c4d920c8..cccddee6d 100644 --- a/packages/addon-shim/.eslintrc.js +++ b/packages/addon-shim/.eslintrc.js @@ -57,6 +57,10 @@ module.exports = { ecmaVersion: 2017, sourceType: 'module', }, + env: { + browser: false, + node: true, + }, plugins: ['@typescript-eslint'], extends: ['prettier'], rules: { diff --git a/packages/addon-shim/README.md b/packages/addon-shim/README.md index b36f2aa59..97451e31a 100644 --- a/packages/addon-shim/README.md +++ b/packages/addon-shim/README.md @@ -55,6 +55,16 @@ module.exports = addonV1Shim(__dirname, { This option _only_ works in non-embroider builds. Under embroider, apps just won't import the parts of your addon they don't want. +## addon-shim command + +The `addon-shim` command helps with common tasks in v2 addons. + +- linking up a test application that is embedded within your addon's repo +- synchronizing `devDependencies` from an embedded test application out into + your addon's actual package.json + +(You can avoid the need for both of these if you keep your addon and its test app as separate packages in a monorepo instead.) + ## Contributing See the top-level CONTRIBUTING.md in this monorepo. diff --git a/packages/addon-shim/addon-main.js b/packages/addon-shim/addon-main.js index 6a1a29de3..41fef8b7d 100644 --- a/packages/addon-shim/addon-main.js +++ b/packages/addon-shim/addon-main.js @@ -9,4 +9,8 @@ module.exports = { .find((a) => a.name === 'ember-auto-import') .registerV2Addon(this.parent.name, this.parent.pkg.root); }, + + includedCommands() { + return require('./src/commands').default; + }, }; diff --git a/packages/addon-shim/package.json b/packages/addon-shim/package.json index 5f6781e8f..30b6e91c0 100644 --- a/packages/addon-shim/package.json +++ b/packages/addon-shim/package.json @@ -17,16 +17,23 @@ "doc": "doc", "test": "tests" }, + "bin": { + "addon-shim": "./src/commands.js" + }, "scripts": { "prepare": "tsc" }, "dependencies": { "@embroider/shared-internals": "^0.44.2", "ember-auto-import": "^2.2.0", - "semver": "^7.3.5" + "fs-extra": "^10.0.0", + "semver": "^7.3.5", + "yargs": "^17.0.1" }, "devDependencies": { + "@types/fs-extra": "^9.0.12", "@types/semver": "^7.3.6", + "@types/yargs": "^17.0.2", "typescript": "*", "webpack": "^5" }, diff --git a/packages/addon-shim/src/commands.ts b/packages/addon-shim/src/commands.ts new file mode 100755 index 000000000..cbf9e574d --- /dev/null +++ b/packages/addon-shim/src/commands.ts @@ -0,0 +1,95 @@ +#!/usr/bin/env node + +import { ensureSymlinkSync, readJSONSync, writeJSONSync } from 'fs-extra'; +import { join } from 'path'; +import yargs from 'yargs/yargs'; +import type { Argv } from 'yargs'; + +function commonArgs(yargs: Argv) { + return yargs + .option('testAppDir', { + type: 'string', + description: 'Path to the test app', + default: 'test-app', + }) + .option('addonDir', { + type: 'string', + description: 'Path to your addon', + default: process.cwd(), + }); +} + +yargs(process.argv.slice(2)) + .scriptName('addon-shim') + .command( + 'link-test-app', + 'Ensures that a test app (that lives a subdir under an addon) has access to the addon and all appropriate deps', + (yargs) => commonArgs(yargs), + function (opts) { + let { testAppDir, addonDir } = opts; + ensureSymlinkSync( + join(addonDir, 'node_modules', '.bin'), + join(testAppDir, 'node_modules', '.bin'), + 'dir' + ); + ensureSymlinkSync( + addonDir, + join( + testAppDir, + 'node_modules', + readJSONSync(join(addonDir, 'package.json')).name + ) + ); + } + ) + .command( + 'sync-dev-deps', + `Synchronizes a test app's devDependencies into the parent addon's devDependencies`, + (yargs) => { + return commonArgs(yargs).option('lint', { + type: 'boolean', + description: + 'Instead of modifying package.json, print what would have been modified and exit with a failure if any changes are required.', + default: false, + }); + }, + function (opts) { + let { testAppDir, addonDir, lint } = opts; + let addonPkg = readJSONSync(join(addonDir, 'package.json')); + let testPkg = readJSONSync(join(testAppDir, 'package.json')); + let foundDifferences = false; + let devDeps: { [name: string]: string } = Object.assign( + {}, + addonPkg.devDependencies + ); + for (let [name, range] of Object.entries( + testPkg.devDependencies as { [name: string]: string } + )) { + if (name === addonPkg.name) { + continue; + } + if (devDeps[name] !== range) { + foundDifferences = true; + if (lint) { + console.error( + `test app depends on ${name} ${range} but that is not present in addon's devDependencies package.json` + ); + } else { + devDeps[name] = range; + } + } + } + if (!foundDifferences) { + return; + } + if (lint) { + process.exit(-1); + } else { + addonPkg.devDependencies = devDeps; + writeJSONSync(join(addonDir, 'package.json'), addonPkg, { spaces: 2 }); + } + } + ) + .demandCommand() + .strictCommands() + .help().argv; diff --git a/yarn.lock b/yarn.lock index 478ca795f..a421e6c98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2691,6 +2691,13 @@ dependencies: "@types/node" "*" +"@types/fs-extra@^9.0.12": + version "9.0.12" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.12.tgz#9b8f27973df8a7a3920e8461517ebf8a7d4fdfaf" + integrity sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw== + dependencies: + "@types/node" "*" + "@types/glob@*", "@types/glob@^7.1.1": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" @@ -2964,6 +2971,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yargs@^17.0.2": + version "17.0.2" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.2.tgz#8fb2e0f4cdc7ab2a1a570106e56533f31225b584" + integrity sha512-JhZ+pNdKMfB0rXauaDlrIvm+U7V4m03PPOSVoPS66z8gf+G4Z/UW8UlrVIj2MRQOBzuoEvYtjS0bqYwnpZaS9Q== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/eslint-plugin@^4.1.1": version "4.26.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz#b9c7313321cb837e2bf8bebe7acc2220659e67d3" @@ -5638,13 +5652,13 @@ browserify-zlib@^0.2.0: pako "~1.0.5" browserslist@^3.2.6, browserslist@^4.0.0, browserslist@^4.14.0, browserslist@^4.14.5, browserslist@^4.16.6: - version "4.16.8" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.8.tgz#cb868b0b554f137ba6e33de0ecff2eda403c4fb0" - integrity sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ== + version "4.17.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.0.tgz#1fcd81ec75b41d6d4994fb0831b92ac18c01649c" + integrity sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g== dependencies: - caniuse-lite "^1.0.30001251" + caniuse-lite "^1.0.30001254" colorette "^1.3.0" - electron-to-chromium "^1.3.811" + electron-to-chromium "^1.3.830" escalade "^3.1.1" node-releases "^1.1.75" @@ -5882,10 +5896,10 @@ caniuse-lite@^1.0.0: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001236.tgz#0a80de4cdf62e1770bb46a30d884fc8d633e3958" integrity sha512-o0PRQSrSCGJKCPZcgMzl5fUaj5xHe8qA2m4QRvnyY4e1lITqoNkr7q/Oh1NcpGSy0Th97UZ35yoKcINPoq7YOQ== -caniuse-lite@^1.0.30001251: - version "1.0.30001251" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001251.tgz#6853a606ec50893115db660f82c094d18f096d85" - integrity sha512-HOe1r+9VkU4TFmnU70z+r7OLmtR+/chB1rdcJUeQlAinjEeb0cKL20tlAtOagNZhbrtLnCvV19B4FmF1rgzl6A== +caniuse-lite@^1.0.30001254: + version "1.0.30001257" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz#150aaf649a48bee531104cfeda57f92ce587f6e5" + integrity sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA== capture-exit@^2.0.0: version "2.0.0" @@ -7104,10 +7118,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.811: - version "1.3.814" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.814.tgz#418fad80c3276a46103ca72a21a8290620d83c4a" - integrity sha512-0mH03cyjh6OzMlmjauGg0TLd87ErIJqWiYxMcOLKf5w6p0YEOl7DJAj7BDlXEFmCguY5CQaKVOiMjAMODO2XDw== +electron-to-chromium@^1.3.830: + version "1.3.840" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.840.tgz#3f2a1df97015d9b1db5d86a4c6bd4cdb920adcbb" + integrity sha512-yRoUmTLDJnkIJx23xLY7GbSvnmDCq++NSuxHDQ0jiyDJ9YZBUGJcrdUqm+ZwZFzMbCciVzfem2N2AWiHJcWlbw== elliptic@^6.5.3: version "6.5.4" @@ -15012,13 +15026,13 @@ qunit-dom@^1.6.0: ember-cli-version-checker "^5.1.1" qunit@^2.14.1, qunit@^2.16.0: - version "2.16.0" - resolved "https://registry.yarnpkg.com/qunit/-/qunit-2.16.0.tgz#b8ed63d512e5d4eaada5afc0c6c9e8b844181ba1" - integrity sha512-88x9t+rRMbB6IrCIUZvYU4pJy7NiBEv7SX8jD4LZAsIj+dV+kwGnFStOmPNvqa6HM96VZMD8CIIFKH2+3qvluA== + version "2.17.1" + resolved "https://registry.yarnpkg.com/qunit/-/qunit-2.17.1.tgz#1969efe4c9b776b4b8cd4fc2fb9634e8f762e177" + integrity sha512-Gx1tpSfYbjRe4TRKCVBLlnCaVThF5Pdnmbbv/zLFfgWKddeQHV/eNi1BG392hw4gEDh2sflMj8kmPJlT7+kVMA== dependencies: commander "7.1.0" node-watch "0.7.1" - tiny-glob "0.2.8" + tiny-glob "0.2.9" randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" @@ -16981,10 +16995,10 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" -tiny-glob@0.2.8: - version "0.2.8" - resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.8.tgz#b2792c396cc62db891ffa161fe8b33e76123e531" - integrity sha512-vkQP7qOslq63XRX9kMswlby99kyO5OvKptw7AMwBVMjXEI7Tb61eoI5DydyEMOseyGS5anDN1VPoVxEvH01q8w== +tiny-glob@0.2.9: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" + integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== dependencies: globalyzer "0.1.0" globrex "^0.1.2"