vyriy 0.4.0 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -78,26 +78,26 @@ Prepares every package inside the `dist/` directory for npm publishing:
78
78
 
79
79
  Registered presets:
80
80
 
81
- | Key | Description |
82
- | --------- | --------------------------------------------- |
83
- | `base` | Minimal monorepo with config only |
84
- | `library` | Workspaces layout with a sample React package |
81
+ | Key | Description |
82
+ | --------- | ---------------------------------------------- |
83
+ | `base` | Minimal monorepo with config only |
84
+ | `library` | Workspaces layout with a sample React package |
85
+ | `api` | Backend API workspace with server/build setup |
86
+ | `ssr` | Server-rendered React API with CMS placeholder |
85
87
 
86
88
  Presets in progress:
87
89
 
88
90
  | Key | Direction |
89
91
  | ------ | ------------------------------ |
90
- | `api` | Backend API project |
91
92
  | `rest` | REST API project |
92
93
  | `gql` | GraphQL API project |
93
- | `ssr` | Server-side rendering project |
94
94
  | `ssg` | Static site generation project |
95
95
  | `spa` | Single-page application |
96
96
  | `mfe` | Micro-frontend project |
97
97
 
98
- Only registered presets are selectable by the wizard today. In-progress presets
99
- exist as source modules and are expected to become selectable as their generated
100
- project shape is finalized.
98
+ Registered presets are selectable by the wizard. In-progress presets exist as
99
+ source modules and are expected to become selectable as their generated project
100
+ shape is finalized.
101
101
 
102
102
  ## Providers
103
103
 
@@ -106,7 +106,9 @@ Provider selections add files to the generated project.
106
106
  | Preset | CI/CD providers | Deploy providers |
107
107
  | --------- | ------------------ | ---------------- |
108
108
  | `base` | `gitlab`, `github` | none |
109
- | `library` | `gitlab`, `github` | `docker` |
109
+ | `library` | `gitlab`, `github` | none |
110
+ | `api` | `gitlab`, `github` | none |
111
+ | `ssr` | `gitlab`, `github` | none |
110
112
 
111
113
  ## API
112
114
 
