@nx/playwright 18.0.0-canary.20240130-bb3cf3a → 18.0.0-canary.20240201-8762c38
Sign up to get free protection for your applications and to get access to all the features.
- package/migrations.json +8 -1
- package/package.json +5 -4
- package/src/executors/playwright/schema.json +1 -1
- package/src/generators/configuration/schema.json +1 -1
- package/src/generators/init/schema.json +1 -1
- package/src/migrations/update-17-3-1/add-project-to-config.d.ts +2 -0
- package/src/migrations/update-17-3-1/add-project-to-config.js +95 -0
- package/src/utils/load-config-file.js +4 -21
package/migrations.json
CHANGED
@@ -1,3 +1,10 @@
|
|
1
1
|
{
|
2
|
-
"generators": {
|
2
|
+
"generators": {
|
3
|
+
"17-3-1-add-project-to-config": {
|
4
|
+
"cli": "nx",
|
5
|
+
"version": "17.3.1-beta.0",
|
6
|
+
"description": "Add project property to playwright config",
|
7
|
+
"implementation": "./src/migrations/update-17-3-1/add-project-to-config"
|
8
|
+
}
|
9
|
+
}
|
3
10
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@nx/playwright",
|
3
|
-
"version": "18.0.0-canary.
|
3
|
+
"version": "18.0.0-canary.20240201-8762c38",
|
4
4
|
"type": "commonjs",
|
5
5
|
"homepage": "https://nx.dev",
|
6
6
|
"private": false,
|
@@ -32,9 +32,10 @@
|
|
32
32
|
"directory": "packages/playwright"
|
33
33
|
},
|
34
34
|
"dependencies": {
|
35
|
-
"@
|
36
|
-
"@nx/
|
37
|
-
"@nx/
|
35
|
+
"@phenomnomnominal/tsquery": "~5.0.1",
|
36
|
+
"@nx/devkit": "18.0.0-canary.20240201-8762c38",
|
37
|
+
"@nx/eslint": "18.0.0-canary.20240201-8762c38",
|
38
|
+
"@nx/js": "18.0.0-canary.20240201-8762c38",
|
38
39
|
"tslib": "^2.3.0",
|
39
40
|
"minimatch": "9.0.3"
|
40
41
|
},
|
@@ -0,0 +1,95 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const devkit_1 = require("@nx/devkit");
|
4
|
+
const ts = require("typescript");
|
5
|
+
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
6
|
+
async function update(tree) {
|
7
|
+
const projects = (0, devkit_1.getProjects)(tree);
|
8
|
+
projects.forEach((project) => {
|
9
|
+
// Check if the project contains playwright config
|
10
|
+
const configPath = (0, devkit_1.joinPathFragments)(project.root, 'playwright.config.ts');
|
11
|
+
if (tree.exists(configPath)) {
|
12
|
+
addProjectIfExists(tree, (0, devkit_1.joinPathFragments)(configPath));
|
13
|
+
}
|
14
|
+
});
|
15
|
+
}
|
16
|
+
exports.default = update;
|
17
|
+
function addProjectIfExists(tree, configFilePath) {
|
18
|
+
const configFileContent = tree.read(configFilePath, 'utf-8');
|
19
|
+
const sourceFile = tsquery_1.tsquery.ast(configFileContent);
|
20
|
+
const printer = ts.createPrinter();
|
21
|
+
const updatedStatements = updateOrCreateImportStatement(sourceFile, '@playwright/test', ['devices']);
|
22
|
+
const exportAssignment = tsquery_1.tsquery.query(sourceFile, 'ExportAssignment')[0];
|
23
|
+
if (!exportAssignment) {
|
24
|
+
// No export found in the file
|
25
|
+
return;
|
26
|
+
}
|
27
|
+
const exportAssignemntObject = exportAssignment.expression;
|
28
|
+
if (!(ts.isCallExpression(exportAssignemntObject) &&
|
29
|
+
exportAssignemntObject.getText(sourceFile).startsWith('defineConfig') &&
|
30
|
+
exportAssignemntObject.arguments.length > 0)) {
|
31
|
+
// Export is not a call expression with defineConfig ex. export default defineConfig({ ... })
|
32
|
+
return;
|
33
|
+
}
|
34
|
+
let firstArgument = exportAssignemntObject.arguments[0];
|
35
|
+
if (!ts.isObjectLiteralExpression(firstArgument)) {
|
36
|
+
// First argument is not an object literal ex. defineConfig('foo')
|
37
|
+
return;
|
38
|
+
}
|
39
|
+
const projectProperty = tsquery_1.tsquery.query(exportAssignemntObject, 'PropertyAssignment > Identifier[name="projects"]')[0];
|
40
|
+
if (projectProperty) {
|
41
|
+
// Projects property already exists in the config
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
// Add projects property to the config
|
45
|
+
const projectsArray = ts.factory.createArrayLiteralExpression([
|
46
|
+
createProperty('chromium', 'Desktop Chrome'),
|
47
|
+
createProperty('firefox', 'Desktop Firefox'),
|
48
|
+
createProperty('webkit', 'Desktop Safari'),
|
49
|
+
], true);
|
50
|
+
const newProjectsProperty = ts.factory.createPropertyAssignment('projects', projectsArray);
|
51
|
+
const newObj = ts.factory.createObjectLiteralExpression([
|
52
|
+
...firstArgument.properties,
|
53
|
+
newProjectsProperty,
|
54
|
+
]);
|
55
|
+
const newCallExpression = ts.factory.updateCallExpression(exportAssignemntObject, exportAssignemntObject.expression, exportAssignemntObject.typeArguments, [newObj]);
|
56
|
+
const newExportAssignment = ts.factory.updateExportAssignment(exportAssignment, exportAssignment.modifiers, newCallExpression);
|
57
|
+
const transformedStatements = updatedStatements.map((statement) => {
|
58
|
+
return statement === exportAssignment ? newExportAssignment : statement;
|
59
|
+
});
|
60
|
+
const transformedSourceFile = ts.factory.updateSourceFile(sourceFile, transformedStatements);
|
61
|
+
const updatedConfigFileContent = printer.printFile(transformedSourceFile);
|
62
|
+
tree.write(configFilePath, updatedConfigFileContent);
|
63
|
+
}
|
64
|
+
function createProperty(name, device) {
|
65
|
+
return ts.factory.createObjectLiteralExpression([
|
66
|
+
ts.factory.createPropertyAssignment('name', ts.factory.createStringLiteral(name)),
|
67
|
+
ts.factory.createPropertyAssignment('use', ts.factory.createObjectLiteralExpression([
|
68
|
+
ts.factory.createSpreadAssignment(ts.factory.createElementAccessExpression(ts.factory.createIdentifier('devices'), ts.factory.createStringLiteral(device))),
|
69
|
+
])),
|
70
|
+
]);
|
71
|
+
}
|
72
|
+
function updateOrCreateImportStatement(sourceFile, moduleName, importNames) {
|
73
|
+
let importDeclarationFound = false;
|
74
|
+
const newStatements = sourceFile.statements.map((statement) => {
|
75
|
+
if (ts.isImportDeclaration(statement) &&
|
76
|
+
statement.moduleSpecifier.getText(sourceFile) === `'${moduleName}'`) {
|
77
|
+
importDeclarationFound = true;
|
78
|
+
const existingSpecifiers = statement.importClause?.namedBindings &&
|
79
|
+
ts.isNamedImports(statement.importClause.namedBindings)
|
80
|
+
? statement.importClause.namedBindings.elements.map((e) => e.name.text)
|
81
|
+
: [];
|
82
|
+
// Merge with new import names, avoiding duplicates
|
83
|
+
const mergedImportNames = Array.from(new Set([...existingSpecifiers, ...importNames]));
|
84
|
+
// Create new import specifiers
|
85
|
+
const importSpecifiers = mergedImportNames.map((name) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name)));
|
86
|
+
return ts.factory.updateImportDeclaration(statement, statement.modifiers, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports(importSpecifiers)), statement.moduleSpecifier, undefined);
|
87
|
+
}
|
88
|
+
return statement;
|
89
|
+
});
|
90
|
+
if (!importDeclarationFound) {
|
91
|
+
const importDeclaration = ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports(importNames.map((name) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name))))), ts.factory.createStringLiteral(moduleName));
|
92
|
+
newStatements.push(importDeclaration);
|
93
|
+
}
|
94
|
+
return newStatements;
|
95
|
+
}
|
@@ -8,44 +8,27 @@ exports.dynamicImport = new Function('modulePath', 'return import(modulePath);')
|
|
8
8
|
async function loadPlaywrightConfig(configFilePath) {
|
9
9
|
{
|
10
10
|
let module;
|
11
|
+
const configPathWithTimestamp = `${configFilePath}?t=${Date.now()}`;
|
11
12
|
if ((0, path_1.extname)(configFilePath) === '.ts') {
|
12
13
|
const tsConfigPath = (0, js_1.getRootTsConfigPath)();
|
13
14
|
if (tsConfigPath) {
|
14
15
|
const unregisterTsProject = (0, internal_1.registerTsProject)(tsConfigPath);
|
15
16
|
try {
|
16
|
-
|
17
|
-
// this function is ran during daemon operation. If the config file
|
18
|
-
// is updated, we need to read its new contents, so we need to clear the cache.
|
19
|
-
// We can't just delete the cache entry for the config file, because
|
20
|
-
// it might have imports that need to be updated as well.
|
21
|
-
clearRequireCache();
|
22
|
-
// ts-node doesn't support dynamic import, so we need to use require
|
23
|
-
module = require(configFilePath);
|
17
|
+
module = await (0, exports.dynamicImport)(configPathWithTimestamp);
|
24
18
|
}
|
25
19
|
finally {
|
26
20
|
unregisterTsProject();
|
27
21
|
}
|
28
22
|
}
|
29
23
|
else {
|
30
|
-
module = await (0, exports.dynamicImport)(
|
24
|
+
module = await (0, exports.dynamicImport)(configPathWithTimestamp);
|
31
25
|
}
|
32
26
|
}
|
33
27
|
else {
|
34
|
-
module = await (0, exports.dynamicImport)(
|
28
|
+
module = await (0, exports.dynamicImport)(configPathWithTimestamp);
|
35
29
|
}
|
36
30
|
return module.default ?? module;
|
37
31
|
}
|
38
32
|
}
|
39
33
|
exports.loadPlaywrightConfig = loadPlaywrightConfig;
|
40
34
|
const packageInstallationDirectories = ['node_modules', '.yarn'];
|
41
|
-
function clearRequireCache() {
|
42
|
-
Object.keys(require.cache).forEach((key) => {
|
43
|
-
// We don't want to clear the require cache of installed packages.
|
44
|
-
// Clearing them can cause some issues when running Nx without the daemon
|
45
|
-
// and may cause issues for other packages that use the module state
|
46
|
-
// in some to store cached information.
|
47
|
-
if (!packageInstallationDirectories.some((dir) => key.includes(dir))) {
|
48
|
-
delete require.cache[key];
|
49
|
-
}
|
50
|
-
});
|
51
|
-
}
|