@pwtap/create 0.1.0

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 (103) hide show
  1. package/core-manifest.json +50 -0
  2. package/dist/commands/add.d.ts +8 -0
  3. package/dist/commands/add.d.ts.map +1 -0
  4. package/dist/commands/add.js +9 -0
  5. package/dist/commands/add.js.map +1 -0
  6. package/dist/commands/create.d.ts +12 -0
  7. package/dist/commands/create.d.ts.map +1 -0
  8. package/dist/commands/create.js +114 -0
  9. package/dist/commands/create.js.map +1 -0
  10. package/dist/commands/remove.d.ts +7 -0
  11. package/dist/commands/remove.d.ts.map +1 -0
  12. package/dist/commands/remove.js +9 -0
  13. package/dist/commands/remove.js.map +1 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +55 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/injectors/assets.d.ts +6 -0
  19. package/dist/injectors/assets.d.ts.map +1 -0
  20. package/dist/injectors/assets.js +53 -0
  21. package/dist/injectors/assets.js.map +1 -0
  22. package/dist/injectors/envJson.d.ts +6 -0
  23. package/dist/injectors/envJson.d.ts.map +1 -0
  24. package/dist/injectors/envJson.js +39 -0
  25. package/dist/injectors/envJson.js.map +1 -0
  26. package/dist/injectors/fixturesBarrel.d.ts +11 -0
  27. package/dist/injectors/fixturesBarrel.d.ts.map +1 -0
  28. package/dist/injectors/fixturesBarrel.js +66 -0
  29. package/dist/injectors/fixturesBarrel.js.map +1 -0
  30. package/dist/injectors/packageJson.d.ts +6 -0
  31. package/dist/injectors/packageJson.d.ts.map +1 -0
  32. package/dist/injectors/packageJson.js +25 -0
  33. package/dist/injectors/packageJson.js.map +1 -0
  34. package/dist/injectors/pwConfig.d.ts +10 -0
  35. package/dist/injectors/pwConfig.d.ts.map +1 -0
  36. package/dist/injectors/pwConfig.js +40 -0
  37. package/dist/injectors/pwConfig.js.map +1 -0
  38. package/dist/manifest.d.ts +65 -0
  39. package/dist/manifest.d.ts.map +1 -0
  40. package/dist/manifest.js +22 -0
  41. package/dist/manifest.js.map +1 -0
  42. package/dist/plugin-apply.d.ts +13 -0
  43. package/dist/plugin-apply.d.ts.map +1 -0
  44. package/dist/plugin-apply.js +89 -0
  45. package/dist/plugin-apply.js.map +1 -0
  46. package/dist/prompts.d.ts +22 -0
  47. package/dist/prompts.d.ts.map +1 -0
  48. package/dist/prompts.js +65 -0
  49. package/dist/prompts.js.map +1 -0
  50. package/dist/registry.d.ts +26 -0
  51. package/dist/registry.d.ts.map +1 -0
  52. package/dist/registry.js +34 -0
  53. package/dist/registry.js.map +1 -0
  54. package/dist/util/fs.d.ts +13 -0
  55. package/dist/util/fs.d.ts.map +1 -0
  56. package/dist/util/fs.js +32 -0
  57. package/dist/util/fs.js.map +1 -0
  58. package/dist/util/log.d.ts +8 -0
  59. package/dist/util/log.d.ts.map +1 -0
  60. package/dist/util/log.js +9 -0
  61. package/dist/util/log.js.map +1 -0
  62. package/dist/util/markers.d.ts +20 -0
  63. package/dist/util/markers.d.ts.map +1 -0
  64. package/dist/util/markers.js +51 -0
  65. package/dist/util/markers.js.map +1 -0
  66. package/dist/util/run.d.ts +8 -0
  67. package/dist/util/run.d.ts.map +1 -0
  68. package/dist/util/run.js +27 -0
  69. package/dist/util/run.js.map +1 -0
  70. package/package.json +31 -0
  71. package/template/.commitlintrc.json +3 -0
  72. package/template/.prettierrc +17 -0
  73. package/template/api/core/ApiClient.ts +99 -0
  74. package/template/api/core/types.ts +29 -0
  75. package/template/api/index.ts +4 -0
  76. package/template/api/models/pet.ts +24 -0
  77. package/template/api/services/PetService.ts +67 -0
  78. package/template/config/envUtils.ts +70 -0
  79. package/template/config/index.ts +2 -0
  80. package/template/config/loadEnv.ts +110 -0
  81. package/template/env/environments.example.json +20 -0
  82. package/template/eslint.config.js +74 -0
  83. package/template/fixtures/api.ts +46 -0
  84. package/template/fixtures/auth.ts +150 -0
  85. package/template/fixtures/index.ts +38 -0
  86. package/template/fixtures/ui.ts +112 -0
  87. package/template/pages/BasePage.ts +225 -0
  88. package/template/pages/LoginPage.ts +50 -0
  89. package/template/pages/index.ts +2 -0
  90. package/template/playwright.config.ts +71 -0
  91. package/template/templates/gitignore +19 -0
  92. package/template/testData/users.example.json +17 -0
  93. package/template/tests/api/pet.api.ts +60 -0
  94. package/template/tests/example/authSession.spec.ts +34 -0
  95. package/template/tests/example/login.spec.ts +50 -0
  96. package/template/tsconfig.json +33 -0
  97. package/template/utils/apiUtils.ts +224 -0
  98. package/template/utils/dateUtils.ts +158 -0
  99. package/template/utils/index.ts +15 -0
  100. package/template/utils/stringUtils.ts +192 -0
  101. package/template/utils/uiUtils.ts +236 -0
  102. package/template/utils/validationUtils.ts +190 -0
  103. package/template/utils/waitUtils.ts +207 -0