@@ -2,7 +2,7 @@ export declare const plan: (dirName: string, appPath: string) => Promise<{
2
2
  name: string;
3
3
  description: string;
4
4
  target: string;
5
- preset: "base" | "library";
5
+ preset: "ssr" | "base" | "api" | "library";
6
6
  scope: string | undefined;
7
7
  ci: import("../preset/types.js").CiProvider | undefined;
8
8
  deploy: import("../preset/types.js").DeployProvider | undefined;
@@ -1,16 +1,168 @@
1
+ import packageJson from '../../../package.json' with { type: 'json' };
1
2
  import { base } from './base.js';
2
3
  export const api = {
3
4
  files: (options) => ({
4
5
  ...base.files(options),
6
+ 'package.json': JSON.stringify({
7
+ name: options.name,
8
+ version: '0.0.0',
9
+ description: options.description,
10
+ private: true,
11
+ type: 'module',
12
+ agents: './AGENTS.md',
13
+ packageManager: packageJson.packageManager,
14
+ engines: {
15
+ node: packageJson.engines.node,
16
+ },
17
+ workspaces: [
18
+ 'workspaces/*',
19
+ ],
20
+ scripts: {
21
+ storybook: 'cross-env STORYBOOK_DISABLE_TELEMETRY=1 storybook dev -p 6006 --disable-telemetry',
22
+ check: 'run-s lint build test',
23
+ fix: "run-s 'fix:*'",
24
+ start: "run-p 'start:*'",
25
+ lint: "run-s 'lint:*'",
26
+ build: "run-s 'build:*'",
27
+ test: "run-s 'test:*'",
28
+ 'fix:prettier': 'prettier . --write',
29
+ 'fix:eslint': 'eslint . --fix',
30
+ 'start:api': 'sh workspaces/api/bin/start.sh',
31
+ 'lint:ts': 'tsc',
32
+ 'lint:prettier': 'prettier . --check',
33
+ 'lint:eslint': 'eslint .',
34
+ 'build:api': 'rimraf dist && sh workspaces/api/bin/build.sh',
35
+ 'build:storybook': 'cross-env STORYBOOK_DISABLE_TELEMETRY=1 storybook build --quiet --disable-telemetry',
36
+ 'test:jest': 'jest',
37
+ postinstall: 'husky',
38
+ },
39
+ devDependencies: {
40
+ '@vyriy/typescript-config': `^${packageJson.version}`,
41
+ typescript: packageJson.peerDependencies.typescript,
42
+ '@vyriy/prettier-config': `^${packageJson.version}`,
43
+ prettier: packageJson.peerDependencies.prettier,
44
+ '@vyriy/eslint-config': `^${packageJson.version}`,
45
+ eslint: packageJson.peerDependencies.eslint,
46
+ '@vyriy/jest-config': `^${packageJson.version}`,
47
+ jest: packageJson.peerDependencies.jest,
48
+ '@vyriy/storybook-config': `^${packageJson.version}`,
49
+ storybook: packageJson.peerDependencies.storybook,
50
+ '@vyriy/path': `^${packageJson.version}`,
51
+ vyriy: `^${packageJson.version}`,
52
+ husky: packageJson.peerDependencies.husky,
53
+ 'npm-run-all2': packageJson.peerDependencies['npm-run-all2'],
54
+ 'cross-env': packageJson.peerDependencies['cross-env'],
55
+ rimraf: packageJson.peerDependencies.rimraf,
56
+ '@vyriy/webpack-config': `^${packageJson.version}`,
57
+ '@vyriy/handler': `^${packageJson.version}`,
58
+ '@vyriy/server': `^${packageJson.version}`,
59
+ tsx: packageJson.peerDependencies.tsx,
60
+ webpack: packageJson.peerDependencies.webpack,
61
+ 'webpack-cli': packageJson.peerDependencies['webpack-cli'],
62
+ },
63
+ }, null, 2) + '\n',
64
+ 'workspaces/api/bin/build.sh': `#!/usr/bin/env sh
65
+
66
+ set -e
67
+
68
+ scriptdir="$PWD/workspaces/api";
69
+
70
+ NODE_ENV=production npx webpack --config $scriptdir/webpack.config.ts
71
+
72
+ cp $scriptdir/package.json dist/api/package.json
73
+ npm pkg delete "type" --prefix dist/api
74
+ npm pkg delete "private" --prefix dist/api
75
+ `,
76
+ 'workspaces/api/bin/start.sh': `#!/usr/bin/env sh
77
+
78
+ set -e
79
+
80
+ scriptdir="$PWD/workspaces/api";
81
+
82
+ NODE_ENV=production LOG_LEVEL=info tsx $scriptdir/index.ts
83
+ `,
84
+ 'workspaces/api/doc.mdx': `import { Meta, Markdown } from '@storybook/addon-docs/blocks';
85
+ import ReadMe from './README.md?raw';
86
+
87
+ <Meta title="Workspaces/API" />
88
+
89
+ <Markdown>{ReadMe}</Markdown>
90
+ `,
91
+ 'workspaces/api/README.md': `# ${options.name} API\n\n${options.description}\n`,
92
+ 'workspaces/api/webpack.config.ts': `import { path } from '@vyriy/path';
93
+ import { ssr, external } from '@vyriy/webpack-config';
94
+
95
+ export default ssr(
96
+ '@w/api',
97
+ {
98
+ path: path('dist', 'api'),
99
+ filename: 'index.js',
100
+ library: { type: 'commonjs2' },
101
+ },
102
+ (config) => ({
103
+ ...config,
104
+ externals: [external({ allowlist: [/^@p/, /^@w/, /^@vyriy/] })],
105
+ }),
106
+ );
107
+ `,
108
+ 'workspaces/api/package.json': JSON.stringify({
109
+ name: '@w/api',
110
+ type: 'module',
111
+ private: true,
112
+ }, null, 2) + '\n',
113
+ 'workspaces/api/index.ts': `import { server } from '@vyriy/server';
114
+ import { api } from '@vyriy/handler';
115
+
116
+ server(
117
+ api(async (event) =>
118
+ Promise.resolve({
119
+ statusCode: 200,
120
+ body: JSON.stringify({
121
+ path: event.path,
122
+ }),
123
+ }),
124
+ ),
125
+ );
126
+ `,
127
+ 'workspaces/api/index.test.ts': `import { describe, expect, it, jest } from '@jest/globals';
128
+
129
+ const apiMock = jest.fn((handler) => ({
130
+ handler,
131
+ }));
132
+ const serverMock = jest.fn();
133
+
134
+ jest.mock('@vyriy/handler', () => ({
135
+ api: apiMock,
136
+ }));
137
+
138
+ jest.mock('@vyriy/server', () => ({
139
+ server: serverMock,
140
+ }));
141
+
142
+ describe('workspaces/api/index.ts', () => {
143
+ it('starts the server with a handler that returns the request path', async () => {
144
+ await import('./index.js');
145
+
146
+ expect(apiMock).toHaveBeenCalledTimes(1);
147
+ expect(serverMock).toHaveBeenCalledTimes(1);
148
+ expect(serverMock).toHaveBeenCalledWith(apiMock.mock.results[0]?.value);
149
+
150
+ const handler = apiMock.mock.calls[0]?.[0] as (event: {
151
+ path: string;
152
+ }) => Promise<{ statusCode: number; body: string }>;
153
+
154
+ await expect(handler({ path: '/healthcheck' })).resolves.toEqual({
155
+ statusCode: 200,
156
+ body: JSON.stringify({
157
+ path: '/healthcheck',
158
+ }),
159
+ });
160
+ });
161
+ });
162
+ `,
5
163
  }),
6
164
  ci: {
7
- github: {
8
- '.gitlab-ci.yml': 'code',
9
- },
10
- },
11
- deploy: {
12
- docker: {
13
- Dockerfile: 'code',
14
- },
165
+ ...base.ci,
15
166
  },
167
+ deploy: {},
16
168
  };
@@ -53,8 +53,8 @@ export const base = {
53
53
  'npm-run-all2': packageJson.peerDependencies['npm-run-all2'],
54
54
  'cross-env': packageJson.peerDependencies['cross-env'],
55
55
  },
56
- }, null, 2),
57
- 'README.md': `# ${name}\n\n${description}`,
56
+ }, null, 2) + '\n',
57
+ 'README.md': `# ${name}\n\n${description}\n`,
58
58
  'doc.mdx': `import { Meta, Markdown } from '@storybook/addon-docs/blocks';
59
59
  import ReadMe from './README.md?raw';
60
60
 
@@ -152,9 +152,9 @@ export default {
152
152
  'workspaces/**/*.tsx',
153
153
  '*.ts',
154
154
  ],
155
- }, null, 2),
155
+ }, null, 2) + '\n',
156
156
  'prettier.config.ts': "export { default } from '@vyriy/prettier-config';\n",
157
- '.prettierignore': 'node_modules\ndist\ncoverage\nstorybook-static\n',
157
+ '.prettierignore': 'node_modules\ndist\ncoverage\nstorybook-static\nconsumer\n',
158
158
  'eslint.config.ts': "export { default } from '@vyriy/eslint-config';\n",
159
159
  'jest.config.ts': "export { default } from '@vyriy/jest-config';\n",
160
160
  }),
@@ -9,4 +9,14 @@ export declare const presets: {
9
9
  description: string;
10
10
  preset: import("./types.js").Preset;
11
11
  };
12
+ api: {
13
+ name: string;
14
+ description: string;
15
+ preset: import("./types.js").Preset;
16
+ };
17
+ ssr: {
18
+ name: string;
19
+ description: string;
20
+ preset: import("./types.js").Preset;
21
+ };
12
22
  };
@@ -1,5 +1,7 @@
1
1
  import { base } from './base.js';
2
2
  import { library } from './library.js';
3
+ import { api } from './api.js';
4
+ import { ssr } from './ssr.js';
3
5
  export const presets = {
4
6
  base: {
5
7
  name: 'Base',
@@ -11,4 +13,14 @@ export const presets = {
11
13
  description: 'Preset to generate JS/React library',
12
14
  preset: library,
13
15
  },
16
+ api: {
17
+ name: 'API',
18
+ description: 'Preset to generate simple API',
19
+ preset: api,
20
+ },
21
+ ssr: {
22
+ name: 'SSR',
23
+ description: 'Preset to generate simple Server Side Rendering (SSR) API',
24
+ preset: ssr,
25
+ },
14
26
  };
@@ -68,7 +68,7 @@ export const library = {
68
68
  stylelint: packageJson.peerDependencies.stylelint,
69
69
  rimraf: packageJson.peerDependencies.rimraf,
70
70
  },
71
- }, null, 2),
71
+ }, null, 2) + '\n',
72
72
  'tsconfig.build.json': JSON.stringify({
73
73
  extends: './tsconfig.json',
74
74
  include: [
@@ -89,7 +89,7 @@ export const library = {
89
89
  declaration: true,
90
90
  allowImportingTsExtensions: false,
91
91
  },
92
- }, null, 2),
92
+ }, null, 2) + '\n',
93
93
  'stylelint.config.ts': "export { default } from '@vyriy/stylelint-config';\n",
94
94
  [`packages/${options.name}/package.json`]: JSON.stringify({
95
95
  name: packageName,
@@ -104,7 +104,7 @@ export const library = {
104
104
  peerDependencies: {
105
105
  react: packageJson.peerDependencies.react,
106
106
  },
107
- }, null, 2),
107
+ }, null, 2) + '\n',
108
108
  [`packages/${options.name}/README.md`]: `# ${packageName}\n\n${options.description}\n`,
