@nx/playwright 18.0.0-canary.20240130-bb3cf3a → 18.0.0-canary.20240201-8762c38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.20240130-bb3cf3a",
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
- "@nx/devkit": "18.0.0-canary.20240130-bb3cf3a",
36
- "@nx/eslint": "18.0.0-canary.20240130-bb3cf3a",
37
- "@nx/js": "18.0.0-canary.20240130-bb3cf3a",
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
  },
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "http://json-schema.org/schema",
2
+ "$schema": "https://json-schema.org/schema",
3
3
  "version": 2,
4
4
  "title": "Playwright executor",
5
5
  "description": "Run Playwright tests.",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "http://json-schema.org/schema",
2
+ "$schema": "https://json-schema.org/schema",
3
3
  "$id": "NxPlaywrightConfiguration",
4
4
  "description": "Add a Playwright configuration.",
5
5
  "title": "Add a Playwright configuration",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "http://json-schema.org/schema",
2
+ "$schema": "https://json-schema.org/schema",
3
3
  "$id": "NxPlaywrightInit",
4
4
  "title": "Playwright Init Generator",
5
5
  "description": "Initializes a Playwright project in the current workspace.",
@@ -0,0 +1,2 @@
1
+ import { Tree } from '@nx/devkit';
2
+ export default function update(tree: Tree): Promise<void>;
@@ -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
- // Require's cache doesn't notice when the file is updated, and
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)(configFilePath);
24
+ module = await (0, exports.dynamicImport)(configPathWithTimestamp);
31
25
  }
32
26
  }
33
27
  else {
34
- module = await (0, exports.dynamicImport)(configFilePath);
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
- }