vyriy 0.3.5 → 0.3.9

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 (38) hide show
  1. package/README.md +48 -21
  2. package/cli/args/args.js +6 -0
  3. package/cli/args/types.d.ts +5 -1
  4. package/cli/cli.js +18 -2
  5. package/commands/new/new.js +77 -6
  6. package/commands/new/types.d.ts +2 -0
  7. package/commands/publish/index.d.ts +2 -0
  8. package/commands/publish/index.js +1 -0
  9. package/commands/publish/publish.d.ts +2 -0
  10. package/commands/publish/publish.js +274 -0
  11. package/commands/publish/types.d.ts +31 -0
  12. package/index.d.ts +1 -0
  13. package/index.js +1 -0
  14. package/package.json +159 -1
  15. package/presets/base/createBaseFiles.d.ts +3 -0
  16. package/presets/base/createBaseFiles.js +307 -0
  17. package/presets/config.d.ts +28 -0
  18. package/presets/config.js +7 -0
  19. package/presets/createProjectFiles.js +4 -175
  20. package/presets/library/createLibraryUiFiles.d.ts +3 -0
  21. package/presets/library/createLibraryUiFiles.js +127 -0
  22. package/presets/packages/createPackageFiles.d.ts +3 -0
  23. package/presets/packages/createPackageFiles.js +39 -0
  24. package/presets/packages/createPackageManifest.d.ts +7 -0
  25. package/presets/packages/createPackageManifest.js +13 -0
  26. package/presets/workspaces/createWorkspaceFiles.d.ts +3 -0
  27. package/presets/workspaces/createWorkspaceFiles.js +98 -0
  28. package/project-plan/api/api.js +5 -17
  29. package/project-plan/api/types.d.ts +1 -0
  30. package/project-plan/create/create.js +68 -77
  31. package/project-plan/kind/kind.js +1 -16
  32. package/project-plan/types.d.ts +7 -7
  33. package/prompts/project-plan/project-plan.js +62 -24
  34. package/shared/index.d.ts +1 -0
  35. package/shared/index.js +1 -0
  36. package/shared/runCommand.d.ts +9 -0
  37. package/shared/runCommand.js +34 -0
  38. package/shared/types.d.ts +7 -0
package/README.md CHANGED
@@ -34,8 +34,13 @@ vyriy new my-app
34
34
  vyriy .
35
35
  vyriy init
36
36
  vyriy doctor
37
+ vyriy publish
37
38
  vyriy --dry-run
38
39
  vyriy --yes
40
+ vyriy --no-install
41
+ vyriy --no-verify
42
+ vyriy --install-only
43
+ vyriy --verify
39
44
  vyriy --overwrite
40
45
  vyriy --skip-existing
41
46
  vyriy --help
@@ -48,7 +53,7 @@ Runs the same flow as `vyriy new`.
48
53
 
49
54
  ### `vyriy new [name]`
50
55
 
51
- Starts the project planning wizard, prints the project summary and file plan, then writes generated files when no unresolved conflicts exist.
56
+ Starts the project planning wizard, prints the project summary and file plan, writes generated files when no unresolved conflicts exist, installs dependencies, and runs generated project checks.
52
57
 
53
58
  If `name` is provided, it is used as the default project name and target directory.
54
59
 
@@ -75,15 +80,37 @@ Current checks:
75
80
 
76
81
  Node.js is fatal when unsupported. Yarn and Git are warnings so generation can continue without silently installing tools or initializing Git.
77
82
 
83
+ ### `vyriy publish`
84
+
85
+ Prepares compiled `dist` package metadata for publishing without running `npm publish`.
86
+
87
+ Use it after TypeScript emits package files into `dist`, for example from a `build:dist` script.
88
+
78
89
  ## Flags
79
90
 
80
91
  ### `--dry-run`
81
92
 
