nuxt-spec 0.3.4 → 0.4.0-alpha.2

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.
Files changed (49) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +13 -13
  3. package/app/app.vue +10 -10
  4. package/app/components/NuxtSpecApiTestComponent.vue +24 -24
  5. package/app/components/NuxtSpecTestComponent.vue +9 -9
  6. package/app/components/index.d.ts +7 -7
  7. package/app/components/index.ts +2 -2
  8. package/app/utils/vitest-utils.ts +5 -5
  9. package/bin/_internal/prepublish-check.js +16 -16
  10. package/bin/cli.js +53 -44
  11. package/bin/commands/ci.js +124 -0
  12. package/bin/commands/setup.js +367 -0
  13. package/bin/commands/update.js +126 -0
  14. package/bin/helpers/commands.js +82 -82
  15. package/bin/helpers/constants.js +8 -0
  16. package/bin/helpers/git.js +73 -0
  17. package/config/index.d.ts +36 -33
  18. package/config/index.mjs +147 -132
  19. package/config/report/report-entry.html +6 -0
  20. package/config/report/report-head.html +34 -0
  21. package/{utils/screenshot/report-entry.html → config/report/report-screenshot.html} +8 -8
  22. package/config/report/report-tail.html +4 -0
  23. package/config/templates/.github/workflows/ci-test-update.yml.template +105 -0
  24. package/config/templates/.github/workflows/ci-test-verify.yml.template +68 -0
  25. package/config/templates/.gitlab/ci/ci-test-update.yml.template +69 -0
  26. package/config/templates/.gitlab/ci/ci-test-verify.yml.template +41 -0
  27. package/config/templates/.gitlab-ci.yml.template +6 -0
  28. package/config/templates/pnpm-workspace.yaml.template +19 -19
  29. package/config/templates/vitest.config.ts.template +5 -5
  30. package/config/utils/merge.mjs +43 -43
  31. package/config/utils/options.d.mts +59 -0
  32. package/config/utils/options.mjs +116 -0
  33. package/config/utils/reporter.mjs +409 -0
  34. package/config/utils/warnings.mjs +38 -33
  35. package/modules/spec-options.ts +23 -0
  36. package/nuxt.config.ts +20 -24
  37. package/package.json +37 -31
  38. package/utils/e2e.ts +131 -131
  39. package/utils/helpers/check-params.ts +44 -41
  40. package/utils/helpers/location-hint.ts +30 -30
  41. package/utils/helpers/screenshot-utils.ts +27 -0
  42. package/utils/index.ts +12 -12
  43. package/utils/playwright.ts +28 -20
  44. package/utils/screenshot.ts +201 -172
  45. package/bin/setup.js +0 -287
  46. package/bin/update.js +0 -94
  47. package/utils/screenshot/report-head.html +0 -24
  48. package/utils/screenshot/report-tail.html +0 -4
  49. package/utils/screenshot/report-utils.ts +0 -236
