mirror of
https://github.com/microsoft/playwright.git
synced 2025-01-05 19:04:43 +03:00
fix(test loader): implement tsconfig paths through resolveFilename (#12357)
This uses `Module._resolveFilename` to intercept module resolution and check `tsconfig.paths` similarly to pirates usage ot `Module._compile`. Previously, we resolved during compilation that required reproducible resolution due to caching. Now we can resolve as we go and support all `tsconfig.paths`.
This commit is contained in:
parent
6438aed36c
commit
3c2bca2768
217
package-lock.json
generated
217
package-lock.json
generated
@ -2088,21 +2088,6 @@
|
||||
"object.assign": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-module-resolver": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-4.1.0.tgz",
|
||||
"integrity": "sha512-MlX10UDheRr3lb3P0WcaIdtCSRlxdQsB1sBqL7W0raF070bGl1HQQq5K3T2vf2XAYie+ww+5AKC/WrkjRO2knA==",
|
||||
"dependencies": {
|
||||
"find-babel-config": "^1.2.0",
|
||||
"glob": "^7.1.6",
|
||||
"pkg-up": "^3.1.0",
|
||||
"reselect": "^4.0.0",
|
||||
"resolve": "^1.13.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@ -3785,34 +3770,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/find-babel-config": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.2.0.tgz",
|
||||
"integrity": "sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==",
|
||||
"dependencies": {
|
||||
"json5": "^0.5.1",
|
||||
"path-exists": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/find-babel-config/node_modules/json5": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
||||
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/find-babel-config/node_modules/path-exists": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
|
||||
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/find-cache-dir": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
|
||||
@ -4422,6 +4379,7 @@
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz",
|
||||
"integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has": "^1.0.3"
|
||||
},
|
||||
@ -5541,6 +5499,7 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@ -5624,7 +5583,8 @@
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "4.0.0",
|
||||
@ -5697,73 +5657,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-up": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
|
||||
"integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
|
||||
"dependencies": {
|
||||
"find-up": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-up/node_modules/find-up": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||
"dependencies": {
|
||||
"locate-path": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-up/node_modules/locate-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
|
||||
"dependencies": {
|
||||
"p-locate": "^3.0.0",
|
||||
"path-exists": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-up/node_modules/p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"dependencies": {
|
||||
"p-try": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-up/node_modules/p-locate": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
|
||||
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
|
||||
"dependencies": {
|
||||
"p-limit": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-up/node_modules/path-exists": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
|
||||
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"resolved": "packages/playwright",
|
||||
"link": true
|
||||
@ -6179,15 +6072,11 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reselect": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz",
|
||||
"integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ=="
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
|
||||
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.2.0",
|
||||
"path-parse": "^1.0.6"
|
||||
@ -7512,7 +7401,6 @@
|
||||
"@babel/plugin-syntax-optional-catch-binding": "7.8.3",
|
||||
"@babel/plugin-transform-modules-commonjs": "7.16.8",
|
||||
"@babel/preset-typescript": "7.16.7",
|
||||
"babel-plugin-module-resolver": "4.1.0",
|
||||
"colors": "1.4.0",
|
||||
"commander": "8.3.0",
|
||||
"debug": "4.3.3",
|
||||
@ -8328,7 +8216,6 @@
|
||||
"@babel/plugin-syntax-optional-catch-binding": "7.8.3",
|
||||
"@babel/plugin-transform-modules-commonjs": "7.16.8",
|
||||
"@babel/preset-typescript": "7.16.7",
|
||||
"babel-plugin-module-resolver": "4.1.0",
|
||||
"colors": "1.4.0",
|
||||
"commander": "8.3.0",
|
||||
"debug": "4.3.3",
|
||||
@ -9173,18 +9060,6 @@
|
||||
"object.assign": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-module-resolver": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-4.1.0.tgz",
|
||||
"integrity": "sha512-MlX10UDheRr3lb3P0WcaIdtCSRlxdQsB1sBqL7W0raF070bGl1HQQq5K3T2vf2XAYie+ww+5AKC/WrkjRO2knA==",
|
||||
"requires": {
|
||||
"find-babel-config": "^1.2.0",
|
||||
"glob": "^7.1.6",
|
||||
"pkg-up": "^3.1.0",
|
||||
"reselect": "^4.0.0",
|
||||
"resolve": "^1.13.1"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@ -10452,27 +10327,6 @@
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"find-babel-config": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.2.0.tgz",
|
||||
"integrity": "sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==",
|
||||
"requires": {
|
||||
"json5": "^0.5.1",
|
||||
"path-exists": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"json5": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
||||
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
|
||||
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
|
||||
}
|
||||
}
|
||||
},
|
||||
"find-cache-dir": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
|
||||
@ -10931,6 +10785,7 @@
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz",
|
||||
"integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
@ -11767,7 +11622,8 @@
|
||||
"p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true
|
||||
},
|
||||
"param-case": {
|
||||
"version": "3.0.4",
|
||||
@ -11830,7 +11686,8 @@
|
||||
"path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
@ -11884,54 +11741,6 @@
|
||||
"find-up": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"pkg-up": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
|
||||
"integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
|
||||
"requires": {
|
||||
"find-up": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"find-up": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||
"requires": {
|
||||
"locate-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
|
||||
"requires": {
|
||||
"p-locate": "^3.0.0",
|
||||
"path-exists": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"requires": {
|
||||
"p-try": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
|
||||
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
|
||||
"requires": {
|
||||
"p-limit": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
|
||||
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
|
||||
}
|
||||
}
|
||||
},
|
||||
"playwright": {
|
||||
"version": "file:packages/playwright",
|
||||
"requires": {
|
||||
@ -12276,15 +12085,11 @@
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||
"dev": true
|
||||
},
|
||||
"reselect": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz",
|
||||
"integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
|
||||
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.2.0",
|
||||
"path-parse": "^1.0.6"
|
||||
|
@ -15,10 +15,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { fork } from 'child_process';
|
||||
import url from 'url';
|
||||
|
||||
if (process.env.PW_EXPERIMENTAL_TS_ESM) {
|
||||
const NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --experimental-loader=${require.resolve('@playwright/test/lib/experimentalLoader')}`;
|
||||
const NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --experimental-loader=${url.pathToFileURL(require.resolve('@playwright/test/lib/experimentalLoader'))}`;
|
||||
const innerProcess = fork(require.resolve('./innerCli'), process.argv.slice(2), {
|
||||
env: { ...process.env, NODE_OPTIONS }
|
||||
});
|
||||
|
@ -45,7 +45,6 @@
|
||||
"@babel/plugin-syntax-optional-catch-binding": "7.8.3",
|
||||
"@babel/plugin-transform-modules-commonjs": "7.16.8",
|
||||
"@babel/preset-typescript": "7.16.7",
|
||||
"babel-plugin-module-resolver": "4.1.0",
|
||||
"colors": "1.4.0",
|
||||
"commander": "8.3.0",
|
||||
"debug": "4.3.3",
|
||||
|
@ -15,28 +15,27 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import { transformHook } from './transform';
|
||||
import url from 'url';
|
||||
import { transformHook, resolveHook } from './transform';
|
||||
|
||||
async function resolve(specifier: string, context: { parentURL: string }, defaultResolve: any) {
|
||||
if (specifier.endsWith('.js') || specifier.endsWith('.ts') || specifier.endsWith('.mjs'))
|
||||
return defaultResolve(specifier, context, defaultResolve);
|
||||
let url = new URL(specifier, context.parentURL).toString();
|
||||
url = url.substring('file://'.length);
|
||||
for (const extension of ['.ts', '.js', '.tsx', '.jsx']) {
|
||||
if (fs.existsSync(url + extension))
|
||||
return defaultResolve(specifier + extension, context, defaultResolve);
|
||||
if (context.parentURL && context.parentURL.startsWith('file://')) {
|
||||
const filename = url.fileURLToPath(context.parentURL);
|
||||
const resolved = resolveHook(filename, specifier);
|
||||
if (resolved !== undefined)
|
||||
specifier = url.pathToFileURL(resolved).toString();
|
||||
}
|
||||
return defaultResolve(specifier, context, defaultResolve);
|
||||
}
|
||||
|
||||
async function load(url: string, context: any, defaultLoad: any) {
|
||||
if (url.endsWith('.ts') || url.endsWith('.tsx')) {
|
||||
const filename = url.substring('file://'.length);
|
||||
async function load(moduleUrl: string, context: any, defaultLoad: any) {
|
||||
if (moduleUrl.startsWith('file://') && (moduleUrl.endsWith('.ts') || moduleUrl.endsWith('.tsx'))) {
|
||||
const filename = url.fileURLToPath(moduleUrl);
|
||||
const code = fs.readFileSync(filename, 'utf-8');
|
||||
const source = transformHook(code, filename, true);
|
||||
return { format: 'module', source };
|
||||
}
|
||||
return defaultLoad(url, context, defaultLoad);
|
||||
return defaultLoad(moduleUrl, context, defaultLoad);
|
||||
}
|
||||
|
||||
module.exports = { resolve, load };
|
||||
|
@ -23,16 +23,15 @@ import * as sourceMapSupport from 'source-map-support';
|
||||
import * as url from 'url';
|
||||
import type { Location } from './types';
|
||||
import { tsConfigLoader, TsConfigLoaderResult } from './third_party/tsconfig-loader';
|
||||
import Module from 'module';
|
||||
|
||||
const version = 6;
|
||||
const version = 7;
|
||||
const cacheDir = process.env.PWTEST_CACHE_DIR || path.join(os.tmpdir(), 'playwright-transform-cache');
|
||||
const sourceMaps: Map<string, string> = new Map();
|
||||
|
||||
type ParsedTsConfigData = {
|
||||
absoluteBaseUrl: string,
|
||||
singlePath: { [key: string]: string },
|
||||
hash: string,
|
||||
alias: { [key: string]: string | ((s: string[]) => string) },
|
||||
absoluteBaseUrl: string;
|
||||
paths: { key: string, values: string[] }[];
|
||||
};
|
||||
const cachedTSConfigs = new Map<string, ParsedTsConfigData | undefined>();
|
||||
|
||||
@ -55,9 +54,8 @@ sourceMapSupport.install({
|
||||
}
|
||||
});
|
||||
|
||||
function calculateCachePath(tsconfigData: ParsedTsConfigData | undefined, content: string, filePath: string): string {
|
||||
function calculateCachePath(content: string, filePath: string): string {
|
||||
const hash = crypto.createHash('sha1')
|
||||
.update(tsconfigData?.hash || '')
|
||||
.update(process.env.PW_TEST_SOURCE_TRANSFORM || '')
|
||||
.update(process.env.PW_EXPERIMENTAL_TS_ESM ? 'esm' : 'no_esm')
|
||||
.update(content)
|
||||
@ -69,43 +67,12 @@ function calculateCachePath(tsconfigData: ParsedTsConfigData | undefined, conten
|
||||
}
|
||||
|
||||
function validateTsConfig(tsconfig: TsConfigLoaderResult): ParsedTsConfigData | undefined {
|
||||
if (!tsconfig.tsConfigPath || !tsconfig.paths || !tsconfig.baseUrl)
|
||||
if (!tsconfig.tsConfigPath || !tsconfig.baseUrl)
|
||||
return;
|
||||
|
||||
const paths = tsconfig.paths;
|
||||
// Path that only contains "*", ".", "/" and "\" is too ambiguous.
|
||||
const ambiguousPath = Object.keys(paths).find(key => key.match(/^[*./\\]+$/));
|
||||
if (ambiguousPath)
|
||||
return;
|
||||
const multiplePath = Object.keys(paths).find(key => paths[key].length > 1);
|
||||
if (multiplePath)
|
||||
return;
|
||||
// Only leave a single path mapping.
|
||||
const singlePath = Object.fromEntries(Object.entries(paths).map(([key, values]) => ([key, values[0]])));
|
||||
// Make 'baseUrl' absolute, because it is relative to the tsconfig.json, not to cwd.
|
||||
const absoluteBaseUrl = path.resolve(path.dirname(tsconfig.tsConfigPath), tsconfig.baseUrl);
|
||||
const hash = JSON.stringify({ absoluteBaseUrl, singlePath });
|
||||
|
||||
const alias: ParsedTsConfigData['alias'] = {};
|
||||
for (const [key, value] of Object.entries(singlePath)) {
|
||||
const regexKey = '^' + key.replace('*', '.*');
|
||||
alias[regexKey] = ([name]) => {
|
||||
let relative: string;
|
||||
if (key.endsWith('/*'))
|
||||
relative = value.substring(0, value.length - 1) + name.substring(key.length - 1);
|
||||
else
|
||||
relative = value;
|
||||
relative = relative.replace(/\//g, path.sep);
|
||||
return path.resolve(absoluteBaseUrl, relative);
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
absoluteBaseUrl,
|
||||
singlePath,
|
||||
hash,
|
||||
alias,
|
||||
};
|
||||
const paths = tsconfig.paths || { '*': ['*'] };
|
||||
return { absoluteBaseUrl, paths: Object.entries(paths).map(([key, values]) => ({ key, values })) };
|
||||
}
|
||||
|
||||
function loadAndValidateTsconfigForFile(file: string): ParsedTsConfigData | undefined {
|
||||
@ -123,6 +90,35 @@ function loadAndValidateTsconfigForFile(file: string): ParsedTsConfigData | unde
|
||||
const pathSeparator = process.platform === 'win32' ? ';' : ':';
|
||||
const scriptPreprocessor = process.env.PW_TEST_SOURCE_TRANSFORM ?
|
||||
require(process.env.PW_TEST_SOURCE_TRANSFORM) : undefined;
|
||||
const builtins = new Set(Module.builtinModules);
|
||||
|
||||
export function resolveHook(filename: string, specifier: string): string | undefined {
|
||||
if (builtins.has(specifier))
|
||||
return;
|
||||
const isTypeScript = filename.endsWith('.ts') || filename.endsWith('.tsx');
|
||||
if (!isTypeScript)
|
||||
return;
|
||||
const tsconfig = loadAndValidateTsconfigForFile(filename);
|
||||
if (!tsconfig)
|
||||
return;
|
||||
for (const { key, values } of tsconfig.paths) {
|
||||
const keyHasStar = key[key.length - 1] === '*';
|
||||
const matches = specifier.startsWith(keyHasStar ? key.substring(0, key.length - 1) : key);
|
||||
if (!matches)
|
||||
continue;
|
||||
for (const value of values) {
|
||||
const valueHasStar = value[value.length - 1] === '*';
|
||||
let candidate = valueHasStar ? value.substring(0, value.length - 1) : value;
|
||||
if (valueHasStar && keyHasStar)
|
||||
candidate += specifier.substring(key.length - 1);
|
||||
candidate = path.resolve(tsconfig.absoluteBaseUrl, candidate.replace(/\//g, path.sep));
|
||||
for (const ext of ['', '.js', '.ts', '.mjs', '.cjs', '.jsx', '.tsx']) {
|
||||
if (fs.existsSync(candidate + ext))
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function transformHook(code: string, filename: string, isModule = false): string {
|
||||
if (isComponentImport(filename))
|
||||
@ -138,8 +134,7 @@ export function transformHook(code: string, filename: string, isModule = false):
|
||||
if (!isTypeScript && !hasPreprocessor)
|
||||
return code;
|
||||
|
||||
const tsconfigData = isTypeScript ? loadAndValidateTsconfigForFile(filename) : undefined;
|
||||
const cachePath = calculateCachePath(tsconfigData, code, filename);
|
||||
const cachePath = calculateCachePath(code, filename);
|
||||
const codePath = cachePath + '.js';
|
||||
const sourceMapPath = cachePath + '.map';
|
||||
sourceMaps.set(filename, sourceMapPath);
|
||||
@ -166,16 +161,6 @@ export function transformHook(code: string, filename: string, isModule = false):
|
||||
[require.resolve('@babel/plugin-proposal-export-namespace-from')]
|
||||
);
|
||||
|
||||
if (tsconfigData) {
|
||||
plugins.push([require.resolve('babel-plugin-module-resolver'), {
|
||||
root: ['./'],
|
||||
alias: tsconfigData.alias,
|
||||
// Silences warning 'Could not resovle ...' that we trigger because we resolve
|
||||
// into 'foo/bar', and not 'foo/bar.ts'.
|
||||
loglevel: 'silent',
|
||||
}]);
|
||||
}
|
||||
|
||||
if (!isModule) {
|
||||
plugins.push([require.resolve('@babel/plugin-transform-modules-commonjs')]);
|
||||
plugins.push([require.resolve('@babel/plugin-proposal-dynamic-import')]);
|
||||
@ -217,12 +202,30 @@ export function transformHook(code: string, filename: string, isModule = false):
|
||||
}
|
||||
|
||||
export function installTransform(): () => void {
|
||||
const exts = ['.ts', '.tsx'];
|
||||
let reverted = false;
|
||||
|
||||
const originalResolveFilename = (Module as any)._resolveFilename;
|
||||
function resolveFilename(this: any, specifier: string, parent: Module, ...rest: any[]) {
|
||||
if (!reverted && parent) {
|
||||
const resolved = resolveHook(parent.filename, specifier);
|
||||
if (resolved !== undefined)
|
||||
specifier = resolved;
|
||||
}
|
||||
return originalResolveFilename.call(this, specifier, parent, ...rest);
|
||||
}
|
||||
(Module as any)._resolveFilename = resolveFilename;
|
||||
|
||||
const exts = ['.ts', '.tsx'];
|
||||
// When script preprocessor is engaged, we transpile JS as well.
|
||||
if (scriptPreprocessor)
|
||||
exts.push('.js', '.mjs');
|
||||
return pirates.addHook((code: string, filename: string) => transformHook(code, filename), { exts });
|
||||
const revertPirates = pirates.addHook((code: string, filename: string) => transformHook(code, filename), { exts });
|
||||
|
||||
return () => {
|
||||
reverted = true;
|
||||
(Module as any)._resolveFilename = originalResolveFilename;
|
||||
revertPirates();
|
||||
};
|
||||
}
|
||||
|
||||
export function wrapFunctionWithLocation<A extends any[], R>(func: (location: Location, ...args: A) => R): (...args: A) => R {
|
||||
|
@ -34,7 +34,7 @@ test('should respect path resolver', async ({ runInlineTest }) => {
|
||||
"paths": {
|
||||
"util/*": ["./foo/bar/util/*"],
|
||||
"util2/*": ["./foo/bar/util/*"],
|
||||
"util3": ["./foo/bar/util/b"],
|
||||
"util3": ["./does-not-exist", "./foo/bar/util/b"],
|
||||
},
|
||||
},
|
||||
}`,
|
||||
@ -136,72 +136,32 @@ test('should respect baseurl', async ({ runInlineTest }) => {
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should ignore for baseurl w/o paths', async ({ runInlineTest }) => {
|
||||
test('should respect baseurl w/o paths', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'tsconfig.json': `{
|
||||
'foo/bar/util/b.ts': `
|
||||
export const foo = 42;
|
||||
`,
|
||||
'dir2/tsconfig.json': `{
|
||||
"compilerOptions": {
|
||||
"target": "ES2019",
|
||||
"module": "commonjs",
|
||||
"lib": ["esnext", "dom", "DOM.Iterable"],
|
||||
"baseUrl": "./"
|
||||
"baseUrl": "..",
|
||||
},
|
||||
}`,
|
||||
'a.test.ts': `
|
||||
import { foo } from 'foo/b';
|
||||
`,
|
||||
'foo/b.ts': `
|
||||
export const foo = 1;
|
||||
'dir2/inner.spec.ts': `
|
||||
// This import should pick up ../foo/bar/util/b due to baseUrl.
|
||||
import { foo } from 'foo/bar/util/b';
|
||||
const { test } = pwt;
|
||||
test('test', ({}, testInfo) => {
|
||||
expect(foo).toBe(42);
|
||||
});
|
||||
`,
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.output).toContain(`Cannot find module 'foo/b'`);
|
||||
});
|
||||
|
||||
test('should ignore tsconfig with ambiguous paths', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'tsconfig.json': `{
|
||||
"compilerOptions": {
|
||||
"target": "ES2019",
|
||||
"module": "commonjs",
|
||||
"lib": ["esnext", "dom", "DOM.Iterable"],
|
||||
"baseUrl": "./",
|
||||
"paths": { "*/*": ["*/*"] }
|
||||
},
|
||||
}`,
|
||||
'a.test.ts': `
|
||||
import { foo } from 'foo/b';
|
||||
`,
|
||||
'foo/b.ts': `
|
||||
export const foo = 1;
|
||||
`,
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.output).toContain(`Cannot find module 'foo/b'`);
|
||||
});
|
||||
|
||||
test('should ignore tsconfig with multi-value paths', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'tsconfig.json': `{
|
||||
"compilerOptions": {
|
||||
"target": "ES2019",
|
||||
"module": "commonjs",
|
||||
"lib": ["esnext", "dom", "DOM.Iterable"],
|
||||
"baseUrl": "./",
|
||||
"paths": { "foo/*": ["./foo/*", "./bar/*"] }
|
||||
},
|
||||
}`,
|
||||
'a.test.ts': `
|
||||
import { foo } from 'foo/b';
|
||||
`,
|
||||
'foo/b.ts': `
|
||||
export const foo = 1;
|
||||
`,
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.output).toContain(`Cannot find module 'foo/b'`);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
expect(result.output).not.toContain(`Could not`);
|
||||
});
|
||||
|
||||
test('should respect path resolver in experimental mode', async ({ runInlineTest }) => {
|
||||
|
Loading…
Reference in New Issue
Block a user