82
- Prints the doctor report, project summary, and file plan without writing files or running fix commands.
93
+ Prints the doctor report, project summary, and file plan without writing files, installing dependencies, or running checks.
94
+
95
+ ### `--no-install`
96
+
97
+ Writes generated files but skips `yarn install` and `yarn check`.
98
+
99
+ ### `--no-verify`
100
+
101
+ Writes generated files and runs `yarn install`, but skips `yarn check`.
102
+
103
+ ### `--install-only`
104
+
105
+ Alias for `--no-verify`.
106
+
107
+ ### `--verify`
108
+
109
+ Explicitly enables `yarn check`. This is already the default unless `--no-install`, `--no-verify`, or `--install-only` is passed.
83
110
 
84
111
  ### `--yes`
85
112
 
86
- Uses default wizard answers and avoids prompts where possible. It does not overwrite existing files unless `--overwrite` is also passed.
113
+ Uses default wizard answers and avoids prompts where possible. In non-interactive mode, the default preset is `empty` with no CI/CD provider. It does not overwrite existing files unless `--overwrite` is also passed.
87
114
 
88
115
  ### `--overwrite`
89
116
 
@@ -106,10 +133,10 @@ The wizard collects:
106
133
  - project preset
107
134
  - API style for API-capable presets
108
135
  - CI/CD provider
109
- - optional infrastructure choices
136
+ - infrastructure provider
110
137
  - confirmation
111
138
 
112
- After confirmation, the CLI prints the project plan, creates generated files in memory, builds a conflict-aware file plan, and writes the accepted file plan.
139
+ After confirmation, the CLI prints the project plan, creates generated files in memory, builds a conflict-aware file plan, writes the accepted file plan, runs `yarn install`, and runs `yarn check`.
113
140
 
114
141
  Presets do not write to disk directly.
115
142
 
@@ -119,28 +146,28 @@ Generated projects always include `AGENTS.md` based on the shared Vyriy package
119
146
 
120
147
  Supported presets:
121
148
 
149
+ - `empty`
122
150
  - `library`
123
151
  - `api`
124
- - `react-csr`
125
- - `react-ssr`
126
- - `react-ssg`
127
- - `mfe`
128
- - `openmfe`
129
- - `mfe-bff`
130
- - `openmfe-bff`
152
+ - `ssr`
153
+ - `ssg`
154
+ - `csr`
131
155
  - `fullstack`
132
- - `aws-serverless`
133
- - `empty`
156
+ - `mfe`
157
+
158
+ The preset is the concrete future generated setup. The project kind is the broader architecture category. The infrastructure choice is selected separately: Docker is the default local/container shape, while AWS selects CDK plus Lambda/API Gateway for API-capable presets.
159
+
160
+ The `mfe` preset uses OpenMFE as the default MFE contract shape. There is no separate `openmfe` preset unless a future use case proves that split is useful.
134
161
 
135
- The preset is the concrete future generated setup. The project kind is the broader architecture category.
162
+ Workspace kinds describe deployment intent: `ui` is universal UI output, `api` is Docker-oriented, `lambda` is the AWS API runtime, `fargate` is an AWS container runtime, and `stack` contains AWS infrastructure.
136
163
 
137
164
  Examples:
138
165
 
139
- - `react-csr` -> `csr`
140
- - `react-ssr` -> `ssr`
141
- - `react-ssg` -> `ssg`
142
- - `openmfe-bff` -> `mfe`
143
- - `aws-serverless` -> `aws-serverless`
166
+ - `csr` -> `csr`
167
+ - `ssr` -> `ssr`
168
+ - `ssg` -> `ssg`
169
+ - `mfe` -> `mfe`
170
+ - `fullstack` -> `fullstack`
144
171
 
145
172
  ## Public API
146
173
 
@@ -178,7 +205,7 @@ It includes:
178
205
  - architecture: `preset`, `projectKind`
179
206
  - selected features
180
207
  - CI/CD planning: enabled state, providers, and validation pipelines
181
- - API planning for API-capable presets: REST, GraphQL, or mixed API style
208
+ - API planning for API-capable presets: REST or GraphQL API style
182
209
  - future package plans
183
210
  - future workspace plans
184
211
 
package/cli/args/args.js CHANGED
@@ -6,6 +6,8 @@ export const parseArgs = (args) => {
6
6
  return { type: 'version' };
7
7
  }
8
8
  const dryRun = args.includes('--dry-run');