109
109
  [`packages/${options.name}/doc.mdx`]: `import { Meta, Markdown } from '@storybook/addon-docs/blocks';
110
110
  import ReadMe from './README.md?raw';
@@ -128,8 +128,8 @@ import type { ButtonType } from './types.js';
128
128
 
129
129
  import './button.scss';
130
130
 
131
- export const Button: ButtonType = ({ className, type = 'button', variant = 'primary', ...props }) => (
132
- <button className={cn('button', \`button--\${variant}\`, className)} type={type} {...props} />
131
+ export const Button: ButtonType = ({ className, variant = 'primary', ...props }) => (
132
+ <button type="button" className={cn('button', \`button--\${variant}\`, className)} {...props} />
133
133
  );
134
134
  `,
135
135
  [`packages/${options.name}/index.test.ts`]: `import { describe, expect, it } from '@jest/globals';
@@ -173,13 +173,38 @@ const meta = {
173
173
  args: {
174
174
  children: 'Button',
175
175
  },
176
+ argTypes: {
177
+ children: {
178
+ control: 'text',
179
+ description: 'Button label.',
180
+ table: {
181
+ type: { summary: 'ReactNode' },
182
+ },
183
+ },
184
+ variant: {
185
+ control: 'select',
186
+ options: ['primary', 'secondary'],
187
+ description: 'Visual style of the button.',
188
+ table: {
189
+ type: { summary: "'primary' | 'secondary'" },
190
+ defaultValue: { summary: 'primary' },
191
+ },
192
+ },
193
+ className: {
194
+ table: { disable: true },
195
+ },
196
+ },
176
197
  } satisfies Meta<typeof Button>;
177
198
 
178
199
  export default meta;
179
200
 
180
201
  type Story = StoryObj<typeof meta>;
181
202
 
182
- export const Primary: Story = {};
203
+ export const Primary: Story = {
204
+ args: {
205
+ variant: 'primary',
206
+ },
207
+ };
183
208
 
184
209
  export const Secondary: Story = {
185
210
  args: {
@@ -1,16 +1,325 @@
1
+ import packageJson from '../../../package.json' with { type: 'json' };
1
2
  import { base } from './base.js';
2
3
  export const ssr = {
3
4
  files: (options) => ({
4
5
  ...base.files(options),
6
+ 'package.json': JSON.stringify({
7
+ name: options.name,
8
+ version: '0.0.0',
9
+ description: options.description,
10
+ private: true,
11
+ type: 'module',
12
+ agents: './AGENTS.md',
13
+ packageManager: packageJson.packageManager,
14
+ engines: {
15
+ node: packageJson.engines.node,
16
+ },
17
+ workspaces: [
18
+ 'packages/*',
19
+ 'workspaces/*',
20
+ ],
21
+ scripts: {
22
+ storybook: 'cross-env STORYBOOK_DISABLE_TELEMETRY=1 storybook dev -p 6006 --disable-telemetry',
23
+ check: 'run-s lint build test',
24
+ fix: "run-s 'fix:*'",
25
+ start: "run-p 'start:*'",
26
+ lint: "run-s 'lint:*'",
27
+ build: "run-s 'build:*'",
28
+ test: "run-s 'test:*'",
29
+ 'fix:prettier': 'prettier . --write',
30
+ 'fix:eslint': 'eslint . --fix',
31
+ 'fix:stylelint': 'stylelint "**/*.{css,scss}" --fix',
32
+ 'start:api': 'sh workspaces/api/bin/start.sh',
33
+ 'lint:ts': 'tsc',
34
+ 'lint:prettier': 'prettier . --check',
35
+ 'lint:eslint': 'eslint .',
36
+ 'lint:stylelint': 'stylelint "**/*.{css,scss}"',
37
+ 'build:api': 'rimraf dist && sh workspaces/api/bin/build.sh',
38
+ 'build:storybook': 'cross-env STORYBOOK_DISABLE_TELEMETRY=1 storybook build --quiet --disable-telemetry',
39
+ 'test:jest': 'jest',
40
+ postinstall: 'husky',
41
+ },
42
+ devDependencies: {
43
+ '@vyriy/typescript-config': `^${packageJson.version}`,
44
+ typescript: packageJson.peerDependencies.typescript,
45
+ '@vyriy/prettier-config': `^${packageJson.version}`,
46
+ prettier: packageJson.peerDependencies.prettier,
47
+ '@vyriy/eslint-config': `^${packageJson.version}`,
48
+ eslint: packageJson.peerDependencies.eslint,
49
+ '@vyriy/jest-config': `^${packageJson.version}`,
50
+ jest: packageJson.peerDependencies.jest,
51
+ '@vyriy/storybook-config': `^${packageJson.version}`,
52
+ storybook: packageJson.peerDependencies.storybook,
53
+ '@vyriy/path': `^${packageJson.version}`,
54
+ vyriy: `^${packageJson.version}`,
55
+ husky: packageJson.peerDependencies.husky,
56
+ 'npm-run-all2': packageJson.peerDependencies['npm-run-all2'],
57
+ 'cross-env': packageJson.peerDependencies['cross-env'],
58
+ rimraf: packageJson.peerDependencies.rimraf,
59
+ '@vyriy/webpack-config': `^${packageJson.version}`,
60
+ '@vyriy/handler': `^${packageJson.version}`,
61
+ '@vyriy/server': `^${packageJson.version}`,
62
+ tsx: packageJson.peerDependencies.tsx,
63
+ webpack: packageJson.peerDependencies.webpack,
64
+ 'webpack-cli': packageJson.peerDependencies['webpack-cli'],
65
+ react: packageJson.peerDependencies.react,
66
+ 'react-dom': packageJson.peerDependencies['react-dom'],
67
+ '@types/react': packageJson.peerDependencies['@types/react'],
68
+ '@types/react-dom': packageJson.peerDependencies['@types/react-dom'],
69
+ '@vyriy/stylelint-config': `^${packageJson.version}`,
70
+ '@vyriy/cn': `^${packageJson.version}`,
71
+ '@vyriy/html': `^${packageJson.version}`,
72
+ stylelint: packageJson.peerDependencies.stylelint,
73
+ sass: packageJson.peerDependencies.sass,
74
+ },
75
+ }, null, 2) + '\n',
76
+ 'stylelint.config.ts': "export { default } from '@vyriy/stylelint-config';\n",
77
+ 'assets.d.ts': "declare module '*.scss';\n",
78
+ 'packages/components/package.json': JSON.stringify({
79
+ name: '@p/components',
80
+ private: true,
81
+ type: 'module',
82
+ }, null, 2) + '\n',
83
+ 'packages/components/index.ts': "export * from './page/index.js';\n",
84
+ 'packages/components/index.test.tsx': `import { describe, expect, it } from '@jest/globals';
85
+
86
+ import { Page } from './index.js';
87
+ import { Page as PageImplementation } from './page/index.js';
88
+
89
+ describe('packages/components/page', () => {
90
+ it('re-exports the page component', () => {
91
+ expect(Page).toBe(PageImplementation);
92
+ });
93
+ });
94
+ `,
95
+ 'packages/components/page/index.ts': `export * from './page.js';
96
+ export type * from './types.js';
97
+ `,
98
+ 'packages/components/page/index.test.ts': `import { describe, expect, it } from '@jest/globals';
99
+
100
+ import { Page } from './index.js';
101
+ import { Page as PageImplementation } from './page.js';
102
+
103
+ describe('packages/components/page', () => {
104
+ it('re-exports the page component', () => {
105
+ expect(Page).toBe(PageImplementation);
106
+ });
107
+ });
108
+ `,
109
+ 'packages/components/page/types.ts': `import { FC } from 'react';
110
+
111
+ export type PageProps = {
112
+ content: string;
113
+ };
114
+
115
+ export type PageType = FC<PageProps>;
116
+ `,
117
+ 'packages/components/page/page.tsx': `import type { PageType } from './types.js';
118
+
119
+ export const Page: PageType = ({ content }) => <div className="content">{content}</div>;
120
+ `,
121
+ 'packages/components/page/styles.scss': `.content {
122
+ display: block;
123
+ }
124
+ `,
125
+ 'packages/components/page/page.test.tsx': `import { renderToStaticMarkup } from 'react-dom/server';
126
+ import { describe, expect, it } from '@jest/globals';
127
+
128
+ import { Page } from './page.js';
129
+
130
+ describe('packages/components/page/page', () => {
131
+ it('renders content inside the page content container', () => {
132
+ expect(renderToStaticMarkup(<Page content="Page body" />)).toBe('<div class="content">Page body</div>');
133
+ });
134
+ });
135
+ `,
136
+ 'packages/services/package.json': JSON.stringify({
137
+ name: '@p/services',
138
+ private: true,
139
+ type: 'module',
140
+ }, null, 2) + '\n',
141
+ 'packages/services/cms/index.ts': `export const cms = {
142
+ getContent: async () => {
143
+ // Placeholder for fetching content from a CMS
144
+ return Promise.resolve({
145
+ title: 'Sample Content',
146
+ body: 'This is a sample content fetched from the CMS.',
147
+ });
148
+ },
149
+ };
150
+ `,
151
+ 'packages/services/cms/index.test.ts': `import { describe, expect, it } from '@jest/globals';
152
+
153
+ import { cms } from './index.js';
154
+
155
+ describe('packages/services/cms', () => {
156
+ it('returns content for rendering a page', async () => {
157
+ await expect(cms.getContent()).resolves.toEqual({
158
+ title: 'Sample Content',
159
+ body: 'This is a sample content fetched from the CMS.',
160
+ });
161
+ });
162
+ });
163
+ `,
164
+ 'workspaces/api/bin/build.sh': `#!/usr/bin/env sh
165
+
166
+ set -e
167
+
168
+ scriptdir="$PWD/workspaces/api";
169
+ distdir="$PWD/dist/api";
170
+
171
+ NODE_ENV=production npx webpack --config $scriptdir/webpack.config.ts
172
+
173
+ yarn exec sass packages/components/page/styles.scss "$distdir/styles.css" --no-source-map --style=compressed
174
+ cp $scriptdir/package.json "$distdir/package.json"
175
+ npm pkg delete "type" --prefix "$distdir"
176
+ npm pkg delete "private" --prefix "$distdir"
177
+ `,
178
+ 'workspaces/api/bin/start.sh': `#!/usr/bin/env sh
179
+
180
+ set -e
181
+
182
+ scriptdir="$PWD/workspaces/api";
183
+ distdir="$PWD/dist/api";
184
+
185
+ mkdir -p "$distdir"
186
+ yarn exec sass packages/components/page/styles.scss "$distdir/styles.css" --no-source-map
187
+
188
+ PROJECT_CWD="$distdir" NODE_ENV=production LOG_LEVEL=info "$PWD/node_modules/.bin/tsx" $scriptdir/index.tsx
189
+ `,
190
+ 'workspaces/api/doc.mdx': `import { Meta, Markdown } from '@storybook/addon-docs/blocks';
191
+ import ReadMe from './README.md?raw';
192
+
193
+ <Meta title="Workspaces/API" />
194
+
195
+ <Markdown>{ReadMe}</Markdown>
196
+ `,
197
+ 'workspaces/api/README.md': `# ${options.name} API\n\n${options.description}\n`,
198
+ 'workspaces/api/webpack.config.ts': `import { path } from '@vyriy/path';
199
+ import { ssr, external } from '@vyriy/webpack-config';
200
+
201
+ export default ssr(
202
+ '@w/api',
203
+ {
204
+ path: path('dist', 'api'),
205
+ filename: 'index.js',
206
+ library: { type: 'commonjs2' },
207
+ },
208
+ (config) => ({
209
+ ...config,
210
+ externals: [external({ allowlist: [/^@p/, /^@w/, /^@vyriy/] })],
211
+ }),
212
+ );
213
+ `,
214
+ 'workspaces/api/package.json': JSON.stringify({
215
+ name: '@w/api',
216
+ type: 'module',
217
+ private: true,
218
+ }, null, 2) + '\n',
219
+ 'workspaces/api/index.tsx': `import { readFileSync } from 'node:fs';
220
+ import { renderToString } from 'react-dom/server';
221
+
222
+ import { server } from '@vyriy/server';
223
+ import { api } from '@vyriy/handler';
224
+ import { html, minify } from '@vyriy/html';
225
+ import { path } from '@vyriy/path';
226
+
227
+ import { cms } from '@p/services/cms';
228
+ import { Page } from '@p/components';
229
+
230
+ const dashboardStyles = readFileSync(path('styles.css'), 'utf8');
231
+
232
+ server(
233
+ api(async () => {
234
+ const content = await cms.getContent();
235
+
236
+ return {
237
+ statusCode: 200,
238
+ headers: {
239
+ 'content-type': 'text/html; charset=utf-8',
240
+ },
241
+ body: minify(
242
+ html({
243
+ htmlAttributes: 'lang="en"',
244
+ title: \`<title>\${content.title}</title>\`,
245
+ meta: '<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />',
246
+ style: \`<style>\${dashboardStyles.trim()}</style>\`,
247
+ body: renderToString(<Page content={content.body} />),
248
+ }),
249
+ ),
250
+ };
251
+ }),
252
+ );
253
+ `,
254
+ 'workspaces/api/index.test.tsx': `import { describe, expect, it, jest } from '@jest/globals';
255
+
256
+ const apiMock = jest.fn((handler) => ({
257
+ handler,
258
+ }));
259
+ const getContentMock = jest.fn(() =>
260
+ Promise.resolve({
261
+ title: 'Sample Content',
262
+ body: 'This is a sample content fetched from the CMS.',
263
+ }),
264
+ );
265
+ const nodeFs = jest.requireActual<typeof import('node:fs')>('node:fs');
266
+ const readFileSyncMock = jest.fn<(path: string | URL, encoding: 'utf8') => string>(
267
+ () => '.content { display: block; }',
268
+ );
269
+ const serverMock = jest.fn();
270
+
271
+ jest.mock('node:fs', () => ({
272
+ ...nodeFs,
273
+ readFileSync: readFileSyncMock,
274
+ }));
275
+
276
+ jest.mock('@vyriy/handler', () => ({
277
+ api: apiMock,
278
+ }));
279
+
280
+ jest.mock('@vyriy/server', () => ({
281
+ server: serverMock,
282
+ }));
283
+
284
+ jest.mock('@p/services/cms', () => ({
285
+ cms: {
286
+ getContent: getContentMock,
287
+ },
288
+ }));
289
+
290
+ describe('workspaces/api/index.tsx', () => {
291
+ it('starts the server with a handler that returns rendered page HTML', async () => {
292
+ await import('./index.js');
293
+
294
+ expect(apiMock).toHaveBeenCalledTimes(1);
295
+ expect(serverMock).toHaveBeenCalledTimes(1);
296
+ expect(serverMock).toHaveBeenCalledWith(apiMock.mock.results[0]?.value);
297
+
298
+ const handler = apiMock.mock.calls[0]?.[0] as () => Promise<{
299
+ statusCode: number;
300
+ headers: Record<string, string>;
301
+ body: string;
302
+ }>;
303
+
304
+ const response = await handler();
305
+
306
+ expect(response).toEqual({
307
+ statusCode: 200,
308
+ headers: {
309
+ 'content-type': 'text/html; charset=utf-8',
310
+ },
311
+ body: expect.stringContaining('<title>Sample Content</title>'),
312
+ });
313
+ expect(response.body).toContain('<style>.content { display: block; }</style>');
314
+ expect(response.body).toContain('This is a sample content fetched from the CMS.');
315
+ expect(readFileSyncMock).toHaveBeenCalledWith(expect.stringContaining('styles.css'), 'utf8');
316
+ expect(getContentMock).toHaveBeenCalledTimes(1);
317
+ });
318
+ });
319
+ `,
5
320
  }),
