diff --git a/.changeset/cuddly-seals-teach.md b/.changeset/cuddly-seals-teach.md new file mode 100644 index 0000000000..00d7227179 --- /dev/null +++ b/.changeset/cuddly-seals-teach.md @@ -0,0 +1,5 @@ +--- +'@lion-tools/nodejs-helpers': patch +--- + +initial release diff --git a/.changeset/gentle-shoes-suffer.md b/.changeset/gentle-shoes-suffer.md new file mode 100644 index 0000000000..e64f7cd158 --- /dev/null +++ b/.changeset/gentle-shoes-suffer.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': minor +--- + +Bypass the requirement to support export & import map to consume @lion/ui diff --git a/.gitignore b/.gitignore index d6a00975d0..eaf3979df8 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,7 @@ _site-dev ## generated test fiels __output .wireit + +## generate-package-exports tests +packages/ui/new-exports/ +packages/ui/test-exports/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 06d28515ac..c58159827e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "@rocket/cli": "^0.10.1", "@rocket/launch": "^0.6.0", "@rocket/search": "^0.5.1", + "@types/chai-as-promised": "^7.1.5", "@types/chai-dom": "^0.0.8", "@types/convert-source-map": "^1.5.1", "@types/fs-extra": "^9.0.7", @@ -46,6 +47,7 @@ "bundlesize": "^1.0.0-beta.2", "cem-plugin-vs-code-custom-data-generator": "^1.4.1", "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", "chalk": "^4.1.0", "concurrently": "^5.2.0", "cross-env": "^7.0.2", @@ -2891,6 +2893,10 @@ "node": ">= 8.0.0" } }, + "node_modules/@lion-tools/nodejs-helpers": { + "resolved": "packages-node/nodejs-helpers", + "link": true + }, "node_modules/@lion/accordion": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@lion/accordion/-/accordion-0.9.0.tgz", @@ -3246,7 +3252,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -3259,7 +3264,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -3268,7 +3272,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -4258,6 +4261,15 @@ "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", "dev": true }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz", + "integrity": "sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, "node_modules/@types/chai-dom": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@types/chai-dom/-/chai-dom-0.0.8.tgz", @@ -6963,6 +6975,18 @@ "axe-core": "^4.3.3" } }, + "node_modules/chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 5" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9023,7 +9047,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, "dependencies": { "path-type": "^4.0.0" }, @@ -10480,7 +10503,6 @@ "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -14447,7 +14469,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "engines": { "node": ">= 8" } @@ -14567,7 +14588,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -16522,7 +16542,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -17623,7 +17642,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -18598,7 +18616,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -18815,7 +18832,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -22190,6 +22206,316 @@ "version": "0.5.3", "license": "MIT" }, + "packages-node/nodejs-helpers": { + "name": "@lion-tools/nodejs-helpers", + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5", + "es-module-lexer": "^0.3.6", + "globby": "^13.2.0", + "prettier": "^2.8.8" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "dependencies": { + "@babel/highlight": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", + "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "dependencies": { + "@babel/types": "^7.22.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/parser": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/traverse": { + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", + "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/types": "^7.22.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@babel/types": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "packages-node/nodejs-helpers/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "packages-node/nodejs-helpers/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "packages-node/nodejs-helpers/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "packages-node/nodejs-helpers/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "packages-node/nodejs-helpers/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "packages-node/nodejs-helpers/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "packages-node/nodejs-helpers/node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "packages-node/nodejs-helpers/node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages-node/nodejs-helpers/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "packages-node/nodejs-helpers/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "engines": { + "node": ">= 4" + } + }, + "packages-node/nodejs-helpers/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "packages-node/nodejs-helpers/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages-node/nodejs-helpers/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "packages-node/providence-analytics": { "version": "0.14.1", "license": "MIT", @@ -22384,7 +22710,7 @@ }, "packages/ajax": { "name": "@lion/ajax", - "version": "1.2.0", + "version": "1.2.1", "license": "MIT" }, "packages/singleton-manager": { @@ -24506,6 +24832,229 @@ "vary": "^1.1.2" } }, + "@lion-tools/nodejs-helpers": { + "version": "file:packages-node/nodejs-helpers", + "requires": { + "@babel/generator": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5", + "es-module-lexer": "^0.3.6", + "globby": "^13.2.0", + "prettier": "^2.8.8" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "requires": { + "@babel/highlight": "^7.22.5" + } + }, + "@babel/generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", + "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "requires": { + "@babel/types": "^7.22.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==" + }, + "@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "requires": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" + }, + "@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==" + }, + "@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "requires": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==" + }, + "@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + } + }, + "@babel/traverse": { + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", + "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/types": "^7.22.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==" + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "@lion/accordion": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@lion/accordion/-/accordion-0.9.0.tgz", @@ -24874,7 +25423,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -24883,14 +25431,12 @@ "@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" }, "@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -25740,6 +26286,15 @@ "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", "dev": true }, + "@types/chai-as-promised": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz", + "integrity": "sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, "@types/chai-dom": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@types/chai-dom/-/chai-dom-0.0.8.tgz", @@ -27982,6 +28537,15 @@ "axe-core": "^4.3.3" } }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "^1.0.2" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -29561,7 +30125,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, "requires": { "path-type": "^4.0.0" } @@ -30701,7 +31264,6 @@ "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, "requires": { "reusify": "^1.0.4" } @@ -33707,8 +34269,7 @@ "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, "mermaid": { "version": "9.3.0", @@ -33791,7 +34352,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "requires": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -35281,8 +35841,7 @@ "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, "pathval": { "version": "1.1.1", @@ -36231,8 +36790,7 @@ "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, "quick-lru": { "version": "4.0.1", @@ -37026,8 +37584,7 @@ "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, "rfdc": { "version": "1.3.0", @@ -37238,7 +37795,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "requires": { "queue-microtask": "^1.2.2" } diff --git a/package.json b/package.json index ee544568c4..a2ac109970 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "lint:versions": "node ./scripts/lint-versions.js", "prepare": "husky install", "release": "changeset publish", + "rm-all-node_modules": "npm exec --workspaces -- npx rimraf node_modules && npx rimraf node_modules", "rocket:build": "rocket build", "rocket:build:start": "web-dev-server --root-dir _site --open", "start": "rocket start", @@ -54,6 +55,7 @@ "@rocket/cli": "^0.10.1", "@rocket/launch": "^0.6.0", "@rocket/search": "^0.5.1", + "@types/chai-as-promised": "^7.1.5", "@types/chai-dom": "^0.0.8", "@types/convert-source-map": "^1.5.1", "@types/fs-extra": "^9.0.7", @@ -73,6 +75,7 @@ "bundlesize": "^1.0.0-beta.2", "cem-plugin-vs-code-custom-data-generator": "^1.4.1", "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", "chalk": "^4.1.0", "concurrently": "^5.2.0", "cross-env": "^7.0.2", diff --git a/packages-node/nodejs-helpers/CHANGELOG.md b/packages-node/nodejs-helpers/CHANGELOG.md new file mode 100644 index 0000000000..0da7b02237 --- /dev/null +++ b/packages-node/nodejs-helpers/CHANGELOG.md @@ -0,0 +1 @@ +# @lion/nodejs-helpers \ No newline at end of file diff --git a/packages-node/nodejs-helpers/README.md b/packages-node/nodejs-helpers/README.md new file mode 100644 index 0000000000..8238affba4 --- /dev/null +++ b/packages-node/nodejs-helpers/README.md @@ -0,0 +1,21 @@ +# Node.js helpers + +## Why? + +To share the common code we use in our Node.js scripts and products. + +## What? + +Node.js helpers is a public package, yet will always stay alpha version. +The idea here is to share common code and tasks with extenders of lion. +It consists of helpers and utilities, specific to our use cases, and not intended to be general purpose library. + +> **Example use case:** Assume package-x extends @lion/ui and package-x wants to bypass the requirement of package export support, by expanding the export map manually, and shipping along with the distributed release. In that case same bypass must be done at @lion/ui side as well. + +## How? + +Don't try to predict and write a utility function, in other words, add utilities if and when needed. + +Keep the APIs simple, extendable, yet specific to our use cases, in other words, don't write a generic utility. + +Put shared tasks between lion and ing-web(extends lion) here. diff --git a/packages-node/nodejs-helpers/package.json b/packages-node/nodejs-helpers/package.json new file mode 100644 index 0000000000..bfd855f664 --- /dev/null +++ b/packages-node/nodejs-helpers/package.json @@ -0,0 +1,61 @@ +{ + "name": "@lion-tools/nodejs-helpers", + "version": "0.0.0", + "description": "Node.js helpers that could be useful to extenders of lion", + "license": "MIT", + "author": "ing-bank", + "homepage": "https://github.com/ing-bank/lion/", + "repository": { + "type": "git", + "url": "https://github.com/ing-bank/lion.git", + "directory": "packages-node/nodejs-helpers" + }, + "type": "module", + "exports": { + ".": { + "types": "./dist-types/src/index.d.ts", + "default": "./src/index.js" + } + }, + "main": "index.js", + "files": [ + "dist-types", + "src" + ], + "scripts": { + "test": "npm run test:node", + "test:node": "mocha 'test-node/**/*.test.js'", + "test:watch": "npm run test:node -- --watch", + "types": "wireit" + }, + "dependencies": { + "@babel/generator": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5", + "es-module-lexer": "^0.3.6", + "globby": "^13.2.0", + "prettier": "^2.8.8" + }, + "keywords": [ + "lion-tools", + "nodejs-helpers" + ], + "publishConfig": { + "access": "public" + }, + "wireit": { + "types": { + "command": "tsc --build --pretty", + "files": [ + "src", + "test-node", + "types", + "tsconfig.json" + ], + "output": [ + "dist-types/**" + ] + } + } +} diff --git a/packages-node/nodejs-helpers/src/babel.js b/packages-node/nodejs-helpers/src/babel.js new file mode 100644 index 0000000000..59e40fc521 --- /dev/null +++ b/packages-node/nodejs-helpers/src/babel.js @@ -0,0 +1,50 @@ +import { parse } from '@babel/parser'; +import _traverse from '@babel/traverse'; +import _generate from '@babel/generator'; +// eslint-disable-next-line no-unused-vars +import * as babelTypes from '@babel/types'; + +const traverse = _traverse.default; +const generate = _generate.default; + +/** + * @typedef {import('@babel/parser').ParserOptions} ParserOptions + * @typedef {import('@babel/parser').ParseResult} AST + */ + +/** + * Parses `code` with the given `options` and returns the resulting AST + * @param {string} code + * @param {ParserOptions} [options] + * [{ sourceType: 'module', plugins: ['importMeta', 'dynamicImport', 'classProperties']}] + * - https://babeljs.io/docs/babel-parser#options + * @returns {AST} + */ +export const parseCode = (code, options = {}) => { + /** @type ParserOptions */ + const parserOptions = { + sourceType: 'module', + plugins: ['importMeta', 'dynamicImport', 'classProperties'], + ...options, + }; + const ast = parse(code, parserOptions); + return ast; +}; + +/** + * Transforms `code` by traversing the AST tree with the `visitor` + * @param {string} code + * @param {object} visitor https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md#visitors + * @param {ParserOptions} [parserOptions] + * [{ sourceType: 'module', plugins: ['importMeta', 'dynamicImport', 'classProperties']}] + * - https://babeljs.io/docs/babel-parser#options + * @returns {string} + */ +export const transformCode = (code, visitor, parserOptions = {}) => { + const ast = parseCode(code, parserOptions); + // @ts-ignore + traverse(ast, visitor); + // @ts-ignore + const { code: transformedCode } = generate(ast); + return transformedCode; +}; diff --git a/packages-node/nodejs-helpers/src/fs.js b/packages-node/nodejs-helpers/src/fs.js new file mode 100644 index 0000000000..7a9d011a09 --- /dev/null +++ b/packages-node/nodejs-helpers/src/fs.js @@ -0,0 +1,21 @@ +import { existsSync, mkdirSync } from 'fs'; +// @ts-ignore +import { mkdir } from 'fs/promises'; + +/** + * Makes a directory recursively & synchronously + * + * @param {string} dirPath + */ +export const makeDirSync = async dirPath => { + if (!existsSync(dirPath)) { + mkdirSync(dirPath, { recursive: true }); + } +}; + +/** + * Makes a directory recursively & asynchronously + * + * @param {string} dirPath + */ +export const makeDir = async dirPath => mkdir(dirPath, { recursive: true }); diff --git a/packages-node/nodejs-helpers/src/index.js b/packages-node/nodejs-helpers/src/index.js new file mode 100644 index 0000000000..7b205a3d30 --- /dev/null +++ b/packages-node/nodejs-helpers/src/index.js @@ -0,0 +1,14 @@ +export { parseCode, transformCode } from './babel.js'; +export { prettify } from './prettify.js'; +export { makeDirSync, makeDir } from './fs.js'; +export { + byStringAscendingSort, + camelToKebabCase, + getExportSpecifiersByFile, + asyncSerialForEach, + asyncConcurrentForEach, +} from './util.js'; + +// Tasks +export { bypassExportMap } from './tasks/bypass-export-map.js'; +export { bypassImportMap } from './tasks/bypass-import-map.js'; diff --git a/packages-node/nodejs-helpers/src/prettify.js b/packages-node/nodejs-helpers/src/prettify.js new file mode 100644 index 0000000000..ed8ca0eff1 --- /dev/null +++ b/packages-node/nodejs-helpers/src/prettify.js @@ -0,0 +1,72 @@ +// @ts-ignore +import prettier from 'prettier'; + +/** @typedef {import('prettier').Options } PrettierOptions */ + +export const ERROR_UNSUPPORTED_FILE_EXTENSION = 'Unsupported file extension'; + +/** + * Removes empty(whitespace only) lines from given string `text` + * @example removeEmptyLines('my\n\n\ntext\n\n') == 'my\ntext' + * @param {string} text + * @returns {string} + */ +const removeEmptyLines = text => { + // @ts-ignore + const isEmptyLine = line => !line?.trim(); + return text.split('\n').filter(isEmptyLine).join('\n'); +}; + +/** + * Get prettier `parser` for given `fileExtension` + * @example getPrettierParser('md') == 'markdown' + * @param {string} fileExtension + * @returns {string} + * @throws ERROR_UNSUPPORTED_FILE_EXTENSION + */ +const getPrettierParser = fileExtension => { + // See more parser options here: https://prettier.io/docs/en/options.html#parser + const FILE_EXTENSION_TO_PARSER = { + js: 'babel', + cjs: 'babel', + mjs: 'babel', + md: 'markdown', + html: 'html', + json: 'json', + css: 'css', + yaml: 'yaml', + yml: 'yaml', + }; + const parser = FILE_EXTENSION_TO_PARSER[fileExtension]; + if (!parser) { + throw new Error(ERROR_UNSUPPORTED_FILE_EXTENSION); + } + return parser; +}; + +/** + * Prettifies text using the prettier. + * - Supported file extensions are: js | cjs | mjs | md | html | json | css | yaml | yml + * @example + * prettify('some js code'); + * prettify('some html', 'html'); + * prettify('some-markdown', 'md', {printWidth: 120}); + * @param {string} text + * @param {string} fileExtension ['js'] + * @param {PrettierOptions} [options] [{ printWidth = 100, singleQuote = true }] + * @see {@link https://prettier.io/docs/en/options.html} + * @returns {string} + * @throws ERROR_UNSUPPORTED_FILE_EXTENSION + */ +export const prettify = (text, fileExtension = 'js', options = {}) => { + const parser = getPrettierParser(fileExtension); + const textToFormat = parser === 'html' ? removeEmptyLines(text) : text; + /** @type PrettierOptions */ + const prettierOptions = { + parser, + printWidth: 100, + singleQuote: true, + ...options, + }; + return prettier.format(textToFormat, prettierOptions); +}; diff --git a/packages-node/nodejs-helpers/src/tasks/bypass-export-map.js b/packages-node/nodejs-helpers/src/tasks/bypass-export-map.js new file mode 100644 index 0000000000..5f6659301f --- /dev/null +++ b/packages-node/nodejs-helpers/src/tasks/bypass-export-map.js @@ -0,0 +1,193 @@ +import path from 'path'; +import { globby } from 'globby'; +// @ts-ignore +import { createRequire } from 'module'; +// @ts-ignore +import { readFile, writeFile } from 'fs/promises'; +import { existsSync, lstatSync } from 'fs'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { isImportDeclaration, isExportDeclaration } from '@babel/types'; +import { prettify } from '../prettify.js'; +import { makeDir } from '../fs.js'; +import { asyncConcurrentForEach } from '../util.js'; +import { transformCode } from '../babel.js'; + +export const ERROR_CAN_NOT_OVERWRITE_EXISTING_FILE = 'Can not overwrite existing file'; +export const ERROR_CAN_NOT_RESOLVE_SOURCE = 'Can not resolve source'; +export const ERROR_ADJUSTED_SOURCE_IS_INVALID = 'Adjusted source is invalid'; // TODO: Can not adjust source maybe +export const ERROR_CAN_NOT_ACCESS_PACKAGE_DIR = 'Can not access package directory'; + +/** + * Checks whether given `filePath` points to a directory + * @param {string} filePath + * @returns {boolean} + */ +const isDirectorySync = filePath => lstatSync(filePath).isDirectory(); + +/** + * Adjusts the source by updating the relative path value + * + * @param {string} initialSource Example: `import sth from source;` + * @param {string} exportsDir + * @param {string} outputDir + * @returns {string} + */ +const adjustSource = (initialSource, exportsDir, outputDir) => { + const initialSourcePath = path.resolve(exportsDir, initialSource); + const adjustedSource = path.relative(outputDir, initialSourcePath); + return adjustedSource.startsWith('.') ? adjustedSource : `./${adjustedSource}`; +}; + +/** + * Checks if initialSource and adjustedSource point to resolvable resources, + * and if they are pointing to the same resource + * + * @param {string} initialSource Example: `import sth from initialSource;` + * @param {string} adjustedSource Example: `import sth from adjustedSource;` + * @param {string} exportsDir + * @param {string} outputDir + * @returns {boolean} + * @throws ERROR_CAN_NOT_RESOLVE_SOURCE + */ +const resolvesToSameResource = (initialSource, adjustedSource, exportsDir, outputDir) => { + try { + const require = createRequire(import.meta.url); + const initialResolvedPath = require.resolve(initialSource, { paths: [exportsDir] }); + const adjustedResolvedPath = require.resolve(adjustedSource, { paths: [outputDir] }); + return initialResolvedPath === adjustedResolvedPath; + } catch (error) { + throw new Error(ERROR_CAN_NOT_RESOLVE_SOURCE); + } +}; + +/** + * Adjusts the source value of an import/export declaration. + * Example: `import sth from source;` + * The new source value is calculated relative to the outputDir. + * And checked to be resolvable to the same resource with Node.js require.resolve + * + * https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md#visitors + * @param {string} exportsDir + * @param {string} outputDir + * @throws ERROR_ADJUSTED_SOURCE_IS_INVALID + */ +const getAdjustImportExportVisitor = (exportsDir, outputDir) => ({ + // @ts-ignore + enter({ node }) { + const isImportOrExportNode = isImportDeclaration(node) || isExportDeclaration(node); + if (!isImportOrExportNode) { + return; + } + // @ts-ignore + const { source } = node; + const initialSource = source?.value; + if (!initialSource) { + return; + } + const isRelativeSource = initialSource?.startsWith('.'); + if (!isRelativeSource) { + return; + } + const adjustedSource = adjustSource(initialSource, exportsDir, outputDir); + if (!resolvesToSameResource(initialSource, adjustedSource, exportsDir, outputDir)) { + throw new Error(ERROR_ADJUSTED_SOURCE_IS_INVALID); + } + // register the new source value + source.value = adjustedSource; + }, +}); + +/** + * Adjusts relative paths for a given fileName, + * and writes it with the same fileName under the outputDir + * + * @param {string} fileName + * @param {string} exportsDir + * @param {string} outputDir + * @throws ERROR_CAN_NOT_OVERWRITE_EXISTING_FILE + */ +const generateAdjustedExportFile = async (fileName, exportsDir, outputDir) => { + const outputPath = path.resolve(outputDir, fileName); + if (existsSync(outputPath)) { + throw new Error(ERROR_CAN_NOT_OVERWRITE_EXISTING_FILE); + } + const inputPath = path.resolve(exportsDir, fileName); + const initialCode = await readFile(inputPath, 'utf-8'); + const adjustImportExportVisitor = getAdjustImportExportVisitor(exportsDir, outputDir); + const adjustedCode = transformCode(initialCode, adjustImportExportVisitor); + return writeFile(outputPath, prettify(adjustedCode)); +}; + +/** + * @param {string} packageDir + * @param {string} exportMapKey + * @param {string} exportMapValue + * @returns {Promise} + */ +const bypassExportMapItem = async (packageDir, exportMapKey, exportMapValue) => { + const exportMapValuePath = path.join(packageDir, exportMapValue); + const exportMapKeyPath = path.join(packageDir, exportMapKey); + const searchPattern = isDirectorySync(exportMapValuePath) + ? path.join(exportMapValuePath, '**', '*.js') + : exportMapValuePath; + const filePaths = await globby(searchPattern); + // @ts-ignore + return asyncConcurrentForEach(filePaths, async filePath => { + const outputFilePath = filePath.replace(exportMapValuePath, exportMapKeyPath); + const outputDir = path.dirname(outputFilePath); + const fileName = path.basename(filePath); + const exportsDir = path.dirname(filePath); + await makeDir(outputDir); + return generateAdjustedExportFile(fileName, exportsDir, outputDir); + }); +}; + +/** + * Stringify export values of the packageJson.exports + * @param {{default:string, module: string}|string} value Value of an packageJson.exports entry + * @returns {string} String value of exports map + */ +// @ts-ignore +const stringifyExportValue = value => value?.default || value?.module || value; + +/** + * Normalizes the export values of packageJson.exports by stringifying the values + * @param {object} exports packageJson.exports + * @param {string[]} ignoredExportMapKeys + * @returns {object} packageJson.exports in which all the values are string + */ +const normalizeExportMap = (exports, ignoredExportMapKeys = []) => { + // @ts-ignore + const removeFirstStar = str => str.replace('*', ''); + return Object.entries(exports || {}) + .filter(([exportKey]) => !ignoredExportMapKeys.includes(exportKey)) + .reduce((accumulator, [key, value]) => { + accumulator[removeFirstStar(key)] = removeFirstStar(stringifyExportValue(value)); + return accumulator; + }, {}); +}; + +/** + * Bypasses export map + * + * @param {string} packageDir + * @param {{ignoredExportMapKeys?: string[]}} [options] + * @throws ERROR_CAN_NOT_ACCESS_PACKAGE_DIR + * @returns {Promise} + */ +export const bypassExportMap = async (packageDir, options = {}) => { + const ignoredExportMapKeys = options.ignoredExportMapKeys || []; + if (!existsSync(packageDir)) { + throw new Error(ERROR_CAN_NOT_ACCESS_PACKAGE_DIR); + } + const require = createRequire(import.meta.url); + // eslint-disable-next-line import/no-dynamic-require + const { exports } = require(path.resolve(packageDir, 'package.json')); + const exportMap = normalizeExportMap(exports, ignoredExportMapKeys); + return asyncConcurrentForEach( + Object.entries(exportMap), + // @ts-ignore + ([exportMapKey, exportMapValue]) => + bypassExportMapItem(packageDir, exportMapKey, exportMapValue), + ); +}; diff --git a/packages-node/nodejs-helpers/src/tasks/bypass-import-map.js b/packages-node/nodejs-helpers/src/tasks/bypass-import-map.js new file mode 100644 index 0000000000..2fea1db447 --- /dev/null +++ b/packages-node/nodejs-helpers/src/tasks/bypass-import-map.js @@ -0,0 +1,149 @@ +import path from 'path'; +import { globby } from 'globby'; +// @ts-ignore +import { createRequire } from 'module'; +// @ts-ignore +import { readFile, writeFile } from 'fs/promises'; +import { existsSync } from 'fs'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { isImportDeclaration } from '@babel/types'; +import { prettify } from '../prettify.js'; +import { asyncConcurrentForEach } from '../util.js'; +import { transformCode } from '../babel.js'; + +export const ERROR_CAN_NOT_RESOLVE_SOURCE = 'Can not resolve source'; +export const ERROR_CAN_NOT_ACCESS_PACKAGE_DIR = 'Can not access package directory'; + +/** + * Gets the matching pattern and replacement from importMap for the given source + * + * @param {string} source + * @param {string} fileDir + * @param {string} packageDir + * @param {object} importMap + * @returns + */ +const getMatchingPatternAndReplacement = (source, fileDir, packageDir, importMap) => { + const [pattern, replacement] = + Object.entries(importMap).find(([key]) => source.includes(key)) || []; + if (!pattern || !replacement) { + return { pattern: '', replacement: '' }; + } + const fileToPackageRelativeDir = path.relative(fileDir, packageDir) || '.'; + // TODO: Is there a case where replacement does not start with ./, check spec. + const updatedReplacement = replacement.replace('./', `${fileToPackageRelativeDir}/`); + return { pattern, replacement: updatedReplacement }; +}; + +/** + * Adjusts the source value of an import declaration. + * Example: `import sth from source;` + * The new source value is calculated with respect to `imports` defined in package.json + * And checked to be resolvable with Node.js require.resolve + * + * https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md#visitors + * @param {string} filePath + * @param {string} packageDir + * @param {object} importMap + * @throws ERROR_ADJUSTED_SOURCE_IS_INVALID + */ +const getAdjustImportVisitor = (filePath, packageDir, importMap) => ({ + // @ts-ignore + enter({ node }) { + const isImportNode = isImportDeclaration(node); + if (!isImportNode) { + return; + } + // @ts-ignore + const { source } = node; + const initialSource = source?.value; + if (!initialSource) { + return; + } + const fileDir = path.dirname(filePath); + // @ts-ignore + const { pattern, replacement } = getMatchingPatternAndReplacement( + initialSource, + fileDir, + packageDir, + importMap, + ); + if (pattern === replacement) { + return; + } + const adjustedSource = initialSource.replace(pattern, replacement); + try { + const require = createRequire(import.meta.url); + require.resolve(adjustedSource, { paths: [fileDir] }); + } catch (error) { + throw new Error(ERROR_CAN_NOT_RESOLVE_SOURCE); + } + // register the new source value + source.value = adjustedSource; + }, +}); + +/** + * Bypasses import map for a file given by `filePath` + * + * @param {string} filePath + * @param {string} packageDir + * @param {object} importMap + * @returns {Promise} + */ +const bypassImportMapForFile = async (filePath, packageDir, importMap) => { + const initialCode = await readFile(filePath, 'utf-8'); + const adjustImportVisitor = getAdjustImportVisitor(filePath, packageDir, importMap); + const updatedCode = transformCode(initialCode, adjustImportVisitor); + const prettyInitialCode = prettify(initialCode); + const prettyUpdatedCode = prettify(updatedCode); + if (prettyInitialCode === prettyUpdatedCode) { + return Promise.resolve(); + } + return writeFile(filePath, prettyUpdatedCode); +}; + +/** + * Normalizes the import map by removing the stars + * + * @param {object} imports packageJson.imports + * @returns {object} + */ +const normalizeImportMap = imports => { + // @ts-ignore + const removeFirstStar = str => str.replace('*', ''); + return Object.entries(imports || {}).reduce((accumulator, [key, value]) => { + // @ts-ignore + accumulator[removeFirstStar(key)] = removeFirstStar(value); + return accumulator; + }, {}); +}; + +/** + * Bypasses import map for a package given by `packageDir` + * + * @param {string} packageDir + * @param {{ignoredDirs?: string[]}} [options] + * @throws ERROR_CAN_NOT_ACCESS_PACKAGE_DIR + * @returns {Promise} + */ +export const bypassImportMap = async (packageDir, options = {}) => { + const ignoredDirs = options.ignoredDirs || ['node_modules']; + if (!existsSync(packageDir)) { + throw new Error(ERROR_CAN_NOT_ACCESS_PACKAGE_DIR); + } + const require = createRequire(import.meta.url); + // eslint-disable-next-line import/no-dynamic-require + const { imports } = require(path.resolve(packageDir, 'package.json')); + if (!imports) { + return Promise.resolve(); + } + const ignoredPatterns = ignoredDirs.map(dir => `!${path.join(packageDir, dir)}`); + const searchPatterns = [path.join(packageDir, '**', '*.js'), ...ignoredPatterns]; + const filePaths = await globby(searchPatterns); + const importMap = normalizeImportMap(imports); + // @ts-ignore + return asyncConcurrentForEach(filePaths, filePath => + bypassImportMapForFile(filePath, packageDir, importMap), + ); +}; diff --git a/packages-node/nodejs-helpers/src/util.js b/packages-node/nodejs-helpers/src/util.js new file mode 100644 index 0000000000..0ac4cc9056 --- /dev/null +++ b/packages-node/nodejs-helpers/src/util.js @@ -0,0 +1,76 @@ +// @ts-ignore +import { readFile } from 'fs/promises'; +// @ts-ignore +import { init, parse } from 'es-module-lexer'; + +/** + * Call `asyncFn` function serially for each item in the `list` + * @param {any[]} list List of items + * @param {function} asyncFn asyncFn(item, index, list) + * @returns {Promise} + */ +export const asyncSerialForEach = async (list, asyncFn) => { + for (let index = 0; index < list.length; index += 1) { + await asyncFn(list[index], index, list); + } + return Promise.resolve(); +}; + +/** + * Call `asyncFn` function concurrently for each item in the `list` + * @param {any[]} list List of items + * @param {function} asyncFn asyncFn(item, index, list) + * @returns {Promise} + */ +export const asyncConcurrentForEach = async (list, asyncFn) => { + const tasks = []; + for (let index = 0; index < list.length; index += 1) { + tasks.push(asyncFn(list[index], index, list)); + } + return Promise.all(tasks); +}; + +/** + * Sort `str1`, `str2` by ascending order [A to Z] + * @example + * byStringAscendingSort('apple', 'banana') == -1 + * byStringAscendingSort('banana', 'banana') == 0 + * byStringAscendingSort('cucumber', 'banana') == 1 + * @param {string} str1 First string + * @param {string} str2 Second string + * @returns {number} + */ +export const byStringAscendingSort = (str1, str2) => str1.localeCompare(str2); + +/** + * Convert `name` from camelCase to kebab-case + * @example + * camelToKebabCase('exportedFileNames') == 'exported-file-names' + * camelToKebabCase('IngCalendar', '_') == 'ing_calendar' + * @param {string} name + * @param {string} [separator] ['-'] + * @returns {string} + */ +export const camelToKebabCase = (name, separator = '-') => { + // @ts-ignore + const toKebabCase = (letter, index) => { + const isLowerCaseLetter = letter === letter.toLowerCase(); + return isLowerCaseLetter ? letter : `${index === 0 ? '' : separator}${letter.toLowerCase()}`; + }; + return name.split('').map(toKebabCase).join(''); +}; + +/** + * Get export specifiers, declared in a js file given with `filePath` + * @see {@link https://www.npmjs.com/package/es-module-lexer} + * @param {string} absFilePath + * @returns {Promise} + */ +export const getExportSpecifiersByFile = async absFilePath => { + await init; + const exportFile = await readFile(absFilePath, 'utf-8'); + // eslint-disable-next-line + const [_, exports] = parse(exportFile); + // @ts-ignore + return exports; +}; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/.gitignore b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/.gitignore new file mode 100644 index 0000000000..825c8a3017 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/.gitignore @@ -0,0 +1 @@ +./*.js \ No newline at end of file diff --git a/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/components/combobox/combobox.js b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/components/combobox/combobox.js new file mode 100644 index 0000000000..85f7504b9f --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/components/combobox/combobox.js @@ -0,0 +1 @@ +export { LionCombobox } from '@lion/ui/combobox.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/components/my-component-list/my-component-list.js b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/components/my-component-list/my-component-list.js new file mode 100644 index 0000000000..253ae90079 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/components/my-component-list/my-component-list.js @@ -0,0 +1,2 @@ +export { MyComponent } from './my-component.js'; +export { LionField as __LionField } from '@lion/ui/form-core.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/components/my-component-list/my-component.js b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/components/my-component-list/my-component.js new file mode 100644 index 0000000000..4ab57aea56 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/components/my-component-list/my-component.js @@ -0,0 +1,3 @@ +import { LitElement } from 'lit'; + +export class MyComponent extends LitElement {} diff --git a/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/exports/combobox.js b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/exports/combobox.js new file mode 100644 index 0000000000..a5f0f0e63e --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/exports/combobox.js @@ -0,0 +1,5 @@ +import path from 'path'; + +export const { basename } = path; +export { MatchesOption } from '@lion/ui/combobox.js'; +export { LionCombobox } from '../components/combobox/combobox.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/exports/my-component-list.js b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/exports/my-component-list.js new file mode 100644 index 0000000000..2e2c61b508 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/exports/my-component-list.js @@ -0,0 +1,2 @@ +export { MyComponent, __LionField } from '../components/my-component-list/my-component-list.js'; +export { basename as __basename } from './combobox.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/package.json b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/package.json new file mode 100644 index 0000000000..5bc09b88fa --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/components-with-3rd-party-imports/package.json @@ -0,0 +1,9 @@ +{ + "private": true, + "name": "simple-export-map", + "author": "ing-bank", + "type": "module", + "exports": { + "./*": "./exports/*" + } +} diff --git a/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/.gitignore b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/.gitignore new file mode 100644 index 0000000000..852c38adf5 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/.gitignore @@ -0,0 +1,2 @@ +./*.js +./define/ \ No newline at end of file diff --git a/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/components/combobox/combobox.js b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/components/combobox/combobox.js new file mode 100644 index 0000000000..85f7504b9f --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/components/combobox/combobox.js @@ -0,0 +1 @@ +export { LionCombobox } from '@lion/ui/combobox.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/exports/combobox.js b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/exports/combobox.js new file mode 100644 index 0000000000..b807cdb42b --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/exports/combobox.js @@ -0,0 +1 @@ +export { LionCombobox } from '../components/combobox/combobox.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/exports/define/helpers/debug.js b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/exports/define/helpers/debug.js new file mode 100644 index 0000000000..8f9ac78f66 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/exports/define/helpers/debug.js @@ -0,0 +1 @@ +export const { debug } = console; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/exports/define/helpers/log.js b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/exports/define/helpers/log.js new file mode 100644 index 0000000000..8f9ac78f66 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/exports/define/helpers/log.js @@ -0,0 +1 @@ +export const { debug } = console; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/package.json b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/package.json new file mode 100644 index 0000000000..57d0439ef6 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/deep-search-exports-directory/package.json @@ -0,0 +1,9 @@ +{ + "private": true, + "name": "deep-search-exports-directory", + "author": "ing-bank", + "type": "module", + "exports": { + "./*": "./exports/*" + } +} diff --git a/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-resolve-resource/.gitignore b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-resolve-resource/.gitignore new file mode 100644 index 0000000000..825c8a3017 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-resolve-resource/.gitignore @@ -0,0 +1 @@ +./*.js \ No newline at end of file diff --git a/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-resolve-resource/exports/combobox.js b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-resolve-resource/exports/combobox.js new file mode 100644 index 0000000000..33f465ecf2 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-resolve-resource/exports/combobox.js @@ -0,0 +1 @@ +export { LionCombobox } from './this-file-does-not-exist.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-resolve-resource/package.json b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-resolve-resource/package.json new file mode 100644 index 0000000000..c8d143e4f1 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-resolve-resource/package.json @@ -0,0 +1,9 @@ +{ + "private": true, + "name": "error-can-not-resolve-resource", + "author": "ing-bank", + "type": "module", + "exports": { + "./combobox.js": "./exports/combobox.js" + } +} diff --git a/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/.gitignore b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/.gitignore new file mode 100644 index 0000000000..0f73c99cf2 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/.gitignore @@ -0,0 +1,2 @@ +./*.js +!combobox.js \ No newline at end of file diff --git a/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/combobox.js b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/combobox.js new file mode 100644 index 0000000000..5d6bd8fd3c --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/combobox.js @@ -0,0 +1 @@ +export { LionCombobox } from './components/combobox/combobox.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/components/combobox/combobox.js b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/components/combobox/combobox.js new file mode 100644 index 0000000000..85f7504b9f --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/components/combobox/combobox.js @@ -0,0 +1 @@ +export { LionCombobox } from '@lion/ui/combobox.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/exports/combobox.js b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/exports/combobox.js new file mode 100644 index 0000000000..b807cdb42b --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/exports/combobox.js @@ -0,0 +1 @@ +export { LionCombobox } from '../components/combobox/combobox.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/package.json b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/package.json new file mode 100644 index 0000000000..a44d97af75 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/error-can-not-write-existing-file/package.json @@ -0,0 +1,9 @@ +{ + "private": true, + "name": "error-can-not-overwrite-existing-file", + "author": "ing-bank", + "type": "module", + "exports": { + "./combobox.js": "./exports/combobox.js" + } +} diff --git a/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/.gitignore b/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/.gitignore new file mode 100644 index 0000000000..00b74c2c87 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/.gitignore @@ -0,0 +1,2 @@ +./*.js +./calendar-translations/ \ No newline at end of file diff --git a/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/components/accordion/accordion.js b/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/components/accordion/accordion.js new file mode 100644 index 0000000000..5f072a4bb4 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/components/accordion/accordion.js @@ -0,0 +1 @@ +export const accordionVar = 'accordion'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/components/calendar/translations/en.js b/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/components/calendar/translations/en.js new file mode 100644 index 0000000000..46fc92a758 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/components/calendar/translations/en.js @@ -0,0 +1,4 @@ +export default { + nextMonth: 'Next month', + previousMonth: 'Previous month', +}; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/exports/accordion.js b/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/exports/accordion.js new file mode 100644 index 0000000000..c6656e705b --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/exports/accordion.js @@ -0,0 +1 @@ +export { accordionVar } from '../components/accordion/accordion.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/package.json b/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/package.json new file mode 100644 index 0000000000..375a74553d --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/multiple-export-map-items/package.json @@ -0,0 +1,12 @@ +{ + "private": true, + "name": "multiple-export-map-items", + "author": "ing-bank", + "type": "module", + "exports": { + "./*": { + "module": "./exports/*" + }, + "./calendar-translations/*": "./components/calendar/translations/*" + } +} diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/.gitignore b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/.gitignore new file mode 100644 index 0000000000..825c8a3017 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/.gitignore @@ -0,0 +1 @@ +./*.js \ No newline at end of file diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/components/accordion/accordion.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/components/accordion/accordion.js new file mode 100644 index 0000000000..5f072a4bb4 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/components/accordion/accordion.js @@ -0,0 +1 @@ +export const accordionVar = 'accordion'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/components/combobox/combobox.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/components/combobox/combobox.js new file mode 100644 index 0000000000..76763a036e --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/components/combobox/combobox.js @@ -0,0 +1 @@ +export const comboboxVar = 'combobox'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/exports/accordion.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/exports/accordion.js new file mode 100644 index 0000000000..c6656e705b --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/exports/accordion.js @@ -0,0 +1 @@ +export { accordionVar } from '../components/accordion/accordion.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/exports/combobox.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/exports/combobox.js new file mode 100644 index 0000000000..c0f8072398 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/exports/combobox.js @@ -0,0 +1 @@ +export { comboboxVar } from '../components/combobox/combobox.js'; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/package.json b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/package.json new file mode 100644 index 0000000000..835a6e0615 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-export-map/package.json @@ -0,0 +1,11 @@ +{ + "private": true, + "name": "simple-export-map", + "author": "ing-bank", + "type": "module", + "exports": { + "./*": { + "default": "./exports/*" + } + } +} diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/.gitignore b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/.gitignore new file mode 100644 index 0000000000..41039ce7ca --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/.gitignore @@ -0,0 +1 @@ +./__icon-backwards-compatibility/ \ No newline at end of file diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/icons/line/arrows/arrow-circle-down.svg.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/icons/line/arrows/arrow-circle-down.svg.js new file mode 100755 index 0000000000..ec88acc8bd --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/icons/line/arrows/arrow-circle-down.svg.js @@ -0,0 +1,3 @@ +import i from '#icon/oj-icons/outline/arrows/arrow_circle_down_outline.svg.js'; + +export default i; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/icons/line/arrows/arrow-circle-up.svg.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/icons/line/arrows/arrow-circle-up.svg.js new file mode 100755 index 0000000000..fe09170c93 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/icons/line/arrows/arrow-circle-up.svg.js @@ -0,0 +1,3 @@ +import i from '#icon/oj-icons/outline/arrows/arrow_circle_up_outline.svg.js'; + +export default i; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-icons/outline/arrows/arrow_circle_down_outline.svg.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-icons/outline/arrows/arrow_circle_down_outline.svg.js new file mode 100644 index 0000000000..6562dc5dd2 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-icons/outline/arrows/arrow_circle_down_outline.svg.js @@ -0,0 +1,4 @@ +import i from '#icon/oj-sun-icons/arrows/arrowCircleDownFilled.svg.js'; + +// @deprecated +export default i; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-icons/outline/arrows/arrow_circle_up_outline.svg.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-icons/outline/arrows/arrow_circle_up_outline.svg.js new file mode 100644 index 0000000000..53400d18ed --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-icons/outline/arrows/arrow_circle_up_outline.svg.js @@ -0,0 +1,4 @@ +import i from '#icon/oj-sun-icons/arrows/arrowCircleUpFilled.svg.js'; + +// @deprecated +export default i; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-sun-icons/arrows/arrowCircleDownFilled.svg.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-sun-icons/arrows/arrowCircleDownFilled.svg.js new file mode 100644 index 0000000000..cbf45794db --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-sun-icons/arrows/arrowCircleDownFilled.svg.js @@ -0,0 +1,2 @@ +export default /** @param {(strings: TemplateStringsArray, ... expr: string[]) => string} tag */ tag => + tag``; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-sun-icons/arrows/arrowCircleUpFilled.svg.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-sun-icons/arrows/arrowCircleUpFilled.svg.js new file mode 100644 index 0000000000..a4ba29cc59 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/components/icon/oj-sun-icons/arrows/arrowCircleUpFilled.svg.js @@ -0,0 +1,2 @@ +export default /** @param {(strings: TemplateStringsArray, ... expr: string[]) => string} tag */ tag => + tag``; diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/package.json b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/package.json new file mode 100644 index 0000000000..ff8055688b --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/package.json @@ -0,0 +1,14 @@ +{ + "private": true, + "name": "simple-import-map", + "author": "ing-bank", + "type": "module", + "exports": { + "./__icon-backwards-compatibility/icons/*": "./components/icon/icons/*", + "./__icon-backwards-compatibility/oj-icons/*": "./components/icon/oj-icons/*" + }, + "imports": { + "#icon/oj-icons/*": "./components/icon/oj-icons/*", + "#icon/oj-sun-icons/*": "./components/icon/oj-sun-icons/*" + } +} diff --git a/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/scripts/someScript.js b/packages-node/nodejs-helpers/test-node/fixtures/simple-import-map/scripts/someScript.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages-node/nodejs-helpers/test-node/index.test.js b/packages-node/nodejs-helpers/test-node/index.test.js new file mode 100644 index 0000000000..0ede78230c --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/index.test.js @@ -0,0 +1,33 @@ +import { expect } from 'chai'; +import { + parseCode, + transformCode, + prettify, + makeDirSync, + makeDir, + byStringAscendingSort, + camelToKebabCase, + getExportSpecifiersByFile, + asyncSerialForEach, + asyncConcurrentForEach, + // Tasks + bypassImportMap, + bypassExportMap, +} from '../src/index.js'; + +describe('Public API', () => { + it('should expose the agreed public API', () => { + expect(parseCode).to.exist; + expect(transformCode).to.exist; + expect(prettify).to.exist; + expect(makeDirSync).to.exist; + expect(makeDir).to.exist; + expect(byStringAscendingSort).to.exist; + expect(camelToKebabCase).to.exist; + expect(getExportSpecifiersByFile).to.exist; + expect(asyncSerialForEach).to.exist; + expect(asyncConcurrentForEach).to.exist; + expect(bypassImportMap).to.exist; + expect(bypassExportMap).to.exist; + }); +}); diff --git a/packages-node/nodejs-helpers/test-node/tasks/bypass-export-map.test.js b/packages-node/nodejs-helpers/test-node/tasks/bypass-export-map.test.js new file mode 100644 index 0000000000..499dc92438 --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/tasks/bypass-export-map.test.js @@ -0,0 +1,328 @@ +import path from 'path'; +import { createRequire } from 'module'; +import { readFile, rm } from 'fs/promises'; +import { existsSync } from 'fs'; +import { globby } from 'globby'; +import chai from 'chai'; +// eslint-disable-next-line import/no-unresolved +import chaiAsPromised from 'chai-as-promised'; +import { isImportDeclaration, isExportDeclaration } from '@babel/types'; +import { + prettify, + asyncConcurrentForEach, + byStringAscendingSort, + getExportSpecifiersByFile, + transformCode, + bypassExportMap, +} from '@lion-tools/nodejs-helpers'; + +import { + ERROR_CAN_NOT_OVERWRITE_EXISTING_FILE, + ERROR_CAN_NOT_RESOLVE_SOURCE, + // ERROR_ADJUSTED_SOURCE_IS_INVALID, + ERROR_CAN_NOT_ACCESS_PACKAGE_DIR, +} from '../../src/tasks/bypass-export-map.js'; + +// Register chai-as-promised plugin for async/await support. +chai.use(chaiAsPromised); +const { expect } = chai; + +/** + * Get sorted js files for the given directory + * + * @param {string} searchPattern The globby search pattern + * @returns {Promise} + */ +const getSortedJsFileNamesInDir = async searchPattern => { + const files = await globby(searchPattern); + return files.map(file => path.basename(file)).sort(byStringAscendingSort); +}; + +/** + * + * @param {string[]} fsPaths FileSystem paths to remove with `rm -rf` + * @returns {Promise} + */ +const rmRecursiveForce = async fsPaths => { + // @ts-ignore + const deleteFsPath = fsPath => rm(fsPath, { recursive: true, force: true }); + return asyncConcurrentForEach(fsPaths, deleteFsPath); +}; + +/** + * Get export specifiers for all the js files in a directory given with `dirPath` + * @param {string} dirPath Directory path + * @returns {Promise} + */ +const getExportSpecifiersByDir = async dirPath => { + const fileNames = await getSortedJsFileNamesInDir(path.join(dirPath, '*.js')); + // @ts-ignore + const exports = await asyncConcurrentForEach(fileNames, async fileName => { + const filePath = path.resolve(dirPath, fileName); + return getExportSpecifiersByFile(filePath); + }); + return exports.flat().sort(byStringAscendingSort); +}; + +/** + * Get the paths returned by require.resolve for each + * import or export declaration found in the given `code` + * @param {string} code + * @param {{paths: string[]}} options + * @returns {Promise} + */ +const getRequireResolvePaths = async (code, { paths = [] }) => { + /** + * @type {string[]} + */ + const resolvedPaths = []; + const visitor = { + // @ts-ignore + enter({ node }) { + const isImportExportNode = isImportDeclaration(node) || isExportDeclaration(node); + if (!isImportExportNode) { + return; + } + // @ts-ignore + const { source } = node; + // example: `import sth from source.value;` + const initialSource = source?.value; + const isRelativeSource = initialSource?.startsWith('.'); + if (!isRelativeSource) { + return; + } + const require = createRequire(import.meta.url); + const resolvedPath = require.resolve(initialSource, { paths }); + resolvedPaths.push(resolvedPath); + }, + }; + transformCode(code, visitor); + return resolvedPaths; +}; + +describe('bypassExportMap simple export map case', async () => { + const packageDir = 'test-node/fixtures/simple-export-map'; + const exportsDirPath = path.resolve(packageDir, 'exports'); + const outputDirPath = path.resolve(packageDir); + const cleanup = async () => + rmRecursiveForce([ + path.join(outputDirPath, 'accordion.js'), + path.join(outputDirPath, 'combobox.js'), + ]); + + before(async () => { + await cleanup(); + await bypassExportMap(packageDir); + }); + + after(async () => { + await cleanup(); + }); + + it(`creates a corresponding export file with the same name, for every .js file under exports directory`, async () => { + // Given + const initialFileNames = await getSortedJsFileNamesInDir(path.join(exportsDirPath, '*.js')); + // When + const adjustedFileNames = await getSortedJsFileNamesInDir(path.join(outputDirPath, '*.js')); + // Then + expect(initialFileNames).to.deep.equal(adjustedFileNames); + }); + + it('generated exports have all the exports, the original exports have', async () => { + // Given + const initialExports = await getExportSpecifiersByDir(exportsDirPath); + // When + const adjustedExports = await getExportSpecifiersByDir(outputDirPath); + // Then + expect(initialExports).to.deep.equal(adjustedExports); + }); + + it('All the generated export/import point to resolvable sources and they match with original exports', async () => { + const fileNames = await getSortedJsFileNamesInDir(exportsDirPath); + // @ts-ignore + await asyncConcurrentForEach(fileNames, async fileName => { + // Given + const initialCode = await readFile(path.resolve(exportsDirPath, fileName), 'utf-8'); + const initialResolvePaths = await getRequireResolvePaths(initialCode, { + paths: [exportsDirPath], + }); + // When + const adjustedCode = await readFile(path.resolve(outputDirPath, fileName), 'utf-8'); + const adjustedResolvePaths = await getRequireResolvePaths(adjustedCode, { + paths: [outputDirPath], + }); + // Then + expect(initialResolvePaths).to.deep.equal(adjustedResolvePaths); + }); + }); +}); + +describe('components import other components and native + 3rd party imports', () => { + const packageDir = 'test-node/fixtures/components-with-3rd-party-imports'; + const outputDirPath = path.resolve(packageDir); + const cleanup = async () => + rmRecursiveForce([ + path.join(outputDirPath, 'my-component-list.js'), + path.join(outputDirPath, 'combobox.js'), + ]); + + before(async () => { + await cleanup(); + await bypassExportMap(packageDir); + }); + + after(async () => { + await cleanup(); + }); + + // TODO: Fails on windows with: + // Error: ENOENT: no such file or directory, open + // 'D:\a\lion\lion\packages-node\nodejs-helpers\test-node\fixtures\components-with-3rd-party-imports\combobox.js' + it('does not update the source for 3rd party import/export', async () => { + // Given + const expectedCode = ` + import path from 'path'; + export const { basename } = path; + export { MatchesOption } from '@lion/ui/combobox.js'; + export { LionCombobox } from './components/combobox/combobox.js'; + `; + // When + if (process.platform !== 'win32') { + // FIXME: skipping test for windows case + const adjustedCode = await readFile( + path.resolve(path.join(outputDirPath, 'combobox.js')), + 'utf-8', + ); + // Then + expect(prettify(adjustedCode)).to.equal(prettify(expectedCode)); + } + }); + + // TODO: Fails on windows with: + // Error: ENOENT: no such file or directory, open + // 'D:\a\lion\lion\packages-node\nodejs-helpers\test-node\fixtures\components-with-3rd-party-imports\my-component-list.js' + it('transforms relative path values as expected', async () => { + // Given + const expectedCode = ` + export { MyComponent, __LionField } from './components/my-component-list/my-component-list.js'; + export { basename as __basename } from './exports/combobox.js'; + `; + // When + if (process.platform !== 'win32') { + // FIXME: skipping test for windows case + const adjustedCode = await readFile( + path.resolve(path.join(outputDirPath, 'my-component-list.js')), + 'utf-8', + ); + + // Then + expect(prettify(adjustedCode)).to.equal(prettify(expectedCode)); + } + }); +}); + +describe('multiple export map items', () => { + const packageDir = 'test-node/fixtures/multiple-export-map-items'; + const outputDirPath = path.resolve(packageDir); + const cleanup = async () => + rmRecursiveForce([ + path.join(outputDirPath, 'calendar-translations'), + path.join(outputDirPath, 'accordion.js'), + ]); + + before(async () => { + await cleanup(); + await bypassExportMap(packageDir); + }); + + after(async () => { + await cleanup(); + }); + + it('processes both export map items', async () => { + expect(existsSync(path.join(outputDirPath, 'accordion.js'))); + expect(existsSync(path.join(outputDirPath, '/calendar-translations/en.js'))); + }); +}); + +describe('deep search exports directory', () => { + const packageDir = 'test-node/fixtures/deep-search-exports-directory'; + const outputDirPath = path.resolve(packageDir); + const cleanup = async () => + rmRecursiveForce([ + path.join(outputDirPath, 'define/helpers/'), + path.join(outputDirPath, 'combobox.js'), + ]); + + before(async () => { + await cleanup(); + await bypassExportMap(packageDir); + }); + + after(async () => { + await cleanup(); + }); + + it('creates found nested directories under outputDir', async () => { + expect(existsSync(path.join(outputDirPath, 'combobox.js'))); + expect(existsSync(path.join(outputDirPath, 'define/helpers/logger.js'))); + }); +}); + +describe('Exceptions/error handling', () => { + it('throws in case packageDir can not be accessed', async () => { + // Given + // packageDir does not exist on the filesystem, intentionally + const packageDir = 'test-node/fixtures/error-can-not-access-package-dir'; + // When + await expect( + bypassExportMap(packageDir), + // Then + ).to.be.rejectedWith(ERROR_CAN_NOT_ACCESS_PACKAGE_DIR); + }); + + // TODO: Fails on windows with: + // AssertionError: expected promise to be rejected with an error including + // 'Can not overwrite existing file' but it was fulfilled with [[]] + it('throws in case a file with the same name exists under outputDir', async () => { + // Given + const packageDir = 'test-node/fixtures/error-can-not-write-existing-file'; + // When + if (process.platform !== 'win32') { + // FIXME: skipping test for windows case + await expect( + bypassExportMap(packageDir), + // Then + ).to.be.rejectedWith(ERROR_CAN_NOT_OVERWRITE_EXISTING_FILE); + } + }); + + context('can not resolve resource', async () => { + // Given + const packageDir = 'test-node/fixtures/error-can-not-resolve-resource'; + const outputDirPath = path.resolve(packageDir); + const cleanup = async () => rmRecursiveForce([path.join(outputDirPath, 'combobox.js')]); + + before(async () => { + await cleanup(); + }); + + after(async () => { + await cleanup(); + }); + + // TODO: Fails on windows with: + // AssertionError: expected promise to be rejected with an error including + // 'Can not resolve source' but it was fulfilled with [[]] + it('throws in case require.resolve can not resolve source', async () => { + // When + if (process.platform !== 'win32') { + // FIXME: skipping test for windows case + await expect( + bypassExportMap(packageDir), + // Then + ).to.be.rejectedWith(ERROR_CAN_NOT_RESOLVE_SOURCE); + } + }); + }); +}); diff --git a/packages-node/nodejs-helpers/test-node/tasks/bypass-import-map.test.js b/packages-node/nodejs-helpers/test-node/tasks/bypass-import-map.test.js new file mode 100644 index 0000000000..33e03532eb --- /dev/null +++ b/packages-node/nodejs-helpers/test-node/tasks/bypass-import-map.test.js @@ -0,0 +1,120 @@ +import path from 'path'; +import { promisify } from 'util'; +import { exec } from 'child_process'; +import { readFile } from 'fs/promises'; +import chai from 'chai'; +// eslint-disable-next-line import/no-unresolved +import chaiAsPromised from 'chai-as-promised'; +import { prettify, bypassImportMap } from '@lion-tools/nodejs-helpers'; + +import { + // ERROR_CAN_NOT_RESOLVE_SOURCE, + ERROR_CAN_NOT_ACCESS_PACKAGE_DIR, +} from '../../src/tasks/bypass-import-map.js'; + +// Register chai-as-promised plugin for async/await support. +chai.use(chaiAsPromised); +const { expect } = chai; + +// create async version of exec +const asyncExec = promisify(exec); + +describe('Bypass import map task', () => { + const packageDir = 'test-node/fixtures/simple-import-map'; + const outputDirPath = path.resolve(packageDir); + + before(async () => { + const ignoredDirs = ['node_modules', 'dist-types', 'scripts']; + await bypassImportMap(packageDir, { ignoredDirs }); + }); + + after(async () => { + await asyncExec(`git checkout ${path.join(packageDir, 'components', 'icon')}`); + }); + + // TODO: Fails on windows with: + // + expected - actual + // -import i from '#icon/oj-icons/outline/arrows/arrow_circle_down_outline.svg.js'; + // +import i from '../../../../../components/icon/oj-icons/outline/arrows/arrow_circle_down_outline.svg.js'; + it('resolves imports for #icon/oj-icons/', async () => { + // Given + const expectedCodeArrowDown = ` + import i from '../../../../../components/icon/oj-icons/outline/arrows/arrow_circle_down_outline.svg.js'; + export default i; + `; + const expectedCodeArrowUp = ` + import i from '../../../../../components/icon/oj-icons/outline/arrows/arrow_circle_up_outline.svg.js'; + export default i; + `; + // When + const adjustedCodeArrowDown = await readFile( + path.join(outputDirPath, 'components/icon/icons/line/arrows/arrow-circle-down.svg.js'), + 'utf-8', + ); + const adjustedCodeArrowUp = await readFile( + path.join(outputDirPath, 'components/icon/icons/line/arrows/arrow-circle-up.svg.js'), + 'utf-8', + ); + // Then + if (process.platform !== 'win32') { + // FIXME: skipping test for windows case + expect(prettify(adjustedCodeArrowDown)).to.equal(prettify(expectedCodeArrowDown)); + expect(prettify(adjustedCodeArrowUp)).to.equal(prettify(expectedCodeArrowUp)); + } + }); + + // TODO: Fails on windows with: + // + expected - actual + // -import i from '#icon/oj-sun-icons/arrows/arrowCircleDownFilled.svg.js'; + // +import i from '../../../../../components/icon/oj-sun-icons/arrows/arrowCircleDownFilled.svg.js'; + it('resolves imports for #icon/oj-sun-icons/', async () => { + // Given + const expectedCodeArrowDown = ` + import i from '../../../../../components/icon/oj-sun-icons/arrows/arrowCircleDownFilled.svg.js'; + + // @deprecated + export default i; + `; + const expectedCodeArrowUp = ` + import i from '../../../../../components/icon/oj-sun-icons/arrows/arrowCircleUpFilled.svg.js'; + + // @deprecated + export default i; + `; + // When + const adjustedCodeArrowDown = await readFile( + path.join( + outputDirPath, + 'components/icon/oj-icons/outline/arrows/arrow_circle_down_outline.svg.js', + ), + 'utf-8', + ); + const adjustedCodeArrowUp = await readFile( + path.resolve( + path.join( + outputDirPath, + 'components/icon/oj-icons/outline/arrows/arrow_circle_up_outline.svg.js', + ), + ), + 'utf-8', + ); + // Then + if (process.platform !== 'win32') { + // FIXME: skipping test for windows case + expect(prettify(adjustedCodeArrowDown)).to.equal(prettify(expectedCodeArrowDown)); + expect(prettify(adjustedCodeArrowUp)).to.equal(prettify(expectedCodeArrowUp)); + } + }); +}); + +describe('Exceptions/error handling', () => { + it('throws in case packageDir can not be accessed', async () => { + // Given (packageDir does not exist on the filesystem, intentionally) + const packageDir = 'test-node/fixtures/error-can-not-access-package-dir'; + // When + await expect( + bypassImportMap(packageDir), + // Then + ).to.be.rejectedWith(ERROR_CAN_NOT_ACCESS_PACKAGE_DIR); + }); +}); diff --git a/packages-node/nodejs-helpers/tsconfig.json b/packages-node/nodejs-helpers/tsconfig.json new file mode 100644 index 0000000000..c2bef97cf1 --- /dev/null +++ b/packages-node/nodejs-helpers/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist-types", + "rootDir": "." + }, + "include": ["src/**/*.js", "types"], + "exclude": ["dist-types"] +} diff --git a/packages-node/nodejs-helpers/types/.gitkeep b/packages-node/nodejs-helpers/types/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/ui/package.json b/packages/ui/package.json index c2982083d8..a71f974641 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -29,19 +29,23 @@ "./docs/*": "./docs/*" }, "files": [ + "*.js", "components", "custom-elements.json", "dist-types", "docs", - "exports" + "exports", + "!custom-elements-manifest.config.js" ], "scripts": { + "create-npm-publish-docs": "node ./scripts/create-docs-for-npm-publish.js", "custom-elements-manifest": "custom-elements-manifest analyze --litelement --exclude \"docs/**/*\" \"test-helpers/**/*\"", "debug": "cd ../../ && npm run debug", "debug:firefox": "cd ../../ && npm run debug:firefox", "debug:webkit": "cd ../../ && npm run debug:webkit", + "generate-lion-exports": "node ./scripts/generate-lion-exports.js", "publish-docs": "node ../../packages-node/publish-docs/src/cli.js --github-url https://github.com/ing-bank/lion/ --git-root-dir ../../", - "prepublishOnly": "npm run types && node ./scripts/create-docs-for-npm-publish.js && npm run publish-docs && npm run custom-elements-manifest", + "prepublishOnly": "npm run types && npm run create-npm-publish-docs && npm run publish-docs && npm run custom-elements-manifest && npm run generate-lion-exports", "test": "cd ../../ && npm run test:browser", "types": "wireit", "types-check-only": "tsc --project tsconfig-check-only.json", diff --git a/packages/ui/scripts/generate-lion-exports.js b/packages/ui/scripts/generate-lion-exports.js new file mode 100644 index 0000000000..1a9ec5d39c --- /dev/null +++ b/packages/ui/scripts/generate-lion-exports.js @@ -0,0 +1,13 @@ +#!/usr/bin/env node +/** + * This script aims to bypass the requirement of package export support, + * by expanding export map manually, and shipping along with the distributed release + */ +import path from 'path'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { bypassImportMap, bypassExportMap } from '@lion-tools/nodejs-helpers'; + +// relative to process.cwd(), aka directory where the script is running from +const packageDir = path.resolve(process.env.PACKAGE_DIR || '.'); +await bypassImportMap(packageDir, { ignoredDirs: ['node_modules', 'scripts', 'docs'] }); +await bypassExportMap(packageDir, { ignoredExportMapKeys: ['./docs/*'] });