9
+ const install = !args.includes('--no-install');
10
+ const verify = install && (args.includes('--verify') || (!args.includes('--no-verify') && !args.includes('--install-only')));
9
11
  const yes = args.includes('--yes') || args.includes('-y');
10
12
  const overwrite = args.includes('--overwrite');
11
13
  const skipExisting = args.includes('--skip-existing');
@@ -13,8 +15,10 @@ export const parseArgs = (args) => {
13
15
  const [command, projectName] = positionalArgs;
14
16
  const options = {
15
17
  dryRun,
18
+ install,
16
19
  yes,
17
20
  overwrite,
21
+ verify,
18
22
  skipExisting,
19
23
  };
20
24
  if (!command) {
@@ -28,6 +32,8 @@ export const parseArgs = (args) => {
28
32
  return { type: 'init', ...options };
29
33
  case 'doctor':
30
34
  return { type: 'doctor' };
35
+ case 'publish':
36
+ return { type: 'publish' };
31
37
  default:
32
38
  return { type: 'unknown', command };
33
39
  }
@@ -2,17 +2,21 @@ export type VyriyCliCommand = {
2
2
  readonly type: 'new';
3
3
  readonly projectName?: string;
4
4
  readonly dryRun: boolean;
5
+ readonly install: boolean;
5
6
  readonly yes: boolean;
6
7
  readonly overwrite: boolean;
8
+ readonly verify: boolean;
7
9
  readonly skipExisting: boolean;
8
10
  } | {
9
11
  readonly type: 'init';
10
12
  readonly dryRun: boolean;
13
+ readonly install: boolean;
11
14
  readonly yes: boolean;
12
15
  readonly overwrite: boolean;
16
+ readonly verify: boolean;
13
17
  readonly skipExisting: boolean;
14
18
  } | {
15
- readonly type: 'doctor' | 'help' | 'version';
19
+ readonly type: 'doctor' | 'help' | 'publish' | 'version';
16
20
  } | {
17
21
  readonly type: 'unknown';
18
22
  readonly command: string;
package/cli/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { runDoctorCommand } from '../commands/doctor/index.js';
2
2
  import { runInitCommand } from '../commands/init/index.js';
3
3
  import { runNewCommand } from '../commands/new/index.js';
4
+ import { runPublishCommand } from '../commands/publish/index.js';
4
5
  import packageJson from '../package.json' with { type: 'json' };
5
6
  import { parseArgs } from './args/index.js';
6
7
  const helpText = `Vyriy Project Master
@@ -10,9 +11,17 @@ Usage:
10
11
  vyriy init Initialize the current directory
11
12
  vyriy . Initialize the current directory
12
13
  vyriy doctor Check local environment
14
+ vyriy publish Prepare dist package metadata without publishing to npm
15
+ vyriy --yes, -y Use defaults where possible (empty preset)
13
16
  vyriy --dry-run Print checks and file plan without writing
14
- vyriy --help Show help
15
- vyriy --version Show version
17
+ vyriy --overwrite Overwrite existing generated paths
18
+ vyriy --skip-existing Leave existing generated paths untouched
19
+ vyriy --no-install Create files without installing dependencies
20
+ vyriy --no-verify Install dependencies without running checks
21
+ vyriy --install-only Alias for --no-verify
22
+ vyriy --verify Explicitly enable generated project checks
23
+ vyriy --help, -h Show help
24
+ vyriy --version, -v Show version
16
25
 
17
26
  Examples:
18
27
  vyriy new my-app
@@ -25,19 +34,23 @@ export const runVyriyCli = async (args = [], { output = console } = {}) => {
25
34
  case 'new':
26
35
  code = await runNewCommand({
27
36
  dryRun: command.dryRun,
37
+ install: command.install,
28
38
  output,
29
39
  overwrite: command.overwrite,
30
40
  projectName: command.projectName,
31
41
  skipExisting: command.skipExisting,
42
+ verify: command.verify,
32
43
  yes: command.yes,
33
44
  });
34
45
  break;
35
46
  case 'init':
36
47
  code = await runInitCommand({
37
48
  dryRun: command.dryRun,
49
+ install: command.install,
38
50
  output,
39
51
  overwrite: command.overwrite,
40
52
  skipExisting: command.skipExisting,
53
+ verify: command.verify,
41
54
  yes: command.yes,
42
55
  });
43
56
  break;
@@ -46,6 +59,9 @@ export const runVyriyCli = async (args = [], { output = console } = {}) => {
46
59
  code = result.code;
47
60
  break;
48
61
  }
62
+ case 'publish':
63
+ code = await runPublishCommand();
64
+ break;
49
65
  case 'help':
50
66
  output.log(helpText);
51
67
  break;
@@ -5,6 +5,8 @@ import { createFilePlan, printFilePlan, writeFilePlan } from '../../file-plan/in
5
5
  import { createProjectFiles } from '../../presets/index.js';
6
6
  import { askProjectPlan as askProjectPlanDefault } from '../../prompts/project-plan/index.js';
7
7
  import { createProjectPlanFromPreset, printProjectPlan } from '../../project-plan/index.js';
8
+ import { runCommand } from '../../shared/index.js';
9
+ const defaultYesPreset = 'empty';
8
10
  const getConflicts = (filePlan) => filePlan.filter((item) => item.status === 'conflict');
9
11
  const logConflicts = (output, conflicts, method) => {
10
12
  output[method]('\nExisting files found:\n');
@@ -28,6 +30,70 @@ const createResolvedFilePlan = async (plan, projectFiles, resolution) => createF
28
30
  overwrite: resolution === 'overwrite',
29
31
  skipExisting: resolution === 'skip',
30
32
  });
33
+ const formatCommand = (command, args) => [command, ...args].join(' ');
34
+ const printFailedPostGenerationCommand = ({ args, command, intro, output, projectDirectory, }) => {
35
+ const commandText = formatCommand(command, args);
36
+ output.error(`\n${intro}\n`);
37
+ output.error(`Failed command:\n ${commandText}\n`);
38
+ output.error(`Project directory:\n ${projectDirectory}\n`);
39
+ output.error(`You can inspect it and run manually:\n cd ${projectDirectory}\n ${commandText}`);
40
+ };
41
+ const runPostGenerationCommands = async ({ install, output, projectDirectory, verify, }) => {
42
+ if (!install) {
43
+ output.log('Installing dependencies... SKIPPED');
44
+ output.log('Running checks... SKIPPED');
45
+ output.log('\nProject files were created.');
46
+ return 0;
47
+ }
48
+ try {
49
+ await runCommand({
50
+ args: ['install'],
51
+ command: 'yarn',
52
+ cwd: projectDirectory,
53
+ });
54
+ }
55
+ catch {
56
+ printFailedPostGenerationCommand({
57
+ args: ['install'],
58
+ command: 'yarn',
59
+ intro: 'Project files were created, but dependency installation failed.',
60
+ output,
61
+ projectDirectory,
62
+ });
63
+ return 1;
64
+ }
65
+ output.log('Installing dependencies... OK');
66
+ if (!verify) {
67
+ output.log('Running checks... SKIPPED');
68
+ output.log('\nProject files were created and dependencies were installed.');
69
+ return 0;
70
+ }
71
+ try {
72
+ await runCommand({
73
+ args: ['fix'],
74
+ command: 'yarn',
75
+ cwd: projectDirectory,
76
+ });
77
+ await runCommand({
78
+ args: ['check'],
79
+ command: 'yarn',
80
+ cwd: projectDirectory,
81
+ });
82
+ }
83
+ catch {
84
+ printFailedPostGenerationCommand({
85
+ args: ['check'],
86
+ command: 'yarn',
87
+ intro: 'Project files were created and dependencies were installed, but verification failed.',
88
+ output,
89
+ projectDirectory,
90
+ });
91
+ return 1;
92
+ }
93
+ output.log('Running checks... OK');
94
+ output.log('\nProject is ready.');
95
+ return 0;
96
+ };
31
97
  const resolveInteractiveConflicts = async (plan, projectFiles, output, conflicts, askConflictResolution) => {
32
98
  logConflicts(output, conflicts, 'log');
33
99
  printConflictPrompt(output);
@@ -46,7 +112,7 @@ const resolveInteractiveConflicts = async (plan, projectFiles, output, conflicts
46
112
  export const askConflictResolutionDefault = async () => {
47
113
  const readline = createInterface({ input: stdin, output: stdout });
48
114
  try {
49
- const answer = (await readline.question('What should Vyriy do? 1. overwrite existing files, 2. skip existing files, 3. abort (abort): '))
115
+ const answer = (await readline.question('What should Vyriy do?\n\n 1. overwrite existing files,\n\n 2. skip existing files,\n\n 3. abort (abort): '))
50
116
  .trim()
51
117
  .toLowerCase();
52
118
  if (answer === '1' || answer === 'overwrite') {
@@ -61,7 +127,7 @@ export const askConflictResolutionDefault = async () => {
61
127
  readline.close();
62
128
  }
63
129
  };
64
- export const runNewCommand = async ({ askConflictResolution = askConflictResolutionDefault, askProjectPlan = askProjectPlanDefault, dryRun = false, output = console, overwrite = false, projectName = 'my-app', skipExisting = false, yes = false, } = {}) => {
130
+ export const runNewCommand = async ({ askConflictResolution = askConflictResolutionDefault, askProjectPlan = askProjectPlanDefault, dryRun = false, install = true, output = console, overwrite = false, projectName = 'my-app', skipExisting = false, verify = true, yes = false, } = {}) => {
65
131
  if (overwrite && skipExisting) {
66
132
  output.error('Cannot use --overwrite and --skip-existing together.');
67
133
  return 1;
@@ -72,13 +138,13 @@ export const runNewCommand = async ({ askConflictResolution = askConflictResolut
72
138
  output.error('\nPlease install Node.js 24+ and run the command again.');
73
139
  return 1;
74
140
  }
75
- const plan = dryRun || yes
141
+ const plan = yes
76
142
  ? createProjectPlanFromPreset({
77
143
  apiStyle: 'rest',
78
144
  ciProvider: 'none',
79
145
  description: 'Calm cloud-ready application.',
80
146
  packageScope: `@${projectName}`,
81
- preset: 'react-ssr',
147
+ preset: defaultYesPreset,
82
148
  projectName,
83
149
  targetDirectory: projectName,
84
150
  })
@@ -113,6 +179,11 @@ export const runNewCommand = async ({ askConflictResolution = askConflictResolut
113
179
  output.log(`\n${printFilePlan(resolved.filePlan)}`);
114
180
  }
115
181
  await writeFilePlan(plan.targetDirectory, filePlan);
116
- output.log('\nProject files written.');
117
- return 0;
182
+ output.log('\nCreating project files... OK');
183
+ return runPostGenerationCommands({
184
+ install,
185
+ output,
186
+ projectDirectory: plan.targetDirectory,
187
+ verify: install && verify,
188
+ });
118
189
  };
@@ -6,8 +6,10 @@ export type RunNewCommandOptions = {
6
6
  readonly askConflictResolution?: () => Promise<ConflictResolution>;
7
7
  readonly output?: Pick<typeof console, 'log' | 'error'>;
8
8
  readonly dryRun?: boolean;
9
+ readonly install?: boolean;
9
10
  readonly yes?: boolean;
10
11
  readonly overwrite?: boolean;
12
+ readonly verify?: boolean;
11
13
  readonly skipExisting?: boolean;
12
14
  };
13
15
  export type RunNewCommand = (options?: RunNewCommandOptions) => Promise<number>;
@@ -0,0 +1,2 @@
1
+ export * from './publish.js';
2
+ export type * from './types.js';
@@ -0,0 +1 @@
1
+ export * from './publish.js';
@@ -0,0 +1,2 @@
1
+ import type { RunPublishCommand } from './types.js';
2
+ export declare const runPublishCommand: RunPublishCommand;
@@ -0,0 +1,274 @@
1
+ import { chmod, copyFile, readdir, readFile, stat, unlink, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ const DIST_DIR = 'dist';
4
+ const AGENTS_FILE = 'AGENTS.md';
5
+ const LICENSE_FILE = 'LICENSE';
6
+ const PACKAGE_JSON_FILE = 'package.json';
7
+ const PACKAGES_DIR = 'packages';
8
+ const README_FILE = 'README.md';
9
+ const toPosixPath = (value) => value.split(path.sep).join('/');
10
+ const toPackagePath = (value) => {
11
+ const normalizedValue = toPosixPath(value);
12
+ return `./${normalizedValue.replace(/^\.\//, '')}`;
13
+ };
14
+ const hasFile = async (filePath) => {
15
+ try {
16
+ return (await stat(filePath)).isFile();
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ };
22
+ const readJson = async (filePath) => {
23
+ const content = await readFile(filePath, 'utf8');
24
+ return JSON.parse(content);
25
+ };
26
+ const writeJson = async (filePath, value) => {
27
+ await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
28
+ };
29
+ const isEmptyJavaScriptContent = (content) => {
30
+ const normalizedContent = content.trim();
31
+ return normalizedContent.length === 0 || normalizedContent === 'export {};';
32
+ };
33
+ const exportTargetPattern = /^\s*export(?:\s+\*|\s+\{[^}]*\})\s+from\s+['"](\..+\.js)['"];?\s*$/;
34
+ const getMissingExportTarget = async (file, line) => {
35
+ const exportTarget = exportTargetPattern.exec(line)?.[1];
36
+ if (!exportTarget) {
37
+ return undefined;
38
+ }
39
+ const exportTargetPath = path.resolve(path.dirname(file), exportTarget);
40
+ return (await hasFile(exportTargetPath)) ? undefined : exportTargetPath;
41
+ };
42
+ const readFiles = async (directory) => {
43
+ const entries = await readdir(directory, { withFileTypes: true });
44
+ const files = await Promise.all(entries.map(async (entry) => {
45
+ const entryPath = path.join(directory, entry.name);
46
+ if (entry.isDirectory()) {
47
+ return readFiles(entryPath);
48
+ }
49
+ return entry.isFile() ? [entryPath] : [];
50
+ }));
51
+ return files.flat();
52
+ };
53
+ const getPackageMain = async (packageDirectory, packageJson, javaScriptFiles) => {
54
+ if (packageJson.main?.endsWith('.js')) {
55
+ const mainPath = packageJson.main.replace(/^\.\//, '');
56
+ if (await hasFile(path.join(packageDirectory, mainPath))) {
57
+ return toPosixPath(mainPath);
58
+ }
59
+ }
60
+ if (javaScriptFiles.includes('index.js')) {
61
+ return 'index.js';
62
+ }
63
+ return javaScriptFiles[0];
64
+ };
65
+ const createExportTarget = (javaScriptFile) => {
66
+ const packagePath = toPackagePath(javaScriptFile);
67
+ return {
68
+ types: packagePath.replace(/\.js$/, '.d.ts'),
69
+ import: packagePath,
70
+ default: packagePath,
71
+ };
72
+ };
73
+ const createExports = (mainFile, javaScriptFiles) => {
74
+ const exports = {
75
+ '.': createExportTarget(mainFile),
76
+ };
77
+ for (const javaScriptFile of javaScriptFiles) {
78
+ const packagePath = toPackagePath(javaScriptFile);
79
+ const extensionlessPackagePath = packagePath.replace(/\.js$/, '');
80
+ const target = createExportTarget(javaScriptFile);
81
+ exports[extensionlessPackagePath] = target;
82
+ exports[packagePath] = target;
83
+ }
84
+ return exports;
85
+ };
86
+ const createPackageRepository = (rootPackageJson, packageDirectory) => {
87
+ const rootRepository = rootPackageJson.repository;
88
+ if (!rootRepository) {
89
+ return undefined;
90
+ }
91
+ return {
92
+ ...rootRepository,
93
+ directory: toPosixPath(path.join(PACKAGES_DIR, path.basename(packageDirectory))),
94
+ };
95
+ };
96
+ const getJavaScriptFiles = async (packageDirectory) => {
97
+ const files = await readFiles(packageDirectory);
98
+ const javaScriptFiles = [];
99
+ for (const file of files) {
100
+ const relativeFile = toPosixPath(path.relative(packageDirectory, file));
101
+ const declarationFile = path.join(packageDirectory, relativeFile.replace(/\.js$/, '.d.ts'));
102
+ if (relativeFile.endsWith('.js') && !relativeFile.endsWith('.test.js') && (await hasFile(declarationFile))) {
103
+ javaScriptFiles.push(relativeFile);
104
+ }
105
+ }
106
+ return javaScriptFiles.sort((left, right) => left.localeCompare(right));
107
+ };
108
+ const removeEmptyJavaScriptFiles = async (packageDirectory) => {
109
+ const files = await readFiles(packageDirectory);
110
+ for (const file of files) {
111
+ if (file.endsWith('.js') && isEmptyJavaScriptContent(await readFile(file, 'utf8'))) {
112
+ await unlink(file);
113
+ }
114
+ }
115
+ };
116
+ const removeMissingJavaScriptExports = async (packageDirectory) => {
117
+ const files = await readFiles(packageDirectory);
118
+ for (const file of files) {
119
+ if (!file.endsWith('.js')) {
120
+ continue;
121
+ }
122
+ const content = await readFile(file, 'utf8');
123
+ const lines = content.split('\n');
124
+ const retainedLines = [];
125
+ for (const line of lines) {
126
+ if (!(await getMissingExportTarget(file, line))) {
127
+ retainedLines.push(line);
128
+ }
129
+ }
130
+ if (retainedLines.length !== lines.length) {
131
+ await writeFile(file, retainedLines.join('\n'));
132
+ }
133
+ }
134
+ };
135
+ const copyReadme = async (packageDirectory) => {
136
+ const packageName = path.basename(packageDirectory);
137
+ const sourceReadmePath = path.join(PACKAGES_DIR, packageName, README_FILE);
138
+ if (await hasFile(sourceReadmePath)) {
139
+ await copyFile(sourceReadmePath, path.join(packageDirectory, README_FILE));
140
+ }
141
+ };
142
+ const resolveSourceAgentsPath = async (packageAgentsPath, sharedAgentsPath, rootAgentsPath) => {
143
+ if (await hasFile(packageAgentsPath)) {
144
+ return packageAgentsPath;
145
+ }
146
+ if (await hasFile(sharedAgentsPath)) {
147
+ return sharedAgentsPath;
148
+ }
149
+ return rootAgentsPath;
150
+ };
151
+ const copyAgents = async (packageDirectory, rootPackageJson) => {
152
+ const packageName = path.basename(packageDirectory);
153
+ const packageAgentsPath = path.join(PACKAGES_DIR, packageName, AGENTS_FILE);
154
+ const sharedAgentsPath = path.join(PACKAGES_DIR, AGENTS_FILE);
155
+ const rootAgentsPath = typeof rootPackageJson.agents === 'string' ? rootPackageJson.agents.replace(/^\.\//, '') : '';
156
+ const sourceAgentsPath = await resolveSourceAgentsPath(packageAgentsPath, sharedAgentsPath, rootAgentsPath);
157
+ if (await hasFile(sourceAgentsPath)) {
158
+ await copyFile(sourceAgentsPath, path.join(packageDirectory, AGENTS_FILE));
159
+ return true;
160
+ }
161
+ return false;
162
+ };
163
+ const copyLicense = async (packageDirectory) => {
164
+ if (await hasFile(LICENSE_FILE)) {
165
+ await copyFile(LICENSE_FILE, path.join(packageDirectory, LICENSE_FILE));
166
+ }
167
+ };
168
+ const getPackageBinFiles = (packageJson) => {
169
+ if (typeof packageJson.bin === 'string') {
170
+ return [packageJson.bin];
171
+ }
172
+ if (packageJson.bin && typeof packageJson.bin === 'object') {
173
+ return Object.values(packageJson.bin);
174
+ }
175
+ return [];
176
+ };
177
+ const makePackageBinsExecutable = async (packageDirectory, packageJson) => {
178
+ for (const binFile of getPackageBinFiles(packageJson)) {
179
+ const binFilePath = path.join(packageDirectory, binFile.replace(/^\.\//, ''));
180
+ if (await hasFile(binFilePath)) {
181
+ await chmod(binFilePath, 0o755);
182
+ }
183
+ }
184
+ };
185
+ const copyRootFile = async (fileName) => {
186
+ if (await hasFile(fileName)) {
187
+ await copyFile(fileName, path.join(DIST_DIR, fileName));
188
+ }
189
+ };
190
+ const publishRootPackageJson = async () => {
191
+ const packageJson = await readJson(PACKAGE_JSON_FILE);
192
+ delete packageJson.agents;
193
+ delete packageJson.dependencies;
194
+ delete packageJson.packageManager;
195
+ delete packageJson.scripts;
196
+ delete packageJson.devDependencies;
197
+ await writeJson(path.join(DIST_DIR, PACKAGE_JSON_FILE), packageJson);
198
+ };
199
+ const publishRoot = async () => {
200
+ await copyRootFile(README_FILE);
201
+ await copyRootFile(LICENSE_FILE);
202
+ await publishRootPackageJson();
203
+ };
204
+ const syncPackageRuntimeMetadata = (packageJson, rootPackageJson) => {
205
+ if (packageJson.name !== 'vyriy') {
206
+ return;
207
+ }
208
+ packageJson.packageManager = rootPackageJson.packageManager;
209
+ packageJson.engines = rootPackageJson.engines;
210
+ };
211
+ const publishPackage = async (packageJsonPath, rootPackageJson) => {
212
+ const packageDirectory = path.dirname(packageJsonPath);
213
+ const packageJson = await readJson(packageJsonPath);
214
+ await removeEmptyJavaScriptFiles(packageDirectory);
215
+ await removeMissingJavaScriptExports(packageDirectory);
216
+ await removeEmptyJavaScriptFiles(packageDirectory);
217
+ const javaScriptFiles = await getJavaScriptFiles(packageDirectory);
218
+ await copyLicense(packageDirectory);
219
+ await copyReadme(packageDirectory);
220
+ const hasAgents = await copyAgents(packageDirectory, rootPackageJson);
221
+ delete packageJson.private;
222
+ if (hasAgents && rootPackageJson.agents) {
223
+ packageJson.agents = toPackagePath(AGENTS_FILE);
224
+ }
225
+ else {
226
+ delete packageJson.agents;
227
+ }
228
+ if (rootPackageJson.license) {
229
+ packageJson.license = rootPackageJson.license;
230
+ }
231
+ else {
232
+ delete packageJson.license;
233
+ }
234
+ const repository = createPackageRepository(rootPackageJson, packageDirectory);
235
+ if (repository) {
236
+ packageJson.repository = repository;
237
+ }
238
+ else {
239
+ delete packageJson.repository;
240
+ }
241
+ syncPackageRuntimeMetadata(packageJson, rootPackageJson);
242
+ if (javaScriptFiles.length > 0) {
243
+ const mainFile = await getPackageMain(packageDirectory, packageJson, javaScriptFiles);
244
+ if (mainFile) {
245
+ packageJson.main = toPackagePath(mainFile);
246
+ packageJson.types = toPackagePath(mainFile).replace(/\.js$/, '.d.ts');
247
+ packageJson.exports = createExports(mainFile, javaScriptFiles);
248
+ }
249
+ }
250
+ await makePackageBinsExecutable(packageDirectory, packageJson);
251
+ await writeJson(packageJsonPath, packageJson);
252
+ };
253
+ export const runPublishCommand = async ({ cwd = process.cwd() } = {}) => {
254
+ const previousCwd = process.cwd();
255
+ try {
256
+ process.chdir(cwd);
257
+ const rootPackageJson = await readJson(PACKAGE_JSON_FILE);
258
+ await publishRoot();
259
+ const entries = await readdir(DIST_DIR, { withFileTypes: true });
260
+ const packageJsonPaths = entries
261
+ .filter((entry) => entry.isDirectory())
262
+ .map((entry) => path.join(DIST_DIR, entry.name, 'package.json'))
263
+ .sort((left, right) => left.localeCompare(right));
264
+ for (const packageJsonPath of packageJsonPaths) {
265
+ if (await hasFile(packageJsonPath)) {
266
+ await publishPackage(packageJsonPath, rootPackageJson);
267
+ }
268
+ }
269
+ return 0;
270
+ }
271
+ finally {
272
+ process.chdir(previousCwd);
273
+ }
274
+ };