6
321
  ci: {
7
- github: {
8
- '.gitlab-ci.yml': 'code',
9
- },
10
- },
11
- deploy: {
12
- docker: {
13
- Dockerfile: 'code',
14
- },
322
+ ...base.ci,
15
323
  },
324
+ deploy: {},
16
325
  };
@@ -1,2 +1,2 @@
1
1
  import { prompt } from './prompt.js';
2
- export const scope = async (question, preset, name) => preset === 'base' ? undefined : prompt(question, 'Package scope', `@${name}`);
2
+ export const scope = async (question, preset, name) => preset === 'library' ? prompt(question, 'Package scope', `@${name}`) : undefined;
package/commands/dist.js CHANGED
@@ -60,6 +60,9 @@ const getPackageMain = async (packageDirectory, packageJson, javaScriptFiles) =>
60
60
  if (javaScriptFiles.includes('index.js')) {
61
61
  return 'index.js';
62
62
  }
63
+ if (getPackageBinFiles(packageJson).length > 0) {
64
+ return undefined;
65
+ }
63
66
  return javaScriptFiles[0];
64
67
  };
65
68
  const createExportTarget = (javaScriptFile) => {
@@ -174,9 +177,18 @@ const getPackageBinFiles = (packageJson) => {
174
177
  }
175
178
  return [];
176
179
  };