@@ -0,0 +1,40 @@
1
+ import path from 'node:path';
2
+ import { readText, writeText } from '../util/fs.js';
3
+ import { addToRegion, hasRegion, removeFromRegion } from '../util/markers.js';
4
+ function configPath(clientDir) {
5
+ return path.join(clientDir, 'playwright.config.ts');
6
+ }
7
+ /**
8
+ * Splice a plugin's env gate and project spread into playwright.config.ts. Returns false if a managed
9
+ * marker is missing (caller then prints a paste block). Each project is env-gated so a bare
10
+ * `npm test` stays UI + API only.
11
+ */
12
+ export function applyProject(clientDir, m) {
13
+ const pp = m.playwrightProject;
14
+ if (!pp) {
15
+ return true;
16
+ }
17
+ const file = configPath(clientDir);
18
+ let src = readText(file);
19
+ if (!hasRegion(src, 'plugins:gates') || !hasRegion(src, 'plugins:projects')) {
20
+ return false;
21
+ }
22
+ src = addToRegion(src, 'plugins:gates', pp.gate, pp.gateVar);
23
+ const projectLine = ` ${pp.project.replace(/,\s*$/, '')},`;
24
+ src = addToRegion(src, 'plugins:projects', projectLine, pp.gateVar);
25
+ writeText(file, src);
26
+ return true;
27
+ }
28
+ /** Reverse applyProject (matches on the gate variable name). */
29
+ export function removeProject(clientDir, m) {
30
+ const pp = m.playwrightProject;
31
+ if (!pp) {
32
+ return;
33
+ }
34
+ const file = configPath(clientDir);
35
+ let src = readText(file);
36
+ src = removeFromRegion(src, 'plugins:gates', pp.gateVar);
37
+ src = removeFromRegion(src, 'plugins:projects', pp.gateVar);
38
+ writeText(file, src);
39
+ }
40
+ //# sourceMappingURL=pwConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pwConfig.js","sourceRoot":"","sources":["../../src/injectors/pwConfig.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,SAAS,UAAU,CAAC,SAAiB;IACnC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,CAAiB;IAC/D,MAAM,EAAE,GAAG,CAAC,CAAC,iBAAiB,CAAC;IAC/B,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACnC,IAAI,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,kBAAkB,CAAC,EAAE,CAAC;QAC5E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,eAAe,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,OAAO,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC;IAC9D,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,kBAAkB,EAAE,WAAW,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IACpE,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,CAAiB;IAChE,MAAM,EAAE,GAAG,CAAC,CAAC,iBAAiB,CAAC;IAC/B,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACnC,IAAI,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzB,GAAG,GAAG,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IACzD,GAAG,GAAG,gBAAgB,CAAC,GAAG,EAAE,kBAAkB,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IAC5D,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The authoritative manifest a plugin ships (compiled to `dist/manifest.js`, exported via the
3
+ * package's `"./manifest"` entry). @pwtap/create loads it AFTER installing the plugin to know what
4
+ * to inject into the client project.
5
+ *
6
+ * @example
7
+ * export const manifest: PluginManifest = {
8
+ * id: 'maestro', name: '@pwtap/plugin-maestro',
9
+ * devDependencies: {}, scripts: { 'test:maestro': 'MAESTRO=1 playwright test --project=maestro' },
10
+ * envKeys: { MOBILE_PLATFORM: 'android' },
11
+ * fixture: { importFrom: '@pwtap/plugin-maestro', test: { alias: 'maestroTest' } },
12
+ * };
13
+ */
14
+ export interface PluginManifest {
15
+ id: string;
16
+ name: string;
17
+ devDependencies: Record<string, string>;
18
+ scripts: Record<string, string>;
19
+ envKeys: Record<string, string>;
20
+ fixture?: {
21
+ importFrom: string;
22
+ /** Composable test object merged via mergeTests. Omit for matcher-only plugins (e.g. ai-judge). */
23
+ test?: {
24
+ export?: string;
25
+ alias: string;
26
+ };
27
+ /** Custom-matcher `expect` merged via mergeExpects. Omit for fixture-only plugins (e.g. maestro). */
28
+ expect?: {
29
+ export?: string;
30
+ alias: string;
31
+ };
32
+ };
33
+ playwrightProject?: {
34
+ gateVar: string;
35
+ gate: string;
36
+ project: string;
37
+ globalTeardown?: string;
38
+ };
39
+ examples?: Array<{
40
+ src: string;
41
+ dest: string;
42
+ }>;
43
+ docs?: Array<{
44
+ src: string;
45
+ dest: string;
46
+ }>;
47
+ readmeSection?: string;
48
+ /** Package-relative path to a compiled module exporting `async ensure(): Promise<void>` (advisory). */
49
+ ensure?: string;
50
+ }
51
+ /** The base package.json scripts/devDeps and Playwright browsers the core scaffold writes. */
52
+ export interface CoreManifest {
53
+ scripts: Record<string, string>;
54
+ devDependencies: Record<string, string>;
55
+ browsers: string[];
56
+ /** Extra fields merged verbatim into the generated package.json (e.g. lint-staged, commitizen config). */
57
+ packageJson?: Record<string, unknown>;
58
+ }
59
+ export declare function loadCoreManifest(coreManifestPath: string): CoreManifest;
60
+ /**
61
+ * Load an installed plugin's manifest by resolving `<pkg>/manifest` from the client's node_modules.
62
+ * Returns `null` when the package or its manifest export can't be resolved (caller warns and skips).
63
+ */
64
+ export declare function loadPluginManifest(clientDir: string, pkg: string): Promise<PluginManifest | null>;
65
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,mGAAmG;QACnG,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1C,qGAAqG;QACrG,MAAM,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;KAC7C,CAAC;IACF,iBAAiB,CAAC,EAAE;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,8FAA8F;AAC9F,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,0GAA0G;IAC1G,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,wBAAgB,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,CAEvE;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAShC"}
@@ -0,0 +1,22 @@
1
+ import { createRequire } from 'node:module';
2
+ import { pathToFileURL } from 'node:url';
3
+ import { readJson } from './util/fs.js';
4
+ export function loadCoreManifest(coreManifestPath) {
5
+ return readJson(coreManifestPath);
6
+ }
7
+ /**
8
+ * Load an installed plugin's manifest by resolving `<pkg>/manifest` from the client's node_modules.
9
+ * Returns `null` when the package or its manifest export can't be resolved (caller warns and skips).
10
+ */
11
+ export async function loadPluginManifest(clientDir, pkg) {
12
+ try {
13
+ const require = createRequire(`${clientDir}/`);
14
+ const resolved = require.resolve(`${pkg}/manifest`, { paths: [clientDir] });
15
+ const mod = (await import(pathToFileURL(resolved).href));
16
+ return mod.manifest ?? null;
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAkDxC,MAAM,UAAU,gBAAgB,CAAC,gBAAwB;IACvD,OAAO,QAAQ,CAAe,gBAAgB,CAAC,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAiB,EACjB,GAAW;IAEX,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,GAAG,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC5E,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAkC,CAAC;QAC1F,OAAO,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface AddOptions {
2
+ clientDir: string;
3
+ pluginIds: string[];
4
+ install: boolean;
5
+ }
6
+ export declare function addPlugins({ clientDir, pluginIds, install }: AddOptions): Promise<void>;
7
+ export interface RemoveOptions {
8
+ clientDir: string;
9
+ pluginIds: string[];
10
+ uninstall?: boolean;
11
+ }
12
+ export declare function removePlugins({ clientDir, pluginIds, uninstall, }: RemoveOptions): Promise<void>;
13
+ //# sourceMappingURL=plugin-apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-apply.d.ts","sourceRoot":"","sources":["../src/plugin-apply.ts"],"names":[],"mappings":"AAwDA,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,UAAU,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB7F;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,aAAa,CAAC,EAClC,SAAS,EACT,SAAS,EACT,SAAgB,GACjB,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB/B"}
@@ -0,0 +1,89 @@
1
+ import { copyDocs, copyExamples } from './injectors/assets.js';
2
+ import { mergePluginEnv, removePluginEnv } from './injectors/envJson.js';
3
+ import { applyFixture, removeFixture } from './injectors/fixturesBarrel.js';
4
+ import { mergePluginPackageJson, removePluginPackageJson } from './injectors/packageJson.js';
5
+ import { applyProject, removeProject } from './injectors/pwConfig.js';
6
+ import { loadPluginManifest } from './manifest.js';
7
+ import { findKnownPlugin } from './registry.js';
8
+ import { log } from './util/log.js';
9
+ import { run } from './util/run.js';
10
+ /** Resolve plugin ids/packages to npm package names via the known-plugins registry. */
11
+ function toPackages(pluginIds) {
12
+ return pluginIds
13
+ .map(id => findKnownPlugin(id)?.package ?? (id.startsWith('@') ? id : undefined))
14
+ .filter((p) => Boolean(p));
15
+ }
16
+ /** Run all injectors for one plugin manifest. Warns (does not throw) when a marker is missing. */
17
+ function injectManifest(clientDir, m) {
18
+ mergePluginPackageJson(clientDir, m);
19
+ mergePluginEnv(clientDir, m);
20
+ copyExamples(clientDir, m);
21
+ copyDocs(clientDir, m);
22
+ if (applyFixture(clientDir, m) === false) {
23
+ log.warn(`fixtures/index.ts is missing a pwtap marker — wire ${m.name} into the barrel manually ` +
24
+ `(import from '${m.fixture?.importFrom}' and add to mergeTests/mergeExpects).`);
25
+ }
26
+ if (applyProject(clientDir, m) === false) {
27
+ log.warn(`playwright.config.ts is missing a pwtap marker — add this project manually:\n ${m.playwrightProject?.gate}`);
28
+ }
29
+ }
30
+ /** Best-effort post-install host check (advisory — never throws). */
31
+ async function runEnsure(clientDir, m) {
32
+ if (!m.ensure) {
33
+ return;
34
+ }
35
+ try {
36
+ const { createRequire } = await import('node:module');
37
+ const { pathToFileURL } = await import('node:url');
38
+ const require = createRequire(`${clientDir}/`);
39
+ const resolved = require.resolve(`${m.name}/${m.ensure}`, { paths: [clientDir] });
40
+ const mod = (await import(pathToFileURL(resolved).href));
41
+ await mod.ensure?.();
42
+ }
43
+ catch (err) {
44
+ log.warn(`${m.name} ensure() check skipped: ${err instanceof Error ? err.message : String(err)}`);
45
+ }
46
+ }
47
+ export async function addPlugins({ clientDir, pluginIds, install }) {
48
+ const packages = toPackages(pluginIds);
49
+ if (packages.length === 0) {
50
+ return;
51
+ }
52
+ if (install) {
53
+ log.step(`Installing plugin${packages.length > 1 ? 's' : ''}: ${packages.join(', ')}`);
54
+ await run('npm', ['install', '-D', ...packages], { cwd: clientDir });
55
+ }
56
+ for (const pkg of packages) {
57
+ const m = await loadPluginManifest(clientDir, pkg);
58
+ if (!m) {
59
+ log.warn(`Could not load manifest for ${pkg} — is it installed? Skipping.`);
60
+ continue;
61
+ }
62
+ injectManifest(clientDir, m);
63
+ await runEnsure(clientDir, m);
64
+ log.done(`Added ${pkg}`);
65
+ }
66
+ // Reconcile: install any devDependencies the plugin manifests added to package.json.
67
+ if (install) {
68
+ await run('npm', ['install'], { cwd: clientDir });
69
+ }
70
+ }
71
+ export async function removePlugins({ clientDir, pluginIds, uninstall = true, }) {
72
+ const packages = toPackages(pluginIds);
73
+ for (const pkg of packages) {
74
+ const m = await loadPluginManifest(clientDir, pkg);
75
+ if (!m) {
76
+ log.warn(`Could not load manifest for ${pkg} — skipping (already removed?).`);
77
+ continue;
78
+ }
79
+ removeFixture(clientDir, m);
80
+ removeProject(clientDir, m);
81
+ removePluginEnv(clientDir, m);
82
+ removePluginPackageJson(clientDir, m);
83
+ log.done(`Removed ${pkg}`);
84
+ }
85
+ if (uninstall && packages.length > 0) {
86
+ await run('npm', ['uninstall', ...packages], { cwd: clientDir });
87
+ }
88
+ }
89
+ //# sourceMappingURL=plugin-apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-apply.js","sourceRoot":"","sources":["../src/plugin-apply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAuB,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEpC,uFAAuF;AACvF,SAAS,UAAU,CAAC,SAAmB;IACrC,OAAO,SAAS;SACb,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;SAChF,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,kGAAkG;AAClG,SAAS,cAAc,CAAC,SAAiB,EAAE,CAAiB;IAC1D,sBAAsB,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACrC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC7B,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAEvB,IAAI,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;QACzC,GAAG,CAAC,IAAI,CACN,sDAAsD,CAAC,CAAC,IAAI,4BAA4B;YACtF,iBAAiB,CAAC,CAAC,OAAO,EAAE,UAAU,wCAAwC,CACjF,CAAC;IACJ,CAAC;IACD,IAAI,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;QACzC,GAAG,CAAC,IAAI,CACN,kFAAkF,CAAC,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAC9G,CAAC;IACJ,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,KAAK,UAAU,SAAS,CAAC,SAAiB,EAAE,CAAiB;IAC3D,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACd,OAAO;IACT,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;QACtD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAqC,CAAC;QAC7F,MAAM,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,IAAI,CACN,GAAG,CAAC,CAAC,IAAI,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACxF,CAAC;IACJ,CAAC;AACH,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAc;IAC5E,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACvC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IACD,IAAI,OAAO,EAAE,CAAC;QACZ,GAAG,CAAC,IAAI,CAAC,oBAAoB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvF,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,MAAM,kBAAkB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACnD,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,IAAI,CAAC,+BAA+B,GAAG,+BAA+B,CAAC,CAAC;YAC5E,SAAS;QACX,CAAC;QACD,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC7B,MAAM,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC9B,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;IAC3B,CAAC;IACD,qFAAqF;IACrF,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAClC,SAAS,EACT,SAAS,EACT,SAAS,GAAG,IAAI,GACF;IACd,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,MAAM,kBAAkB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACnD,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,IAAI,CAAC,+BAA+B,GAAG,iCAAiC,CAAC,CAAC;YAC9E,SAAS;QACX,CAAC;QACD,aAAa,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC5B,aAAa,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC5B,eAAe,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC9B,uBAAuB,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QACtC,GAAG,CAAC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,GAAG,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IACnE,CAAC;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { KnownPlugin } from './registry.js';
2
+ /**
3
+ * Tiny zero-dependency prompter. In non-interactive mode (no TTY or `--yes`) every prompt resolves
4
+ * to its default immediately, so CI and `npm init @pwtap dir -y` never hang.
5
+ *
6
+ * @example
7
+ * const p = new Prompter(false);
8
+ * const name = await p.text('Project name', 'my-tests');
9
+ * p.close();
10
+ */
11
+ export declare class Prompter {
12
+ private readonly interactive;
13
+ private rl?;
14
+ constructor(yes: boolean);
15
+ private io;
16
+ text(question: string, def: string): Promise<string>;
17
+ confirm(question: string, def: boolean): Promise<boolean>;
18
+ /** Present selectable plugins (coming-soon shown but not selectable); returns chosen ids. */
19
+ selectPlugins(plugins: KnownPlugin[]): Promise<string[]>;
20
+ close(): void;
21
+ }
22
+ //# sourceMappingURL=prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;;;;;GAQG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,EAAE,CAAC,CAAqB;gBAEpB,GAAG,EAAE,OAAO;IAIxB,OAAO,CAAC,EAAE;IAOJ,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQpD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAY/D,6FAA6F;IACvF,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAqB9D,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,65 @@
1
+ import { stdin, stdout } from 'node:process';
2
+ import readline from 'node:readline/promises';
3
+ /**
4
+ * Tiny zero-dependency prompter. In non-interactive mode (no TTY or `--yes`) every prompt resolves
5
+ * to its default immediately, so CI and `npm init @pwtap dir -y` never hang.
6
+ *
7
+ * @example
8
+ * const p = new Prompter(false);
9
+ * const name = await p.text('Project name', 'my-tests');
10
+ * p.close();
11
+ */
12
+ export class Prompter {
13
+ interactive;
14
+ rl;
15
+ constructor(yes) {
16
+ this.interactive = Boolean(stdin.isTTY) && !yes;
17
+ }
18
+ io() {
19
+ if (!this.rl) {
20
+ this.rl = readline.createInterface({ input: stdin, output: stdout });
21
+ }
22
+ return this.rl;
23
+ }
24
+ async text(question, def) {
25
+ if (!this.interactive) {
26
+ return def;
27
+ }
28
+ const answer = (await this.io().question(`${question} (${def}): `)).trim();
29
+ return answer || def;
30
+ }
31
+ async confirm(question, def) {
32
+ if (!this.interactive) {
33
+ return def;
34
+ }
35
+ const hint = def ? 'Y/n' : 'y/N';
36
+ const answer = (await this.io().question(`${question} (${hint}): `)).trim().toLowerCase();
37
+ if (!answer) {
38
+ return def;
39
+ }
40
+ return answer === 'y' || answer === 'yes';
41
+ }
42
+ /** Present selectable plugins (coming-soon shown but not selectable); returns chosen ids. */
43
+ async selectPlugins(plugins) {
44
+ const selectable = plugins.filter(p => p.status !== 'coming-soon');
45
+ if (!this.interactive || selectable.length === 0) {
46
+ return plugins.filter(p => p.defaultSelected && p.status !== 'coming-soon').map(p => p.id);
47
+ }
48
+ stdout.write('\nOptional plugins (comma-separated numbers, blank for none):\n');
49
+ plugins.forEach((p, i) => {
50
+ const n = p.status === 'coming-soon' ? ' -' : ` ${String(i + 1).padStart(2)}`;
51
+ const tag = p.status === 'coming-soon' ? ' [coming soon]' : '';
52
+ stdout.write(`${n}. [${p.category}] ${p.package} — ${p.description}${tag}\n`);
53
+ });
54
+ const answer = await this.io().question('> ');
55
+ const picked = new Set(answer
56
+ .split(',')
57
+ .map(s => Number(s.trim()))
58
+ .filter(n => Number.isInteger(n)));
59
+ return plugins.filter((p, i) => picked.has(i + 1) && p.status !== 'coming-soon').map(p => p.id);
60
+ }
61
+ close() {
62
+ this.rl?.close();
63
+ }
64
+ }
65
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Curated, pre-install plugin registry. Ships inside @pwtap/create so the interactive menu can be
3
+ * built BEFORE any plugin is installed. The authoritative injection metadata travels with each
4
+ * plugin (see PluginManifest); this only drives menu rendering and `--flag` resolution.
5
+ *
6
+ * @example
7
+ * const chosen = KNOWN_PLUGINS.filter(p => selectedIds.includes(p.id)).map(p => p.package);
8
+ */
9
+ export interface KnownPlugin {
10
+ /** Stable slug — used for dirs, markers, and the Playwright project name. */
11
+ id: string;
12
+ /** npm package to install (e.g. '@pwtap/plugin-maestro'). */
13
+ package: string;
14
+ /** Menu grouping label. */
15
+ category: string;
16
+ /** One-line menu description. */
17
+ description: string;
18
+ /** Non-interactive selection flag (e.g. '--maestro'). */
19
+ flag: string;
20
+ defaultSelected: boolean;
21
+ status?: 'stable' | 'coming-soon';
22
+ }
23
+ export declare const KNOWN_PLUGINS: KnownPlugin[];
24
+ /** Find a known plugin by id or package name. */
25
+ export declare function findKnownPlugin(idOrPackage: string): KnownPlugin | undefined;
26
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,EAAE,EAAE,MAAM,CAAC;IACX,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;CACnC;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EA4BtC,CAAC;AAEF,iDAAiD;AACjD,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAE5E"}
@@ -0,0 +1,34 @@
1
+ export const KNOWN_PLUGINS = [
2
+ {
3
+ id: 'maestro',
4
+ package: '@pwtap/plugin-maestro',
5
+ category: 'mobile',
6
+ description: 'Mobile testing with Maestro flows (Android + iOS)',
7
+ flag: '--maestro',
8
+ defaultSelected: false,
9
+ status: 'coming-soon',
10
+ },
11
+ {
12
+ id: 'appium',
13
+ package: '@pwtap/plugin-appium',
14
+ category: 'mobile',
15
+ description: 'Mobile testing with Appium (iOS XCUITest, Android UiAutomator2)',
16
+ flag: '--appium',
17
+ defaultSelected: false,
18
+ status: 'coming-soon',
19
+ },
20
+ {
21
+ id: 'ai-judge',
22
+ package: '@pwtap/plugin-ai-judge',
23
+ category: 'ai',
24
+ description: 'AI/LLM judge matchers (toPassRubric, toScoreAtLeast, toMatchImage)',
25
+ flag: '--ai-judge',
26
+ defaultSelected: false,
27
+ status: 'stable',
28
+ },
29
+ ];
30
+ /** Find a known plugin by id or package name. */
31
+ export function findKnownPlugin(idOrPackage) {
32
+ return KNOWN_PLUGINS.find(p => p.id === idOrPackage || p.package === idOrPackage);
33
+ }
34
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAuBA,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C;QACE,EAAE,EAAE,SAAS;QACb,OAAO,EAAE,uBAAuB;QAChC,QAAQ,EAAE,QAAQ;QAClB,WAAW,EAAE,mDAAmD;QAChE,IAAI,EAAE,WAAW;QACjB,eAAe,EAAE,KAAK;QACtB,MAAM,EAAE,aAAa;KACtB;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,OAAO,EAAE,sBAAsB;QAC/B,QAAQ,EAAE,QAAQ;QAClB,WAAW,EAAE,iEAAiE;QAC9E,IAAI,EAAE,UAAU;QAChB,eAAe,EAAE,KAAK;QACtB,MAAM,EAAE,aAAa;KACtB;IACD;QACE,EAAE,EAAE,UAAU;QACd,OAAO,EAAE,wBAAwB;QACjC,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,oEAAoE;QACjF,IAAI,EAAE,YAAY;QAClB,eAAe,EAAE,KAAK;QACtB,MAAM,EAAE,QAAQ;KACjB;CACF,CAAC;AAEF,iDAAiD;AACjD,MAAM,UAAU,eAAe,CAAC,WAAmB;IACjD,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,WAAW,IAAI,CAAC,CAAC,OAAO,KAAK,WAAW,CAAC,CAAC;AACpF,CAAC"}
@@ -0,0 +1,13 @@
1
+ /** Recursively copy a directory tree (Node 20's fs.cpSync). */
2
+ export declare function copyDir(src: string, dest: string): void;
3
+ export declare function exists(p: string): boolean;
4
+ export declare function readText(p: string): string;
5
+ export declare function writeText(p: string, content: string): void;
6
+ export declare function readJson<T>(p: string): T;
7
+ export declare function writeJson(p: string, data: unknown): void;
8
+ export declare function ensureDir(p: string): void;
9
+ /** True when `p` is absent or an empty directory (used to guard scaffolding into a fresh dir). */
10
+ export declare function isEmptyDir(p: string): boolean;
11
+ /** Sort an object's keys (stable package.json output). */
12
+ export declare function sortObject<T extends Record<string, unknown>>(obj: T): T;
13
+ //# sourceMappingURL=fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/util/fs.ts"],"names":[],"mappings":"AAEA,+DAA+D;AAC/D,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzC;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1D;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,CAExC;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAExD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,kGAAkG;AAClG,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAEvE"}
@@ -0,0 +1,32 @@
1
+ import fs from 'node:fs';
2
+ /** Recursively copy a directory tree (Node 20's fs.cpSync). */
3
+ export function copyDir(src, dest) {
4
+ fs.cpSync(src, dest, { recursive: true });
5
+ }
6
+ export function exists(p) {
7
+ return fs.existsSync(p);
8
+ }
9
+ export function readText(p) {
10
+ return fs.readFileSync(p, 'utf8');
11
+ }
12
+ export function writeText(p, content) {
13
+ fs.writeFileSync(p, content);
14
+ }
15
+ export function readJson(p) {
16
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
17
+ }
18
+ export function writeJson(p, data) {
19
+ fs.writeFileSync(p, `${JSON.stringify(data, null, 2)}\n`);
20
+ }
21
+ export function ensureDir(p) {
22
+ fs.mkdirSync(p, { recursive: true });
23
+ }
24
+ /** True when `p` is absent or an empty directory (used to guard scaffolding into a fresh dir). */
25
+ export function isEmptyDir(p) {
26
+ return !fs.existsSync(p) || fs.readdirSync(p).length === 0;
27
+ }
28
+ /** Sort an object's keys (stable package.json output). */
29
+ export function sortObject(obj) {
30
+ return Object.fromEntries(Object.entries(obj).sort(([a], [b]) => a.localeCompare(b)));
31
+ }
32
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/util/fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,+DAA+D;AAC/D,MAAM,UAAU,OAAO,CAAC,GAAW,EAAE,IAAY;IAC/C,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,CAAS;IAC9B,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,CAAS;IAChC,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,CAAS,EAAE,OAAe;IAClD,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAI,CAAS;IACnC,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAM,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,CAAS,EAAE,IAAa;IAChD,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,CAAS;IACjC,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,UAAU,CAAoC,GAAM;IAClE,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAM,CAAC;AAC7F,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare const log: {
2
+ info: (msg: string) => void;
3
+ warn: (msg: string) => void;
4
+ error: (msg: string) => void;
5
+ step: (msg: string) => void;
6
+ done: (msg: string) => void;
7
+ };
8
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/util/log.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,GAAG;gBACF,MAAM,KAAG,IAAI;gBACb,MAAM,KAAG,IAAI;iBACZ,MAAM,KAAG,IAAI;gBACd,MAAM,KAAG,IAAI;gBACb,MAAM,KAAG,IAAI;CAC1B,CAAC"}
@@ -0,0 +1,9 @@
1
+ /* Minimal console logger for the scaffolder. */
2
+ export const log = {
3
+ info: (msg) => console.info(msg),
4
+ warn: (msg) => console.warn(`⚠ ${msg}`),
5
+ error: (msg) => console.error(`✖ ${msg}`),
6
+ step: (msg) => console.info(`\n▸ ${msg}`),
7
+ done: (msg) => console.info(`✓ ${msg}`),
8
+ };
9
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/util/log.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;IAC9C,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IACrD,KAAK,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;IACvD,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;IACvD,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;CACtD,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Managed-region editing for generated files. A region is a pair of line comments
3
+ * `// pwtap:<key>` … `// pwtap:<key>:end`; `add`/`remove` splice lines between them idempotently.
4
+ * If a marker is missing (a user deleted it) `locate` throws a `MarkerError` so the caller can print
5
+ * a paste-block instead of making a half-edit.
6
+ *
7
+ * @example
8
+ * let src = addToRegion(barrel, 'plugins:tests', ' maestroTest,', 'maestroTest');
9
+ * src = removeFromRegion(src, 'plugins:tests', 'maestroTest');
10
+ */
11
+ export declare class MarkerError extends Error {
12
+ readonly key: string;
13
+ constructor(key: string);
14
+ }
15
+ export declare function hasRegion(source: string, key: string): boolean;
16
+ /** Insert `line` before the end marker of `key`, unless `uniq` already appears in the region. */
17
+ export declare function addToRegion(source: string, key: string, line: string, uniq: string): string;
18
+ /** Remove every line inside `key`'s region that contains `uniq`. */
19
+ export declare function removeFromRegion(source: string, key: string, uniq: string): string;
20
+ //# sourceMappingURL=markers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.d.ts","sourceRoot":"","sources":["../../src/util/markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qBAAa,WAAY,SAAQ,KAAK;aACR,GAAG,EAAE,MAAM;gBAAX,GAAG,EAAE,MAAM;CAIxC;AAeD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ3F;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlF"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Managed-region editing for generated files. A region is a pair of line comments
3
+ * `// pwtap:<key>` … `// pwtap:<key>:end`; `add`/`remove` splice lines between them idempotently.
4
+ * If a marker is missing (a user deleted it) `locate` throws a `MarkerError` so the caller can print
5
+ * a paste-block instead of making a half-edit.
6
+ *
7
+ * @example
8
+ * let src = addToRegion(barrel, 'plugins:tests', ' maestroTest,', 'maestroTest');
9
+ * src = removeFromRegion(src, 'plugins:tests', 'maestroTest');
10
+ */
11
+ export class MarkerError extends Error {
12
+ key;
13
+ constructor(key) {
14
+ super(`[pwtap] managed region '${key}' markers not found`);
15
+ this.key = key;
16
+ this.name = 'MarkerError';
17
+ }
18
+ }
19
+ const startMarker = (key) => `// pwtap:${key}`;
20
+ const endMarker = (key) => `// pwtap:${key}:end`;
21
+ function locate(source, key) {
22
+ const lines = source.split('\n');
23
+ const start = lines.findIndex(l => l.trim() === startMarker(key));
24
+ const end = lines.findIndex(l => l.trim() === endMarker(key));
25
+ if (start === -1 || end === -1 || end < start) {
26
+ throw new MarkerError(key);
27
+ }
28
+ return { start, end, lines };
29
+ }
30
+ export function hasRegion(source, key) {
31
+ return source.includes(startMarker(key)) && source.includes(endMarker(key));
32
+ }
33
+ /** Insert `line` before the end marker of `key`, unless `uniq` already appears in the region. */
34
+ export function addToRegion(source, key, line, uniq) {
35
+ const { start, end, lines } = locate(source, key);
36
+ const region = lines.slice(start + 1, end);
37
+ if (region.some(l => l.includes(uniq))) {
38
+ return source; // already present — idempotent
39
+ }
40
+ lines.splice(end, 0, line);
41
+ return lines.join('\n');
42
+ }
43
+ /** Remove every line inside `key`'s region that contains `uniq`. */
44
+ export function removeFromRegion(source, key, uniq) {
45
+ const { start, end, lines } = locate(source, key);
46
+ const before = lines.slice(0, start + 1);
47
+ const inside = lines.slice(start + 1, end).filter(l => !l.includes(uniq));
48
+ const after = lines.slice(end);
49
+ return [...before, ...inside, ...after].join('\n');
50
+ }
51
+ //# sourceMappingURL=markers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.js","sourceRoot":"","sources":["../../src/util/markers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface RunOptions {
2
+ cwd?: string;
3
+ /** Silence child stdio (default false — inherit, so npm/playwright output is visible). */
4
+ silent?: boolean;
5
+ }
6
+ /** Run a command to completion; rejects on non-zero exit or spawn error. */
7
+ export declare function run(cmd: string, args: string[], opts?: RunOptions): Promise<void>;
8
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/util/run.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,4EAA4E;AAC5E,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAerF"}