@@ -0,0 +1,367 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from 'node:child_process'
4
+ import { readFileSync } from 'node:fs'
5
+ import {
6
+ createFileFromWebTemplate, deletePath,
7
+ pathExists, promptUser, removeFromJsonFile, showMessage,
8
+ updateConfigFile, updateJsonFile, updateTextFile,
9
+ } from 'elrh-cosca'
10
+ import {
11
+ getPlaywrightInstallCmd, getPrepareCmd,
12
+ } from '../helpers/commands.js'
13
+ import { COSCA_VERSION, COSCA_URL } from '../helpers/constants.js'
14
+
15
+ /**
16
+ * CLI tool to scaffold necessary adjustments in project folder.
17
+ *
18
+ * It first asks whether to run in "auto" mode (no prompts, force = true) or "manual" mode (with prompts, force = false).
19
+ *
20
+ * Then it:
21
+ * 1) adds `nuxt-spec` into `package.json` dependencies and removes `nuxt`, `vue` and `vue-router` if present
22
+ * 2) adds `extends: ['nuxt-spec']` to `nuxt.config.ts`
23
+ * 3) creates/updates `pnpm-workspace.yaml` file (only if pnpm is used)
24
+ * 4) creates default `vitest.config.ts` file
25
+ * 5) creates default `.nuxtrc` file
26
+ * 6) adds test-related scripts in `package.json`
27
+ * 7) creates sample test files
28
+ * 8) adds nuxt-spec related entries to `.gitignore`
29
+ * 9) clear node_modules and lock file(s)
30
+ * 10) run install command
31
+ * 11) run Nuxt prepare command to generate types and auto-imports
32
+ * 12) run Playwright setup command
33
+ *
34
+ * @param {boolean} autoRun - Whether to run the setup automatically without any prompts (defaults to false).
35
+ * @param {string} [packageManager] - Package manager to be used (`npm`, `pnpm`, `yarn`, `bun` or `deno`).
36
+ */
37
+ export async function specSetup(autoRun = false, packageManager) {
38
+ showMessage({ message: 'NUXT SPEC SETUP' })
39
+ showMessage({ message: 'This CLI tool will help you include Nuxt Spec in your project.' })
40
+ showMessage({ message: 'Refer to the documentation for more information.', linesAfter: 2 })
41
+
42
+ const isAutoRun = autoRun || await promptUser({ question: '[Nuxt Spec] Do you want to set everything up automatically (no more prompts)?' })
43
+ showMessage({ message: '' })
44
+
45
+ // 1) manage dependencies in package.json
46
+
47
+ // add nuxt-spec
48
+ try {
49
+ await updateJsonFile({
50
+ targetFile: 'package.json',
51
+ jsonKey: 'dependencies',
52
+ patch: {
53
+ ['nuxt-spec']: COSCA_VERSION,
54
+ },
55
+ force: isAutoRun,
56
+ prompt: `[Nuxt Spec] This will add 'nuxt-spec' dependency to your 'package.json'. Continue?`,
57
+ })
58
+ } catch (error) {
59
+ console.error(`[Nuxt Spec] Error adding 'nuxt-spec' dependency:\n`, error.message)
60
+ }
61
+
62
+ // remove now obsolete nuxt, vue and vue-router
63
+ const removeDeps = isAutoRun || await promptUser({ question: `[Nuxt Spec] As 'nuxt-spec' provides 'nuxt', 'vue' and 'vue-router' dependencies out of the box, do you want to remove them from your 'package.json' to avoid duplications and possible version clashes?` })
64
+ if (removeDeps) {
65
+ try {
66
+ await removeFromJsonFile({ targetFile: 'package.json', jsonKey: 'dependencies.nuxt', force: true })
67
+ } catch (error) {
68
+ console.error('[Nuxt Spec] Error removing \'nuxt\' dependency:\n', error.message)
69
+ }
70
+ try {
71
+ await removeFromJsonFile({ targetFile: 'package.json', jsonKey: 'dependencies.vue', force: true })
72
+ } catch (error) {
73
+ console.error('[Nuxt Spec] Error removing \'vue\' dependency:\n', error.message)
74
+ }
75
+ try {
76
+ await removeFromJsonFile({ targetFile: 'package.json', jsonKey: 'dependencies.vue-router', force: true })
77
+ } catch (error) {
78
+ console.error('[Nuxt Spec] Error removing \'vue-router\' dependency:\n', error.message)
79
+ }
80
+ try {
81
+ await removeFromJsonFile({ targetFile: 'package.json', jsonKey: 'devDependencies.nuxt', force: true })
82
+ } catch (error) {
83
+ console.error('[Nuxt Spec] Error removing \'nuxt\' devDependency:\n', error.message)
84
+ }
85
+ try {
86
+ await removeFromJsonFile({ targetFile: 'package.json', jsonKey: 'devDependencies.vue', force: true })
87
+ } catch (error) {
88
+ console.error('[Nuxt Spec] Error removing \'vue\' devDependency:\n', error.message)
89
+ }
90
+ try {
91
+ await removeFromJsonFile({ targetFile: 'package.json', jsonKey: 'devDependencies.vue-router', force: true })
92
+ } catch (error) {
93
+ console.error('[Nuxt Spec] Error removing \'vue-router\' devDependency:\n', error.message)
94
+ }
95
+ }
96
+
97
+ // 2) modify nuxt.config.ts
98
+ try {
99
+ await updateConfigFile({
100
+ targetFile: 'nuxt.config.ts',
101
+ newConfig: {
102
+ extends: [
103
+ 'nuxt-spec',
104
+ ],
105
+ },
106
+ force: isAutoRun,
107
+ prompt: `[Nuxt Spec] This will add 'nuxt-spec' module to your 'nuxt.config.ts'. Continue?`,
108
+ })
109
+ } catch (error) {
110
+ console.error('[Nuxt Spec] Error updating \'nuxt.config.ts\':\n', error.message)
111
+ }
112
+
113
+ // 3) `pnpm-workspace.yaml` file (only if pnpm is used)
114
+ if (packageManager === 'pnpm') {
115
+ try {
116
+ if (pathExists({ targetPath: 'pnpm-workspace.yaml' })) {
117
+ await updateTextFile({
118
+ targetFile: 'pnpm-workspace.yaml',
119
+ rowsToAdd: ['shamefullyHoist: true'],
120
+ force: isAutoRun,
121
+ prompt: '[Nuxt Spec] This will adjust \'pnpm-workspace.yaml\' file in your project. Continue?',
122
+ })
123
+ } else {
124
+ await createFileFromWebTemplate({
125
+ url: `${COSCA_URL}/config/templates/pnpm-workspace.yaml.template`,
126
+ targetFile: 'pnpm-workspace.yaml',
127
+ force: isAutoRun,
128
+ prompt: '[Nuxt Spec] This will add \'pnpm-workspace.yaml\' file for your project. Continue?',
129
+ })
130
+ }
131
+ } catch (error) {
132
+ console.error('[Nuxt Spec] Error setting up \'pnpm-workspace.yaml\':\n', error.message)
133
+ }
134
+ }
135
+
136
+ // 4) create vitest.config.ts
137
+ try {
138
+ await createFileFromWebTemplate({
139
+ url: `${COSCA_URL}/config/templates/vitest.config.ts.template`,
140
+ targetFile: 'vitest.config.ts',
141
+ force: isAutoRun,
142
+ prompt: '[Nuxt Spec] This will create a new \'vitest.config.ts\' file for your project. Continue?',
143
+ })
144
+ } catch (error) {
145
+ console.error('[Nuxt Spec] Error setting up \'vitest.config.ts\':\n', error.message)
146
+ }
147
+
148
+ // 5) create .nuxtrc to prevent @nuxt/test-utils setup from running automatically on first start
149
+ if (!pathExists({ targetPath: '.nuxtrc' })) {
150
+ try {
151
+ await createFileFromWebTemplate({
152
+ url: `${COSCA_URL}/.nuxtrc`,
153
+ targetFile: '.nuxtrc',
154
+ force: isAutoRun,
155
+ prompt: '[Nuxt Spec] This will create a \'.nuxtrc\' file to prevent @nuxt/test-utils setup from running automatically when dev server starts. Continue?',
156
+ })
157
+ } catch (error) {
158
+ console.error('[Nuxt Spec] Error creating \'.nuxtrc\':\n', error.message)
159
+ }
160
+ }
161
+
162
+ // 6) modify package.json with test scripts
163
+ try {
164
+ await updateJsonFile({
165
+ targetFile: 'package.json',
166
+ jsonKey: 'scripts',
167
+ patch: {
168
+ 'test': 'vitest run',
169
+ 'test-u': 'vitest run -u',
170
+ 'test-c': 'vitest run --coverage',
171
+ 'test-i': 'vitest',
172
+ },
173
+ force: isAutoRun,
174
+ prompt: '[Nuxt Spec] This will adjust the test-related commands in your \'package.json\'. Continue?',
175
+ })
176
+ } catch (error) {
177
+ console.error('[Nuxt Spec] Error adjusting scripts in \'package.json\':\n', error.message)
178
+ }
179
+
180
+ // 7) create sample test files
181
+ const createSampleTests = isAutoRun || await promptUser({ question: '[Nuxt Spec] Do you want to create sample tests in \'/test\' folder?' })
182
+ if (createSampleTests) {
183
+ try {
184
+ await createFileFromWebTemplate({
185
+ url: `${COSCA_URL}/test/browser/vitest-browser.test.ts`,
186
+ targetFile: 'test/browser/vitest-browser.test.ts',
187
+ force: true,
188
+ })
189
+ } catch (error) {
190
+ console.error('[Nuxt Spec] Error setting up \'vitest-browser.test.ts\':\n', error.message)
191
+ }
192
+ try {
193
+ await createFileFromWebTemplate({
194
+ url: `${COSCA_URL}/test/e2e/nuxt-e2e.test.ts`,
195
+ targetFile: 'test/e2e/nuxt-e2e.test.ts',
196
+ force: true,
197
+ })
198
+ } catch (error) {
199
+ console.error('[Nuxt Spec] Error setting up \'nuxt-e2e.test.ts\':\n', error.message)
200
+ }
201
+ try {
202
+ await createFileFromWebTemplate({
203
+ url: `${COSCA_URL}/test/e2e/nuxt-visual.test.ts`,
204
+ targetFile: 'test/e2e/nuxt-visual.test.ts',
205
+ force: true,
206
+ })
207
+ } catch (error) {
208
+ console.error('[Nuxt Spec] Error setting up \'nuxt-visual.test.ts\':\n', error.message)
209
+ }
210
+ // pre-existing baseline ensures the sample "wrong" visual test fails as intended
211
+ try {
212
+ await createFileFromWebTemplate({
213
+ url: `${COSCA_URL}/test/e2e/__baseline__/wrong.png`,
214
+ targetFile: 'test/e2e/__baseline__/wrong.png',
215
+ force: true,
216
+ })
217
+ } catch (error) {
218
+ console.error('[Nuxt Spec] Error setting up \'wrong.png\':\n', error.message)
219
+ }
220
+ try {
221
+ await createFileFromWebTemplate({
222
+ url: `${COSCA_URL}/test/nuxt/nuxt-unit.test.ts`,
223
+ targetFile: 'test/nuxt/nuxt-unit.test.ts',
224
+ force: true,
225
+ })
226
+ } catch (error) {
227
+ console.error('[Nuxt Spec] Error setting up \'nuxt-unit.test.ts\':\n', error.message)
228
+ }
229
+ try {
230
+ await createFileFromWebTemplate({
231
+ url: `${COSCA_URL}/test/unit/vitest-unit.test.ts`,
232
+ targetFile: 'test/unit/vitest-unit.test.ts',
233
+ force: true,
234
+ })
235
+ } catch (error) {
236
+ console.error('[Nuxt Spec] Error setting up \'vitest-unit.test.ts\':\n', error.message)
237
+ }
238
+ }
239
+
240
+ // 8) add nuxt-spec related entries to .gitignore
241
+ try {
242
+ const gitignoreEntries = [
243
+ ['# vitest output folder', '.vitest'],
244
+ ['# vitest coverage output folder', 'coverage'],
245
+ ['# nuxt-spec screenshots folder', '__current__'],
246
+ ['# nuxt-spec HTML test reports folder', '__reports__'],
247
+ ]
248
+ // if .gitignore exists, we rather check for duplicates
249
+ const gitignoreExists = pathExists({ targetPath: '.gitignore' })
250
+ const gitignoreRows = gitignoreExists ? readFileSync('.gitignore', 'utf8').split(/\r?\n/) : []
251
+ const rowsToAdd = gitignoreEntries
252
+ .filter(([, entry]) => !gitignoreRows.includes(entry))
253
+ .flatMap(([comment, entry]) => ['', comment, entry])
254
+ .slice(gitignoreExists ? 0 : 1)
255
+ //
256
+ if (rowsToAdd.length > 0) {
257
+ await updateTextFile({
258
+ targetFile: '.gitignore',
259
+ rowsToAdd,
260
+ allowDuplicates: true,
261
+ createMissing: true,
262
+ force: isAutoRun,
263
+ prompt: '[Nuxt Spec] This will add nuxt-spec related entries to your \'.gitignore\' file. Continue?',
264
+ })
265
+ }
266
+ } catch (error) {
267
+ console.error('[Nuxt Spec] Error updating \'.gitignore\':\n', error.message)
268
+ }
269
+
270
+ // 9) clear node_modules and lock file(s)
271
+ const prepareForReinstall = isAutoRun || await promptUser({ question: '[Nuxt Spec] Dependencies should be re-installed now. Do you want to remove node_modules and the lock file?' })
272
+ if (prepareForReinstall) {
273
+ if (pathExists({ targetPath: 'node_modules' })) {
274
+ try {
275
+ await deletePath({ targetPath: 'node_modules', force: true })
276
+ } catch (error) {
277
+ console.error('[Nuxt Spec] Error deleting \'node_modules\':\n', error.message)
278
+ }
279
+ }
280
+ if (pathExists({ targetPath: 'package-lock.json' })) {
281
+ try {
282
+ await deletePath({ targetPath: 'package-lock.json', force: true })
283
+ } catch (error) {
284
+ console.error('[Nuxt Spec] Error deleting \'package-lock.json\':\n', error.message)
285
+ }
286
+ }
287
+ if (pathExists({ targetPath: 'pnpm-lock.yaml' })) {
288
+ try {
289
+ await deletePath({ targetPath: 'pnpm-lock.yaml', force: true })
290
+ } catch (error) {
291
+ console.error('[Nuxt Spec] Error deleting \'pnpm-lock.yaml\':\n', error.message)
292
+ }
293
+ }
294
+ if (pathExists({ targetPath: 'yarn.lock' })) {
295
+ try {
296
+ await deletePath({ targetPath: 'yarn.lock', force: true })
297
+ } catch (error) {
298
+ console.error('[Nuxt Spec] Error deleting \'yarn.lock\':\n', error.message)
299
+ }
300
+ }
301
+ if (pathExists({ targetPath: 'bun.lockb' })) {
302
+ try {
303
+ await deletePath({ targetPath: 'bun.lockb', force: true })
304
+ } catch (error) {
305
+ console.error('[Nuxt Spec] Error deleting \'bun.lockb\':\n', error.message)
306
+ }
307
+ }
308
+ if (pathExists({ targetPath: 'deno.lock' })) {
309
+ try {
310
+ await deletePath({ targetPath: 'deno.lock', force: true })
311
+ } catch (error) {
312
+ console.error('[Nuxt Spec] Error deleting \'deno.lock\':\n', error.message)
313
+ }
314
+ }
315
+ }
316
+
317
+ // 10) run install command
318
+ const runInstall = isAutoRun || await promptUser({ question: `[Nuxt Spec] Fresh \`${packageManager} install\` is required. Do you want to run it now?` })
319
+ if (runInstall) {
320
+ try {
321
+ showMessage({ message: `Running \`${packageManager} install\`...` })
322
+ execSync(`${packageManager} install`, { stdio: 'inherit' })
323
+ } catch (error) {
324
+ console.error(`[Nuxt Spec] Error running \`${packageManager} install\`:\n`, error.message)
325
+ }
326
+ }
327
+
328
+ // 11) run Nuxt prepare command to generate types and auto-imports
329
+ const prepareCmd = getPrepareCmd(packageManager)
330
+ const runPrepare = isAutoRun || await promptUser({ question: `[Nuxt Spec] Nuxt needs to generate types and auto-imports before the project is fully usable. Do you want to run \`${prepareCmd}\` now?` })
331
+ if (runPrepare) {
332
+ try {
333
+ showMessage({ message: `Running \`${prepareCmd}\`...` })
334
+ execSync(prepareCmd, { stdio: 'inherit' })
335
+ } catch (error) {
336
+ console.error(`[Nuxt Spec] Error running \`${prepareCmd}\`:\n`, error.message)
337
+ }
338
+ }
339
+
340
+ // 12) run Playwright browser install command
341
+ const playwrightInstallCmd = getPlaywrightInstallCmd(packageManager)
342
+ const runPlaywrightInstall = isAutoRun || await promptUser({ question: `[Nuxt Spec] Playwright browser runtimes might need to be installed locally for e2e tests. Do you want to run \`${playwrightInstallCmd}\` now?` })
343
+ if (runPlaywrightInstall) {
344
+ try {
345
+ showMessage({ message: `Running \`${playwrightInstallCmd}\`...` })
346
+ execSync(playwrightInstallCmd, { stdio: 'inherit' })
347
+ } catch (error) {
348
+ console.error(`[Nuxt Spec] Error running \`${playwrightInstallCmd}\`:\n`, error.message)
349
+ }
350
+ }
351
+
352
+ // 13) inform user
353
+ showMessage({ message: '' })
354
+ showMessage({ message: 'NUXT SPEC SETUP COMPLETE', linesAfter: 2 })
355
+ if (!runInstall) {
356
+ showMessage({ message: `Proceed with \`${packageManager} install\` to install dependencies.` })
357
+ }
358
+ if (!runPrepare) {
359
+ showMessage({ message: `Run \`${prepareCmd}\` to generate the required Nuxt types and auto-imports.` })
360
+ }
361
+ if (!runPlaywrightInstall) {
362
+ showMessage({ message: `Run \`${playwrightInstallCmd}\` to setup the Playwright browser runtimes for e2e tests.` })
363
+ }
364
+
365
+ // force exit to prevent #20
366
+ process.exit(0)
367
+ }
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from 'node:child_process'
4
+ import { readFileSync, writeFileSync } from 'node:fs'
5
+ import {
6
+ hasJsonKey, pathExists, promptUser, showMessage,
7
+ } from 'elrh-cosca'
8
+ import { CI_TEMPLATES } from './ci.js'
9
+ import {
10
+ getPlaywrightInstallCmd, getPrepareCmd, getUpdateCmd,
11
+ } from '../helpers/commands.js'
12
+ import { COSCA_VERSION, PLAYWRIGHT_VERSION } from '../helpers/constants.js'
13
+
14
+ // for automated version patching in CI workflow files
15
+ const PLAYWRIGHT_IMAGE_REGEX = /(mcr\.microsoft\.com\/playwright:v)\d+\.\d+\.\d+/g
16
+
17
+ /**
18
+ * CLI tool to update existing `nuxt-spec` installation.
19
+ *
20
+ * It first asks whether to run in "auto" mode (no prompts, force = true) or "manual" mode (with prompts, force = false).
21
+ *
22
+ * Then it:
23
+ * 1) verifies `nuxt-spec` is present in `package.json` (fails with a hint to run `setup` otherwise)
24
+ * 2) runs the package manager's update command to bump `nuxt-spec` to the latest version
25
+ * 3) run the `nuxt prepare` command to regenerate types and auto-imports
26
+ * 4) run the `playwright install` command to ensure the Playwright browser runtimes are up to date
27
+ * 5) bump the Playwright container image version in CI workflow files created by the `ci` command
28
+ *
29
+ * @param {boolean} autoRun - Whether to run the update automatically without any prompts (defaults to false).
30
+ * @param {string} [packageManager] - Package manager to be used (`npm`, `pnpm`, `yarn`, `bun` or `deno`).
31
+ */
32
+ export async function specUpdate(autoRun = false, packageManager) {
33
+ showMessage({ message: 'NUXT SPEC UPDATE' })
34
+ showMessage({ message: 'This CLI tool will help you update Nuxt Spec in your project.' })
35
+ showMessage({ message: 'Refer to the documentation for more information.', linesAfter: 2 })
36
+
37
+ // fail fast if there is nothing to update
38
+ const isInstalled = hasJsonKey({ targetFile: 'package.json', jsonKey: `dependencies.nuxt-spec` })
39
+ || hasJsonKey({ targetFile: 'package.json', jsonKey: `devDependencies.nuxt-spec` })
40
+ if (!isInstalled) {
41
+ console.error(`[Nuxt Spec] 'nuxt-spec' was not found in your 'package.json'. Run the 'setup' command first.`)
42
+ process.exit(1)
43
+ }
44
+
45
+ const isAutoRun = autoRun || await promptUser({ question: '[Nuxt Spec] Do you want to update everything automatically (no more prompts)?' })
46
+ showMessage({ message: '' })
47
+
48
+ // 1) run 'update nuxt-spec'
49
+ const updateCmd = getUpdateCmd(packageManager, `nuxt-spec@${COSCA_VERSION}`)
50
+ const runUpdate = isAutoRun || await promptUser({ question: `[Nuxt Spec] This will bump 'nuxt-spec' to version '${COSCA_VERSION}' by running \`${updateCmd}\`. Continue?` })
51
+ if (runUpdate) {
52
+ try {
53
+ showMessage({ message: `Running \`${updateCmd}\`...` })
54
+ execSync(updateCmd, { stdio: 'inherit' })
55
+ } catch (error) {
56
+ console.error(`[Nuxt Spec] Error running \`${updateCmd}\`:\n`, error.message)
57
+ }
58
+ }
59
+
60
+ // 2) run 'nuxt prepare'
61
+ const prepareCmd = getPrepareCmd(packageManager)
62
+ const runPrepare = isAutoRun || await promptUser({ question: `[Nuxt Spec] Nuxt types and auto-imports should be regenerated after the update. Do you want to run \`${prepareCmd}\` now?` })
63
+ if (runPrepare) {
64
+ try {
65
+ showMessage({ message: `Running \`${prepareCmd}\`...` })
66
+ execSync(prepareCmd, { stdio: 'inherit' })
67
+ } catch (error) {
68
+ console.error(`[Nuxt Spec] Error running \`${prepareCmd}\`:\n`, error.message)
69
+ }
70
+ }
71
+
72
+ // 3) run 'playwright install'
73
+ const playwrightUpdateCmd = getPlaywrightInstallCmd(packageManager)
74
+ const runPlaywrightUpdate = isAutoRun || await promptUser({ question: `[Nuxt Spec] Playwright browser runtimes might need to be updated for e2e tests. Do you want to run \`${playwrightUpdateCmd}\` now?` })
75
+ if (runPlaywrightUpdate) {
76
+ try {
77
+ showMessage({ message: `Running \`${playwrightUpdateCmd}\`...` })
78
+ execSync(playwrightUpdateCmd, { stdio: 'inherit' })
79
+ } catch (error) {
80
+ console.error(`[Nuxt Spec] Error running \`${playwrightUpdateCmd}\`:\n`, error.message)
81
+ }
82
+ }
83
+
84
+ // 4) bump Playwright container image in CI workflow files
85
+ const outdatedCiFiles = []
86
+ const ciFiles = Object.values(CI_TEMPLATES).flat()
87
+ .map(({ targetFile }) => targetFile)
88
+ .filter(targetFile => pathExists({ targetPath: targetFile }))
89
+ for (const targetFile of ciFiles) {
90
+ try {
91
+ const content = readFileSync(targetFile, 'utf8')
92
+ const bumped = content.replace(PLAYWRIGHT_IMAGE_REGEX, `$1${PLAYWRIGHT_VERSION}`)
93
+ if (bumped === content) {
94
+ continue
95
+ }
96
+ const runBump = isAutoRun || await promptUser({ question: `[Nuxt Spec] This will bump the Playwright container image in '${targetFile}' to version '${PLAYWRIGHT_VERSION}'. Continue?` })
97
+ if (runBump) {
98
+ writeFileSync(targetFile, bumped)
99
+ showMessage({ message: `Playwright container image in '${targetFile}' bumped to version '${PLAYWRIGHT_VERSION}'.` })
100
+ } else {
101
+ outdatedCiFiles.push(targetFile)
102
+ }
103
+ } catch (error) {
104
+ console.error(`[Nuxt Spec] Error bumping Playwright container image in '${targetFile}':\n`, error.message)
105
+ }
106
+ }
107
+
108
+ // 5) inform user
109
+ showMessage({ message: '' })
110
+ showMessage({ message: 'NUXT SPEC UPDATE COMPLETE', linesAfter: 2 })
111
+ if (!runUpdate) {
112
+ showMessage({ message: `Run \`${updateCmd}\` to update 'nuxt-spec'.` })
113
+ }
114
+ if (!runPrepare) {
115
+ showMessage({ message: `Run \`${prepareCmd}\` to regenerate the Nuxt types and auto-imports.` })
116
+ }
117
+ if (!runPlaywrightUpdate) {
118
+ showMessage({ message: `Run \`${playwrightUpdateCmd}\` to update the Playwright browser runtimes for e2e tests.` })
119
+ }
120
+ for (const targetFile of outdatedCiFiles) {
121
+ showMessage({ message: `Bump the Playwright container image in '${targetFile}' to version '${PLAYWRIGHT_VERSION}' to match the \`playwright\` dependency.` })
122
+ }
123
+
124
+ // force exit to prevent #20
125
+ process.exit(0)
126
+ }
@@ -1,82 +1,82 @@
1
- import { getPackageManager } from 'elrh-cosca'
2
-
3
- export const SUPPORTED_PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun', 'deno']
4
-
5
- // get the package manager name for usage in commands
6
- // explicit override takes precedence over auto-detection
7
- export function resolvePackageManager(override) {
8
- if (override) {
9
- if (!SUPPORTED_PACKAGE_MANAGERS.includes(override)) {
10
- throw new Error(`Unsupported package manager '${override}'. Use one of: ${SUPPORTED_PACKAGE_MANAGERS.join(', ')}`)
11
- }
12
- return override
13
- }
14
- return getPackageManager()
15
- }
16
-
17
- // use nuxt-spec CLI tool
18
- export function getCmd(packageManager) {
19
- const command = 'nuxt-spec'
20
- switch (packageManager) {
21
- case 'pnpm':
22
- return `pnpx ${command}`
23
- case 'yarn':
24
- return `yarn dlx ${command}`
25
- case 'bun':
26
- return `bunx ${command}`
27
- case 'deno':
28
- return `deno run -A npm:${command}`
29
- default:
30
- return `npx ${command}`
31
- }
32
- }
33
-
34
- // update nuxt-spec
35
- export function getUpdateCmd(packageManager, target) {
36
- switch (packageManager) {
37
- case 'pnpm':
38
- return `pnpm update ${target}`
39
- case 'yarn':
40
- return `yarn up ${target}`
41
- case 'bun':
42
- return `bun update ${target}`
43
- case 'deno':
44
- return `deno add npm:${target}`
45
- default:
46
- return `npm install ${target}`
47
- }
48
- }
49
-
50
- // generate Nuxt types and auto-imports
51
- export function getPrepareCmd(packageManager) {
52
- const command = 'nuxt prepare'
53
- switch (packageManager) {
54
- case 'pnpm':
55
- return `pnpm exec ${command}`
56
- case 'yarn':
57
- return `yarn ${command}`
58
- case 'bun':
59
- return `bunx ${command}`
60
- case 'deno':
61
- return `deno run -A npm:${command}`
62
- default:
63
- return `npx ${command}`
64
- }
65
- }
66
-
67
- // install/update playwright
68
- export function getPlaywrightInstallCmd(packageManager) {
69
- const command = 'playwright install'
70
- switch (packageManager) {
71
- case 'pnpm':
72
- return `pnpm exec ${command}`
73
- case 'yarn':
74
- return `yarn ${command}`
75
- case 'bun':
76
- return `bunx ${command}`
77
- case 'deno':
78
- return `deno run -A npm:${command}`
79
- default:
80
- return `npx ${command}`
81
- }
82
- }
1
+ import { getPackageManager } from 'elrh-cosca'
2
+
3
+ export const SUPPORTED_PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun', 'deno']
4
+
5
+ // get the package manager name for usage in commands
6
+ // explicit override takes precedence over auto-detection
7
+ export function resolvePackageManager(override) {
8
+ if (override) {
9
+ if (!SUPPORTED_PACKAGE_MANAGERS.includes(override)) {
10
+ throw new Error(`Unsupported package manager '${override}'. Use one of: ${SUPPORTED_PACKAGE_MANAGERS.join(', ')}`)
11
+ }
12
+ return override
13
+ }
14
+ return getPackageManager()
15
+ }
16
+
17
+ // use nuxt-spec CLI tool
18
+ export function getCmd(packageManager) {
19
+ const command = 'nuxt-spec'
20
+ switch (packageManager) {
21
+ case 'pnpm':
22
+ return `pnpx ${command}`
23
+ case 'yarn':
24
+ return `yarn dlx ${command}`
25
+ case 'bun':
26
+ return `bunx ${command}`
27
+ case 'deno':
28
+ return `deno run -A npm:${command}`
29
+ default:
30
+ return `npx ${command}`
31
+ }
32
+ }
33
+
34
+ // update nuxt-spec
35
+ export function getUpdateCmd(packageManager, target) {
36
+ switch (packageManager) {
37
+ case 'pnpm':
38
+ return `pnpm update ${target}`
39
+ case 'yarn':
40
+ return `yarn up ${target}`
41
+ case 'bun':
42
+ return `bun update ${target}`
43
+ case 'deno':
44
+ return `deno add npm:${target}`
45
+ default:
46
+ return `npm install ${target}`
47
+ }
48
+ }
49
+
50
+ // generate Nuxt types and auto-imports
51
+ export function getPrepareCmd(packageManager) {
52
+ const command = 'nuxt prepare'
53
+ switch (packageManager) {
54
+ case 'pnpm':
55
+ return `pnpm exec ${command}`
56
+ case 'yarn':
57
+ return `yarn ${command}`
58
+ case 'bun':
59
+ return `bunx ${command}`
60
+ case 'deno':
61
+ return `deno run -A npm:${command}`
62
+ default:
63
+ return `npx ${command}`
64
+ }
65
+ }
66
+
67
+ // install/update playwright
68
+ export function getPlaywrightInstallCmd(packageManager) {
69
+ const command = 'playwright install'
70
+ switch (packageManager) {
71
+ case 'pnpm':
72
+ return `pnpm exec ${command}`
73
+ case 'yarn':
74
+ return `yarn ${command}`
75
+ case 'bun':
76
+ return `bunx ${command}`
77
+ case 'deno':
78
+ return `deno run -A npm:${command}`
79
+ default:
80
+ return `npx ${command}`
81
+ }
82
+ }
@@ -0,0 +1,8 @@
1
+ // current elrh-cosca version for usage in CLI scripts
2
+ export const COSCA_VERSION = '0.4.0-alpha.2'
3
+
4
+ // URL to current tag in GitHub repository to fetch template files in CLI scripts
5
+ export const COSCA_URL = `https://raw.githubusercontent.com/AloisSeckar/nuxt-spec/refs/tags/v${COSCA_VERSION}`
6
+
7
+ // current Playwright version for usage in CLI scripts
8
+ export const PLAYWRIGHT_VERSION = '1.64.0'