diff --git a/.changeset/cuddly-seals-teach.md b/.changeset/cuddly-seals-teach.md new file mode 100644 index 0000000000..f88176bc58 --- /dev/null +++ b/.changeset/cuddly-seals-teach.md @@ -0,0 +1,5 @@ +--- +'@lion/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..8211d724eb 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/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/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/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..72c1cb6d9e --- /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(DRY) and tasks with extenders of lion, like ing-web. +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 export/import map support. 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(KISS), 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..6252cf21a9 --- /dev/null +++ b/packages-node/nodejs-helpers/package.json @@ -0,0 +1,61 @@ +{ + "name": "@lion/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..0f3f333207 --- /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 byNonEmptyLines = line => !!line?.trim(); + return text.split('\n').filter(byNonEmptyLines).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..4fa736c0c7 --- /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/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..70b0ff4759 --- /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/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..cd0c999610 --- /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/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/*'] });