180
+ const toPackageLocalPath = (filePath) => filePath.replace(/^\.\//, '');
181
+ const removePackageBinDeclarationFiles = async (packageDirectory, packageJson) => {
182
+ for (const binFile of getPackageBinFiles(packageJson)) {
183
+ const declarationFilePath = path.join(packageDirectory, toPackageLocalPath(binFile).replace(/\.js$/, '.d.ts'));
184
+ if (await hasFile(declarationFilePath)) {
185
+ await unlink(declarationFilePath);
186
+ }
187
+ }
188
+ };
177
189
  const makePackageBinsExecutable = async (packageDirectory, packageJson) => {
178
190
  for (const binFile of getPackageBinFiles(packageJson)) {
179
- const binFilePath = path.join(packageDirectory, binFile.replace(/^\.\//, ''));
191
+ const binFilePath = path.join(packageDirectory, toPackageLocalPath(binFile));
180
192
  if (await hasFile(binFilePath)) {
181
193
  await chmod(binFilePath, 0o755);
182
194
  }
@@ -214,6 +226,7 @@ const distPackage = async (packageJsonPath, rootPackageJson) => {
214
226
  await removeEmptyJavaScriptFiles(packageDirectory);
215
227
  await removeMissingJavaScriptExports(packageDirectory);
216
228
  await removeEmptyJavaScriptFiles(packageDirectory);
229
+ await removePackageBinDeclarationFiles(packageDirectory, packageJson);
217
230
  const javaScriptFiles = await getJavaScriptFiles(packageDirectory);
218
231
  await copyLicense(packageDirectory);
219
232
  await copyReadme(packageDirectory);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vyriy",
3
- "version": "0.4.0",
3
+ "version": "0.4.3",
4
4
  "description": "Interactive project master for calm cloud-ready applications.",
5
5
  "type": "module",
6
6
  "bin": "./bin/vyriy.js",
@@ -20,9 +20,13 @@
20
20
  "react": "^19.2.6",
21
21
  "react-dom": "^19.2.6",
22
22
  "rimraf": "^6.1.3",
23
+ "sass": "^1.100.0",
23
24
  "storybook": "^10.4.1",
24
25
  "stylelint": "^17.12.0",
25
- "typescript": "^6.0.3"
26
+ "tsx": "^4.22.3",
27
+ "typescript": "^6.0.3",
28
+ "webpack": "^5.107.2",
29
+ "webpack-cli": "^7.0.2"
26
30
  },
27
31
  "peerDependenciesMeta": {
28
32
  "@storybook/react-webpack5": {
@@ -61,14 +65,26 @@
61
65
  "rimraf": {
62
66
  "optional": true
63
67
  },
68
+ "sass": {
69
+ "optional": true
70
+ },
64
71
  "storybook": {
65
72
  "optional": true
66
73
  },
67
74
  "stylelint": {
68
75
  "optional": true
69
76
  },
77
+ "tsx": {
78
+ "optional": true
79
+ },
70
80
  "typescript": {
71
81
  "optional": true
82
+ },
83
+ "webpack": {
84
+ "optional": true
85
+ },
86
+ "webpack-cli": {
87
+ "optional": true
72
88
  }
73
89
  },
74
90
  "agents": "./AGENTS.md",
@@ -77,314 +93,5 @@
77
93
  "type": "git",
78
94
  "url": "https://github.com/evheniy/vyriy",
79
95
  "directory": "packages/vyriy"
80
- },
81
- "main": "./bin/vyriy.js",
82
- "types": "./bin/vyriy.d.ts",
83
- "exports": {
84
- ".": {
85
- "types": "./bin/vyriy.d.ts",
86
- "import": "./bin/vyriy.js",
87
- "default": "./bin/vyriy.js"
88
- },
89
- "./bin/vyriy": {
90
- "types": "./bin/vyriy.d.ts",
91
- "import": "./bin/vyriy.js",
92
- "default": "./bin/vyriy.js"
93
- },
94
- "./bin/vyriy.js": {
95
- "types": "./bin/vyriy.d.ts",
96
- "import": "./bin/vyriy.js",
97
- "default": "./bin/vyriy.js"
98
- },
99
- "./cli/args": {
100
- "types": "./cli/args.d.ts",
101
- "import": "./cli/args.js",
102
- "default": "./cli/args.js"
103
- },
104
- "./cli/args.js": {
105
- "types": "./cli/args.d.ts",
106
- "import": "./cli/args.js",
107
- "default": "./cli/args.js"
108
- },
109
- "./cli/cli": {
110
- "types": "./cli/cli.d.ts",
111
- "import": "./cli/cli.js",
112
- "default": "./cli/cli.js"
113
- },
114
- "./cli/cli.js": {
115
- "types": "./cli/cli.d.ts",
116
- "import": "./cli/cli.js",
117
- "default": "./cli/cli.js"
118
- },
119
- "./cli/index": {
120
- "types": "./cli/index.d.ts",
121
- "import": "./cli/index.js",
122
- "default": "./cli/index.js"
123
- },
124
- "./cli/index.js": {
125
- "types": "./cli/index.d.ts",
126
- "import": "./cli/index.js",
127
- "default": "./cli/index.js"
128
- },
129
- "./commands/check-env": {
130
- "types": "./commands/check-env.d.ts",
131
- "import": "./commands/check-env.js",
132
- "default": "./commands/check-env.js"
133
- },
134
- "./commands/check-env.js": {
135
- "types": "./commands/check-env.d.ts",
136
- "import": "./commands/check-env.js",
137
- "default": "./commands/check-env.js"
138
- },
139
- "./commands/create/index": {
140
- "types": "./commands/create/index.d.ts",
141
- "import": "./commands/create/index.js",
142
- "default": "./commands/create/index.js"
143
- },
144
- "./commands/create/index.js": {
145
- "types": "./commands/create/index.d.ts",
146
- "import": "./commands/create/index.js",
147
- "default": "./commands/create/index.js"
148
- },
149
- "./commands/create/plan/index": {
150
- "types": "./commands/create/plan/index.d.ts",
151
- "import": "./commands/create/plan/index.js",
152
- "default": "./commands/create/plan/index.js"
153
- },
154
- "./commands/create/plan/index.js": {
155
- "types": "./commands/create/plan/index.d.ts",
156
- "import": "./commands/create/plan/index.js",
157
- "default": "./commands/create/plan/index.js"
158
- },
159
- "./commands/create/plan/plan": {
160
- "types": "./commands/create/plan/plan.d.ts",
161
- "import": "./commands/create/plan/plan.js",
162
- "default": "./commands/create/plan/plan.js"
163
- },
164
- "./commands/create/plan/plan.js": {
165
- "types": "./commands/create/plan/plan.d.ts",
166
- "import": "./commands/create/plan/plan.js",
167
- "default": "./commands/create/plan/plan.js"
168
- },
169
- "./commands/create/plan/question": {
170
- "types": "./commands/create/plan/question.d.ts",
171
- "import": "./commands/create/plan/question.js",
172
- "default": "./commands/create/plan/question.js"
173
- },
174
- "./commands/create/plan/question.js": {
175
- "types": "./commands/create/plan/question.d.ts",
176
- "import": "./commands/create/plan/question.js",
177
- "default": "./commands/create/plan/question.js"
178
- },
179
- "./commands/create/preset/api": {
180
- "types": "./commands/create/preset/api.d.ts",
181
- "import": "./commands/create/preset/api.js",
182
- "default": "./commands/create/preset/api.js"
183
- },
184
- "./commands/create/preset/api.js": {
185
- "types": "./commands/create/preset/api.d.ts",
186
- "import": "./commands/create/preset/api.js",
187
- "default": "./commands/create/preset/api.js"
188
- },
189
- "./commands/create/preset/base": {
190
- "types": "./commands/create/preset/base.d.ts",
191
- "import": "./commands/create/preset/base.js",
192
- "default": "./commands/create/preset/base.js"
193
- },
194
- "./commands/create/preset/base.js": {
195
- "types": "./commands/create/preset/base.d.ts",
196
- "import": "./commands/create/preset/base.js",
197
- "default": "./commands/create/preset/base.js"
198
- },
199
- "./commands/create/preset/gql": {
200
- "types": "./commands/create/preset/gql.d.ts",
201
- "import": "./commands/create/preset/gql.js",
202
- "default": "./commands/create/preset/gql.js"
203
- },
204
- "./commands/create/preset/gql.js": {
205
- "types": "./commands/create/preset/gql.d.ts",
206
- "import": "./commands/create/preset/gql.js",
207
- "default": "./commands/create/preset/gql.js"
208
- },
209
- "./commands/create/preset/index": {
210
- "types": "./commands/create/preset/index.d.ts",
211
- "import": "./commands/create/preset/index.js",
212
- "default": "./commands/create/preset/index.js"
213
- },
214
- "./commands/create/preset/index.js": {
215
- "types": "./commands/create/preset/index.d.ts",
216
- "import": "./commands/create/preset/index.js",
217
- "default": "./commands/create/preset/index.js"
218
- },
219
- "./commands/create/preset/library": {
220
- "types": "./commands/create/preset/library.d.ts",
221
- "import": "./commands/create/preset/library.js",
222
- "default": "./commands/create/preset/library.js"
223
- },
224
- "./commands/create/preset/library.js": {
225
- "types": "./commands/create/preset/library.d.ts",
226
- "import": "./commands/create/preset/library.js",
227
- "default": "./commands/create/preset/library.js"
228
- },
229
- "./commands/create/preset/mfe": {
230
- "types": "./commands/create/preset/mfe.d.ts",
231
- "import": "./commands/create/preset/mfe.js",
232
- "default": "./commands/create/preset/mfe.js"
233
- },
234
- "./commands/create/preset/mfe.js": {
235
- "types": "./commands/create/preset/mfe.d.ts",
236
- "import": "./commands/create/preset/mfe.js",
237
- "default": "./commands/create/preset/mfe.js"
238
- },
239
- "./commands/create/preset/rest": {
240
- "types": "./commands/create/preset/rest.d.ts",
241
- "import": "./commands/create/preset/rest.js",
242
- "default": "./commands/create/preset/rest.js"
243
- },
244
- "./commands/create/preset/rest.js": {
245
- "types": "./commands/create/preset/rest.d.ts",
246
- "import": "./commands/create/preset/rest.js",
247
- "default": "./commands/create/preset/rest.js"
248
- },
249
- "./commands/create/preset/spa": {
250
- "types": "./commands/create/preset/spa.d.ts",
251
- "import": "./commands/create/preset/spa.js",
252
- "default": "./commands/create/preset/spa.js"
253
- },
254
- "./commands/create/preset/spa.js": {
255
- "types": "./commands/create/preset/spa.d.ts",
256
- "import": "./commands/create/preset/spa.js",
257
- "default": "./commands/create/preset/spa.js"
258
- },
259
- "./commands/create/preset/ssg": {
260
- "types": "./commands/create/preset/ssg.d.ts",
261
- "import": "./commands/create/preset/ssg.js",
262
- "default": "./commands/create/preset/ssg.js"
263
- },
264
- "./commands/create/preset/ssg.js": {
265
- "types": "./commands/create/preset/ssg.d.ts",
266
- "import": "./commands/create/preset/ssg.js",
267
- "default": "./commands/create/preset/ssg.js"
268
- },
269
- "./commands/create/preset/ssr": {
270
- "types": "./commands/create/preset/ssr.d.ts",
271
- "import": "./commands/create/preset/ssr.js",
272
- "default": "./commands/create/preset/ssr.js"
273
- },
274
- "./commands/create/preset/ssr.js": {
275
- "types": "./commands/create/preset/ssr.d.ts",
276
- "import": "./commands/create/preset/ssr.js",
277
- "default": "./commands/create/preset/ssr.js"
278
- },
279
- "./commands/create/prompt/conflict-strategy": {
280
- "types": "./commands/create/prompt/conflict-strategy.d.ts",
281
- "import": "./commands/create/prompt/conflict-strategy.js",
282
- "default": "./commands/create/prompt/conflict-strategy.js"
283
- },
284
- "./commands/create/prompt/conflict-strategy.js": {
285
- "types": "./commands/create/prompt/conflict-strategy.d.ts",
286
- "import": "./commands/create/prompt/conflict-strategy.js",
287
- "default": "./commands/create/prompt/conflict-strategy.js"
288
- },
289
- "./commands/create/prompt/index": {
290
- "types": "./commands/create/prompt/index.d.ts",
291
- "import": "./commands/create/prompt/index.js",
292
- "default": "./commands/create/prompt/index.js"
293
- },
294
- "./commands/create/prompt/index.js": {
295
- "types": "./commands/create/prompt/index.d.ts",
296
- "import": "./commands/create/prompt/index.js",
297
- "default": "./commands/create/prompt/index.js"
298
- },
299
- "./commands/create/prompt/preset": {
300
- "types": "./commands/create/prompt/preset.d.ts",
301
- "import": "./commands/create/prompt/preset.js",
302
- "default": "./commands/create/prompt/preset.js"
303
- },
304
- "./commands/create/prompt/preset.js": {
305
- "types": "./commands/create/prompt/preset.d.ts",
306
- "import": "./commands/create/prompt/preset.js",
307
- "default": "./commands/create/prompt/preset.js"
308
- },
309
- "./commands/create/prompt/prompt": {
310
- "types": "./commands/create/prompt/prompt.d.ts",
311
- "import": "./commands/create/prompt/prompt.js",
312
- "default": "./commands/create/prompt/prompt.js"
313
- },
314
- "./commands/create/prompt/prompt.js": {
315
- "types": "./commands/create/prompt/prompt.d.ts",
316
- "import": "./commands/create/prompt/prompt.js",
317
- "default": "./commands/create/prompt/prompt.js"
318
- },
319
- "./commands/create/prompt/provider": {
320
- "types": "./commands/create/prompt/provider.d.ts",
321
- "import": "./commands/create/prompt/provider.js",
322
- "default": "./commands/create/prompt/provider.js"
323
- },
324
- "./commands/create/prompt/provider.js": {
325
- "types": "./commands/create/prompt/provider.d.ts",
326
- "import": "./commands/create/prompt/provider.js",
327
- "default": "./commands/create/prompt/provider.js"
328
- },
329
- "./commands/create/prompt/resolve-option": {
330
- "types": "./commands/create/prompt/resolve-option.d.ts",
331
- "import": "./commands/create/prompt/resolve-option.js",
332
- "default": "./commands/create/prompt/resolve-option.js"
333
- },
334
- "./commands/create/prompt/resolve-option.js": {
335
- "types": "./commands/create/prompt/resolve-option.d.ts",
336
- "import": "./commands/create/prompt/resolve-option.js",
337
- "default": "./commands/create/prompt/resolve-option.js"
338
- },
339
- "./commands/create/prompt/scope": {
340
- "types": "./commands/create/prompt/scope.d.ts",
341
- "import": "./commands/create/prompt/scope.js",
342
- "default": "./commands/create/prompt/scope.js"
343
- },
344
- "./commands/create/prompt/scope.js": {
345
- "types": "./commands/create/prompt/scope.d.ts",
346
- "import": "./commands/create/prompt/scope.js",
347
- "default": "./commands/create/prompt/scope.js"
348
- },
349
- "./commands/dist": {
350
- "types": "./commands/dist.d.ts",
351
- "import": "./commands/dist.js",
352
- "default": "./commands/dist.js"
353
- },
354
- "./commands/dist.js": {
355
- "types": "./commands/dist.d.ts",
356
- "import": "./commands/dist.js",
357
- "default": "./commands/dist.js"
358
- },
359
- "./commands/help": {
360
- "types": "./commands/help.d.ts",
361
- "import": "./commands/help.js",
362
- "default": "./commands/help.js"
363
- },
364
- "./commands/help.js": {
365
- "types": "./commands/help.d.ts",
366
- "import": "./commands/help.js",
367
- "default": "./commands/help.js"
368
- },
369
- "./commands/index": {
370
- "types": "./commands/index.d.ts",
371
- "import": "./commands/index.js",
372
- "default": "./commands/index.js"
373
- },
374
- "./commands/index.js": {
375
- "types": "./commands/index.d.ts",
376
- "import": "./commands/index.js",
377
- "default": "./commands/index.js"
378
- },
379
- "./commands/version": {
380
- "types": "./commands/version.d.ts",
381
- "import": "./commands/version.js",
382
- "default": "./commands/version.js"
383
- },
384
- "./commands/version.js": {
385
- "types": "./commands/version.d.ts",
386
- "import": "./commands/version.js",
387
- "default": "./commands/version.js"
388
- }
389
96
  }
390
97
  }
package/bin/vyriy.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};