vyriy 0.4.6 → 0.4.8
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/commands/check-env.js +58 -3
- package/commands/create/index.js +13 -2
- package/commands/create/plan/plan.d.ts +1 -1
- package/commands/create/preset/api.js +0 -2
- package/commands/create/preset/gql.js +740 -6
- package/commands/create/preset/index.d.ts +5 -0
- package/commands/create/preset/index.js +6 -0
- package/commands/create/preset/rest.js +0 -2
- package/commands/create/preset/spa.js +0 -2
- package/commands/create/preset/ssg.js +0 -2
- package/commands/create/preset/ssr.js +0 -2
- package/commands/types.d.ts +4 -5
- package/package.json +6 -6
package/commands/check-env.js
CHANGED
|
@@ -2,6 +2,7 @@ import { exec as processExec } from 'node:child_process';
|
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
3
|
import packageJson from '../package.json' with { type: 'json' };
|
|
4
4
|
const exec = promisify(processExec);
|
|
5
|
+
const yarnStableHint = 'Try:\n corepack enable\n corepack prepare yarn@stable --activate';
|
|
5
6
|
const node = () => {
|
|
6
7
|
const majorVersion = Number.parseInt(process.versions.node.split('.')[0]);
|
|
7
8
|
const minimumMajorVersion = Number.parseInt(packageJson.engines.node.match(/(\d+)/)?.[0]);
|
|
@@ -18,6 +19,39 @@ const node = () => {
|
|
|
18
19
|
message: `Vyriy requires Node.js >= ${minimumMajorVersion}.\n\nCurrent version: ${process.versions.node}\n\nPlease upgrade Node.js and run the command again.`,
|
|
19
20
|
};
|
|
20
21
|
};
|
|
22
|
+
const corepack = async () => {
|
|
23
|
+
let currentVersion;
|
|
24
|
+
try {
|
|
25
|
+
const { stdout } = await exec('corepack --version');
|
|
26
|
+
currentVersion = stdout.trim();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
message: `Corepack was not found.\n\nVyriy uses Corepack to install Yarn stable.\n\nInstall a Node.js distribution that includes Corepack and run the command again.`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
ok: true,
|
|
36
|
+
message: `Corepack ${currentVersion}`,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const activateYarnStable = async () => {
|
|
40
|
+
try {
|
|
41
|
+
await exec('corepack enable');
|
|
42
|
+
await exec('corepack prepare yarn@stable --activate');
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
message: `Corepack could not activate Yarn stable.\n\n${yarnStableHint}`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
ok: true,
|
|
52
|
+
message: 'Yarn stable activated',
|
|
53
|
+
};
|
|
54
|
+
};
|
|
21
55
|
const yarn = async () => {
|
|
22
56
|
const minimumMajorVersion = Number.parseInt(packageJson.packageManager.match(/(\d+)/)?.[0]);
|
|
23
57
|
let currentVersion;
|
|
@@ -28,7 +62,7 @@ const yarn = async () => {
|
|
|
28
62
|
catch {
|
|
29
63
|
return {
|
|
30
64
|
ok: false,
|
|
31
|
-
message: `Yarn was not found.\n\nVyriy requires Yarn >= ${minimumMajorVersion}.\n\
|
|
65
|
+
message: `Yarn was not found.\n\nVyriy requires Yarn >= ${minimumMajorVersion}.\n\n${yarnStableHint}`,
|
|
32
66
|
};
|
|
33
67
|
}
|
|
34
68
|
const majorVersion = Number.parseInt(currentVersion.match(/(\d+)/)?.[0]);
|
|
@@ -40,7 +74,7 @@ const yarn = async () => {
|
|
|
40
74
|
}
|
|
41
75
|
return {
|
|
42
76
|
ok: false,
|
|
43
|
-
message: `Vyriy requires Yarn >= ${minimumMajorVersion}.\n\nCurrent version: ${currentVersion}\n\
|
|
77
|
+
message: `Vyriy requires Yarn >= ${minimumMajorVersion}.\n\nCurrent version: ${currentVersion}\n\n${yarnStableHint}`,
|
|
44
78
|
};
|
|
45
79
|
};
|
|
46
80
|
export const checkEnv = async () => {
|
|
@@ -53,7 +87,28 @@ export const checkEnv = async () => {
|
|
|
53
87
|
console.error(nodeResults.message);
|
|
54
88
|
return 1;
|
|
55
89
|
}
|
|
56
|
-
const
|
|
90
|
+
const corepackResults = await corepack();
|
|
91
|
+
if (corepackResults.ok) {
|
|
92
|
+
console.log(' ', corepackResults.message);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.error(corepackResults.message);
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
let yarnResults = await yarn();
|
|
99
|
+
if (yarnResults.ok) {
|
|
100
|
+
console.log(' ', yarnResults.message);
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
const yarnStableResults = await activateYarnStable();
|
|
104
|
+
if (yarnStableResults.ok) {
|
|
105
|
+
console.log(' ', yarnStableResults.message);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.error(yarnStableResults.message);
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
yarnResults = await yarn();
|
|
57
112
|
if (yarnResults.ok) {
|
|
58
113
|
console.log(' ', yarnResults.message);
|
|
59
114
|
}
|
package/commands/create/index.js
CHANGED
|
@@ -57,6 +57,18 @@ const writeFiles = (target, files, overwrite) => {
|
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
59
|
};
|
|
60
|
+
const verifyProject = async (target) => {
|
|
61
|
+
console.log('Running checks...');
|
|
62
|
+
try {
|
|
63
|
+
await exec(`yarn --cwd ${target} check`);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
console.log('Running fixes...');
|
|
67
|
+
await exec(`yarn --cwd ${target} fix`);
|
|
68
|
+
console.log('Running checks...');
|
|
69
|
+
await exec(`yarn --cwd ${target} check`);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
60
72
|
export const create = async (options) => {
|
|
61
73
|
const { directory, dryRun, overwrite, skipExisting, install, verify } = options;
|
|
62
74
|
const checkEnvCode = await checkEnv();
|
|
@@ -107,8 +119,7 @@ export const create = async (options) => {
|
|
|
107
119
|
return 0;
|
|
108
120
|
}
|
|
109
121
|
if (verify) {
|
|
110
|
-
|
|
111
|
-
await exec(`yarn --cwd ${target} check`);
|
|
122
|
+
await verifyProject(target);
|
|
112
123
|
}
|
|
113
124
|
else {
|
|
114
125
|
console.log('Running checks... SKIPPED');
|
|
@@ -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: "ssr" | "base" | "rest" | "api" | "library" | "ssg" | "spa";
|
|
5
|
+
preset: "ssr" | "base" | "rest" | "api" | "library" | "gql" | "ssg" | "spa";
|
|
6
6
|
scope: string | undefined;
|
|
7
7
|
ci: import("../preset/types.js").CiProvider | undefined;
|
|
8
8
|
deploy: import("../preset/types.js").DeployProvider | undefined;
|
|
@@ -48,7 +48,6 @@ export const api = {
|
|
|
48
48
|
'@vyriy/storybook-config': `^${packageJson.version}`,
|
|
49
49
|
storybook: packageJson.peerDependencies.storybook,
|
|
50
50
|
'@vyriy/path': `^${packageJson.version}`,
|
|
51
|
-
vyriy: `^${packageJson.version}`,
|
|
52
51
|
husky: packageJson.peerDependencies.husky,
|
|
53
52
|
'npm-run-all2': packageJson.peerDependencies['npm-run-all2'],
|
|
54
53
|
'cross-env': packageJson.peerDependencies['cross-env'],
|
|
@@ -57,7 +56,6 @@ export const api = {
|
|
|
57
56
|
'@vyriy/handler': `^${packageJson.version}`,
|
|
58
57
|
'@vyriy/server': `^${packageJson.version}`,
|
|
59
58
|
tsx: packageJson.peerDependencies.tsx,
|
|
60
|
-
webpack: packageJson.peerDependencies.webpack,
|
|
61
59
|
'webpack-cli': packageJson.peerDependencies['webpack-cli'],
|
|
62
60
|
},
|
|
63
61
|
}, null, 2) + '\n',
|
|
@@ -1,16 +1,750 @@
|
|
|
1
|
+
import packageJson from '../../../package.json' with { type: 'json' };
|
|
1
2
|
import { base } from './base.js';
|
|
2
3
|
export const gql = {
|
|
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
|
+
'start:graphql': 'sh workspaces/graphql/bin/start.sh',
|
|
32
|
+
'lint:ts': 'tsc',
|
|
33
|
+
'lint:prettier': 'prettier . --check',
|
|
34
|
+
'lint:eslint': 'eslint .',
|
|
35
|
+
'build:graphql': 'rimraf dist && sh workspaces/graphql/bin/build.sh',
|
|
36
|
+
'build:storybook': 'cross-env STORYBOOK_DISABLE_TELEMETRY=1 storybook build --quiet --disable-telemetry',
|
|
37
|
+
'test:jest': 'jest',
|
|
38
|
+
postinstall: 'husky',
|
|
39
|
+
},
|
|
40
|
+
dependencies: {
|
|
41
|
+
'@vyriy/typescript-config': `^${packageJson.version}`,
|
|
42
|
+
typescript: packageJson.peerDependencies.typescript,
|
|
43
|
+
'@vyriy/prettier-config': `^${packageJson.version}`,
|
|
44
|
+
prettier: packageJson.peerDependencies.prettier,
|
|
45
|
+
'@vyriy/eslint-config': `^${packageJson.version}`,
|
|
46
|
+
eslint: packageJson.peerDependencies.eslint,
|
|
47
|
+
'@vyriy/jest-config': `^${packageJson.version}`,
|
|
48
|
+
jest: packageJson.peerDependencies.jest,
|
|
49
|
+
'@vyriy/storybook-config': `^${packageJson.version}`,
|
|
50
|
+
storybook: packageJson.peerDependencies.storybook,
|
|
51
|
+
'@vyriy/path': `^${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
|
+
'@vyriy/router': `^${packageJson.version}`,
|
|
60
|
+
tsx: packageJson.peerDependencies.tsx,
|
|
61
|
+
'webpack-cli': packageJson.peerDependencies['webpack-cli'],
|
|
62
|
+
graphql: packageJson.peerDependencies.graphql,
|
|
63
|
+
'@vyriy/html': `^${packageJson.version}`,
|
|
64
|
+
},
|
|
65
|
+
}, null, 2) + '\n',
|
|
66
|
+
'packages/graphql/doc.mdx': `import { Meta, Markdown } from '@storybook/addon-docs/blocks';
|
|
67
|
+
import ReadMe from './README.md?raw';
|
|
68
|
+
|
|
69
|
+
<Meta title="Packages/GraphQL" />
|
|
70
|
+
|
|
71
|
+
<Markdown>{ReadMe}</Markdown>
|
|
72
|
+
`,
|
|
73
|
+
'packages/graphql/graphiql.test.ts': `import { describe, expect, it } from '@jest/globals';
|
|
74
|
+
|
|
75
|
+
import { graphiql } from './graphiql.js';
|
|
76
|
+
|
|
77
|
+
describe('packages/graphql/graphiql.ts', () => {
|
|
78
|
+
it('returns the embedded GraphiQL HTML page', () => {
|
|
79
|
+
const page = graphiql();
|
|
80
|
+
|
|
81
|
+
expect(page).toContain('<html lang="en">');
|
|
82
|
+
expect(page).toContain('<title>GraphiQL</title>');
|
|
83
|
+
expect(page).toContain('<meta charset="UTF-8"');
|
|
84
|
+
expect(page).toContain('<div id="graphiql"><div class="loading">Loading');
|
|
85
|
+
expect(page).toContain('https://esm.sh/graphiql@5.2.2/dist/style.css');
|
|
86
|
+
expect(page).toContain('https://esm.sh/@graphiql/plugin-explorer@5.1.1/dist/style.css');
|
|
87
|
+
expect(page).toContain('<script type="importmap">');
|
|
88
|
+
expect(page).toContain('"graphiql": "https://esm.sh/graphiql@5.2.2?standalone');
|
|
89
|
+
expect(page).toContain('const fetcher = createGraphiQLFetcher({');
|
|
90
|
+
expect(page).toContain("url: 'http://localhost:3000'");
|
|
91
|
+
expect(page).toContain('root.render(React.createElement(App));');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
`,
|
|
95
|
+
'packages/graphql/graphiql.ts': `import { html, minify } from '@vyriy/html';
|
|
96
|
+
|
|
97
|
+
export const graphiql = () =>
|
|
98
|
+
minify(
|
|
99
|
+
html({
|
|
100
|
+
htmlAttributes: 'lang="en"',
|
|
101
|
+
title: '<title>GraphiQL</title>',
|
|
102
|
+
meta: '<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />',
|
|
103
|
+
link: [
|
|
104
|
+
\`<link
|
|
105
|
+
rel="stylesheet"
|
|
106
|
+
href="https://esm.sh/graphiql@5.2.2/dist/style.css"
|
|
107
|
+
integrity="sha384-f6GHLfCwoa4MFYUMd3rieGOsIVAte/evKbJhMigNdzUf52U9bV2JQBMQLke0ua+2"
|
|
108
|
+
crossorigin="anonymous"
|
|
109
|
+
/>\`,
|
|
110
|
+
\`<link
|
|
111
|
+
rel="stylesheet"
|
|
112
|
+
href="https://esm.sh/@graphiql/plugin-explorer@5.1.1/dist/style.css"
|
|
113
|
+
integrity="sha384-vTFGj0krVqwFXLB7kq/VHR0/j2+cCT/B63rge2mULaqnib2OX7DVLUVksTlqvMab"
|
|
114
|
+
crossorigin="anonymous"
|
|
115
|
+
/>\`,
|
|
116
|
+
].join(''),
|
|
117
|
+
style: \`<style>
|
|
118
|
+
body {
|
|
119
|
+
margin: 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#graphiql {
|
|
123
|
+
height: 100dvh;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.loading {
|
|
127
|
+
height: 100%;
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
justify-content: center;
|
|
131
|
+
font-size: 4rem;
|
|
132
|
+
}
|
|
133
|
+
</style>\`,
|
|
134
|
+
script: [
|
|
135
|
+
\`<script type="importmap">
|
|
136
|
+
{
|
|
137
|
+
"imports": {
|
|
138
|
+
"react": "https://esm.sh/react@19.2.5",
|
|
139
|
+
"react/": "https://esm.sh/react@19.2.5/",
|
|
140
|
+
"react-dom": "https://esm.sh/react-dom@19.2.5",
|
|
141
|
+
"react-dom/": "https://esm.sh/react-dom@19.2.5/",
|
|
142
|
+
"graphiql": "https://esm.sh/graphiql@5.2.2?standalone&external=react,react-dom,@graphiql/react,graphql",
|
|
143
|
+
"graphiql/": "https://esm.sh/graphiql@5.2.2/",
|
|
144
|
+
"@graphiql/plugin-explorer": "https://esm.sh/@graphiql/plugin-explorer@5.1.1?standalone&external=react,@graphiql/react,graphql",
|
|
145
|
+
"@graphiql/react": "https://esm.sh/@graphiql/react@0.37.3?standalone&external=react,react-dom,graphql,@graphiql/toolkit,@emotion/is-prop-valid",
|
|
146
|
+
"@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit@0.11.3?standalone&external=graphql",
|
|
147
|
+
"graphql": "https://esm.sh/graphql@16.13.2",
|
|
148
|
+
"@emotion/is-prop-valid": "data:text/javascript,"
|
|
149
|
+
},
|
|
150
|
+
"integrity": {
|
|
151
|
+
"https://esm.sh/react@19.2.5": "sha384-ZNmUQ9QQgyl95nnD/FJTBQn2ZEPTbWtMuWCXTKWNuF6Si7nC+6bvSgk5LWu+ELHn",
|
|
152
|
+
"https://esm.sh/react-dom@19.2.5": "sha384-qtNxBzn9gBs3CmJItMuvIVyjW3VIU0/rzGhCm9MippVU1BpR/c4VgaFYDIg/FrY2",
|
|
153
|
+
"https://esm.sh/graphiql@5.2.2": "sha384-MBVZMq1pmz8DwpwIWPWLk2tmS6tGiSi6WwbXvy9NhuDYASAAWd2m96xbxLqszig9",
|
|
154
|
+
"https://esm.sh/graphiql@5.2.2?standalone&external=react,react-dom,@graphiql/react,graphql": "sha384-SzHBEbcQfhvmwqh5Vtat9k7b/kIzmdVO3KMzQiAYwcxCA9x7vZwFRUgjzN1AeV3q",
|
|
155
|
+
"https://esm.sh/@graphiql/plugin-explorer@5.1.1": "sha384-83REbLb9KtIhL/6J1n91SLoP0648KOKZLIDdHRx/a0E7T3ajq6PzKz+815SCfN52",
|
|
156
|
+
"https://esm.sh/@graphiql/react@0.37.3?standalone&external=react,react-dom,graphql,@graphiql/toolkit,@emotion/is-prop-valid": "sha384-iZsbTy9B0VcX2BOTdqMuX0uJ9Hff5GbG2QeOt4OeMp0GHza76dwQaYQYNYkZkIVq",
|
|
157
|
+
"https://esm.sh/@graphiql/toolkit@0.11.3?standalone&external=graphql": "sha384-ZsnupyYmzpNjF1Z/81zwi4nV352n4P7vm0JOFKiYnAwVGOf9twnEMnnxmxabMBXe",
|
|
158
|
+
"https://esm.sh/graphql@16.13.2": "sha384-TQg9alwG3P9fzBErDW011vKuyTnrwpBZsl3SdMAh6DwBcv9ezFOl0djGI/68VOyy"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
</script>\`,
|
|
162
|
+
\`<script type="module">
|
|
163
|
+
import React from 'react';
|
|
164
|
+
import ReactDOM from 'react-dom/client';
|
|
165
|
+
import { GraphiQL, HISTORY_PLUGIN } from 'graphiql';
|
|
166
|
+
import { createGraphiQLFetcher } from '@graphiql/toolkit';
|
|
167
|
+
import { explorerPlugin } from '@graphiql/plugin-explorer';
|
|
168
|
+
import 'graphiql/setup-workers/esm.sh';
|
|
169
|
+
|
|
170
|
+
const fetcher = createGraphiQLFetcher({
|
|
171
|
+
url: 'http://localhost:3000',
|
|
172
|
+
});
|
|
173
|
+
const plugins = [HISTORY_PLUGIN, explorerPlugin()];
|
|
174
|
+
|
|
175
|
+
function App() {
|
|
176
|
+
return React.createElement(GraphiQL, {
|
|
177
|
+
fetcher,
|
|
178
|
+
plugins,
|
|
179
|
+
defaultEditorToolsVisibility: true,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const container = document.getElementById('graphiql');
|
|
184
|
+
const root = ReactDOM.createRoot(container);
|
|
185
|
+
root.render(React.createElement(App));
|
|
186
|
+
</script>\`,
|
|
187
|
+
].join(''),
|
|
188
|
+
body: '<div id="graphiql"><div class="loading">Loading…</div></div>',
|
|
5
189
|
}),
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
190
|
+
);
|
|
191
|
+
`,
|
|
192
|
+
'packages/graphql/index.test.ts': `import { describe, expect, it } from '@jest/globals';
|
|
193
|
+
|
|
194
|
+
import { router } from './router.js';
|
|
195
|
+
|
|
196
|
+
describe('packages/graphql/index.ts', () => {
|
|
197
|
+
it('re-exports the GraphQL router', async () => {
|
|
198
|
+
await expect(import('./index.js')).resolves.toMatchObject({
|
|
199
|
+
router,
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
`,
|
|
204
|
+
'packages/graphql/index.ts': "export * from './router.js';\n",
|
|
205
|
+
'packages/graphql/package.json': `{
|
|
206
|
+
"name": "@p/graphql",
|
|
207
|
+
"type": "module",
|
|
208
|
+
"private": true
|
|
209
|
+
}
|
|
210
|
+
`,
|
|
211
|
+
'packages/graphql/README.md': `# @p/graphql
|
|
212
|
+
|
|
213
|
+
Reusable GraphQL API package for the application.
|
|
214
|
+
|
|
215
|
+
## Exports
|
|
216
|
+
|
|
217
|
+
- \`router\` - an \`@vyriy/router\` instance that serves the GraphiQL page and GraphQL HTTP requests.
|
|
218
|
+
|
|
219
|
+
## Routes
|
|
220
|
+
|
|
221
|
+
### \`GET /\`
|
|
222
|
+
|
|
223
|
+
Returns an embedded GraphiQL page for exploring the schema in a browser.
|
|
224
|
+
|
|
225
|
+
### \`POST /\`
|
|
226
|
+
|
|
227
|
+
The router expects a JSON body with a GraphQL request:
|
|
228
|
+
|
|
229
|
+
\`\`\`json
|
|
230
|
+
{
|
|
231
|
+
"query": "{ hello }",
|
|
232
|
+
"variables": {},
|
|
233
|
+
"operationName": "Hello"
|
|
234
|
+
}
|
|
235
|
+
\`\`\`
|
|
236
|
+
|
|
237
|
+
Invalid JSON or an empty body returns \`400\` with \`Invalid JSON body\`. A body without \`query\` returns \`400\` with \`Missing GraphQL query\`.
|
|
238
|
+
|
|
239
|
+
## Schema
|
|
240
|
+
|
|
241
|
+
The package schema currently supports:
|
|
242
|
+
|
|
243
|
+
- \`hello: String\`
|
|
244
|
+
- \`test: Test\`
|
|
245
|
+
- \`ping(message: String): String\`
|
|
246
|
+
|
|
247
|
+
Example mutation:
|
|
248
|
+
|
|
249
|
+
\`\`\`graphql
|
|
250
|
+
mutation Ping($message: String) {
|
|
251
|
+
ping(message: $message)
|
|
252
|
+
}
|
|
253
|
+
\`\`\`
|
|
254
|
+
`,
|
|
255
|
+
'packages/graphql/router.test.ts': `import { describe, expect, it } from '@jest/globals';
|
|
256
|
+
import type { APIGatewayProxyEvent, APIGatewayProxyResult } from '@vyriy/router';
|
|
257
|
+
|
|
258
|
+
import { router } from './router.js';
|
|
259
|
+
|
|
260
|
+
type GraphQLBody = {
|
|
261
|
+
data?: Record<string, unknown> | null;
|
|
262
|
+
errors?: Array<{
|
|
263
|
+
message: string;
|
|
264
|
+
}>;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const getPostEvent = (
|
|
268
|
+
body: string | null,
|
|
269
|
+
headers: APIGatewayProxyEvent['headers'] | null = {
|
|
270
|
+
authorization: 'Bearer token',
|
|
271
|
+
},
|
|
272
|
+
): APIGatewayProxyEvent =>
|
|
273
|
+
({
|
|
274
|
+
body,
|
|
275
|
+
headers,
|
|
276
|
+
httpMethod: 'POST',
|
|
277
|
+
path: '/',
|
|
278
|
+
pathParameters: null,
|
|
279
|
+
queryStringParameters: null,
|
|
280
|
+
}) as unknown as APIGatewayProxyEvent;
|
|
281
|
+
|
|
282
|
+
const getEvent = (path = '/'): APIGatewayProxyEvent =>
|
|
283
|
+
({
|
|
284
|
+
body: null,
|
|
285
|
+
headers: {},
|
|
286
|
+
httpMethod: 'GET',
|
|
287
|
+
path,
|
|
288
|
+
pathParameters: null,
|
|
289
|
+
queryStringParameters: null,
|
|
290
|
+
}) as unknown as APIGatewayProxyEvent;
|
|
291
|
+
|
|
292
|
+
const parseBody = <Value>(response: APIGatewayProxyResult): Value => JSON.parse(response.body) as Value;
|
|
293
|
+
|
|
294
|
+
describe('packages/graphql/router.ts', () => {
|
|
295
|
+
it('serves the embedded GraphiQL page', async () => {
|
|
296
|
+
const response = await router.route(getEvent());
|
|
297
|
+
|
|
298
|
+
expect(response.statusCode).toBe(200);
|
|
299
|
+
expect(response.headers).toEqual({
|
|
300
|
+
'Content-Type': 'text/html; charset=UTF-8',
|
|
301
|
+
});
|
|
302
|
+
expect(response.body).toContain('<title>GraphiQL</title>');
|
|
303
|
+
expect(response.body).toContain('<div id="graphiql">');
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('executes GraphQL requests', async () => {
|
|
307
|
+
const response = await router.route(
|
|
308
|
+
getPostEvent(
|
|
309
|
+
JSON.stringify({
|
|
310
|
+
query: 'query Hello { hello }',
|
|
311
|
+
operationName: 'Hello',
|
|
312
|
+
}),
|
|
313
|
+
),
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
expect(response.statusCode).toBe(200);
|
|
317
|
+
expect(parseBody<GraphQLBody>(response)).toEqual({
|
|
318
|
+
data: {
|
|
319
|
+
hello: 'Hello from GraphQL',
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('passes variables to GraphQL execution', async () => {
|
|
325
|
+
const response = await router.route(
|
|
326
|
+
getPostEvent(
|
|
327
|
+
JSON.stringify({
|
|
328
|
+
query: 'mutation Ping($message: String) { ping(message: $message) }',
|
|
329
|
+
variables: {
|
|
330
|
+
message: 'from router',
|
|
331
|
+
},
|
|
332
|
+
}),
|
|
333
|
+
),
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
expect(response.statusCode).toBe(200);
|
|
337
|
+
expect(parseBody<GraphQLBody>(response)).toEqual({
|
|
338
|
+
data: {
|
|
339
|
+
ping: 'pong: from router',
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('executes GraphQL requests without request headers', async () => {
|
|
345
|
+
const response = await router.route(
|
|
346
|
+
getPostEvent(
|
|
347
|
+
JSON.stringify({
|
|
348
|
+
query: '{ test { ok message } }',
|
|
349
|
+
}),
|
|
350
|
+
null,
|
|
351
|
+
),
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
expect(response.statusCode).toBe(200);
|
|
355
|
+
expect(parseBody<GraphQLBody>(response)).toEqual({
|
|
356
|
+
data: {
|
|
357
|
+
test: {
|
|
358
|
+
ok: true,
|
|
359
|
+
message: 'GraphQL works',
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('rejects missing bodies', async () => {
|
|
366
|
+
await expect(router.route(getPostEvent(null))).resolves.toEqual({
|
|
367
|
+
body: JSON.stringify({
|
|
368
|
+
errors: [{ message: 'Invalid JSON body' }],
|
|
369
|
+
}),
|
|
370
|
+
statusCode: 400,
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('rejects invalid JSON bodies', async () => {
|
|
375
|
+
await expect(router.route(getPostEvent('{'))).resolves.toEqual({
|
|
376
|
+
body: JSON.stringify({
|
|
377
|
+
errors: [{ message: 'Invalid JSON body' }],
|
|
378
|
+
}),
|
|
379
|
+
statusCode: 400,
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('rejects bodies without a GraphQL query', async () => {
|
|
384
|
+
await expect(router.route(getPostEvent(JSON.stringify({ variables: {} })))).resolves.toEqual({
|
|
385
|
+
body: JSON.stringify({
|
|
386
|
+
errors: [{ message: 'Missing GraphQL query' }],
|
|
387
|
+
}),
|
|
388
|
+
statusCode: 400,
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
`,
|
|
393
|
+
'packages/graphql/router.ts': `import { createRouter } from '@vyriy/router';
|
|
394
|
+
|
|
395
|
+
import { graphql } from 'graphql';
|
|
396
|
+
|
|
397
|
+
import { graphiql } from './graphiql.js';
|
|
398
|
+
import { schema } from './schema.js';
|
|
399
|
+
|
|
400
|
+
export const router = createRouter();
|
|
401
|
+
|
|
402
|
+
router.get('/', () => ({
|
|
403
|
+
headers: {
|
|
404
|
+
'Content-Type': 'text/html; charset=UTF-8',
|
|
405
|
+
},
|
|
406
|
+
body: graphiql(),
|
|
407
|
+
}));
|
|
408
|
+
|
|
409
|
+
router.post('/', async (params) => {
|
|
410
|
+
let payload: {
|
|
411
|
+
query?: string;
|
|
412
|
+
variables?: Record<string, unknown>;
|
|
413
|
+
operationName?: string;
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
if (!params.body) {
|
|
417
|
+
return {
|
|
418
|
+
statusCode: 400,
|
|
419
|
+
body: JSON.stringify({
|
|
420
|
+
errors: [{ message: 'Invalid JSON body' }],
|
|
421
|
+
}),
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
try {
|
|
426
|
+
payload = JSON.parse(params.body) as typeof payload;
|
|
427
|
+
} catch {
|
|
428
|
+
return {
|
|
429
|
+
statusCode: 400,
|
|
430
|
+
body: JSON.stringify({
|
|
431
|
+
errors: [{ message: 'Invalid JSON body' }],
|
|
432
|
+
}),
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (!payload.query) {
|
|
437
|
+
return {
|
|
438
|
+
statusCode: 400,
|
|
439
|
+
body: JSON.stringify({
|
|
440
|
+
errors: [{ message: 'Missing GraphQL query' }],
|
|
441
|
+
}),
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return {
|
|
446
|
+
body: JSON.stringify(
|
|
447
|
+
await graphql({
|
|
448
|
+
schema,
|
|
449
|
+
source: payload.query,
|
|
450
|
+
variableValues: payload.variables,
|
|
451
|
+
operationName: payload.operationName,
|
|
452
|
+
contextValue: {
|
|
453
|
+
event: params.event,
|
|
454
|
+
headers: params.headers ?? {},
|
|
455
|
+
},
|
|
456
|
+
}),
|
|
457
|
+
),
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
`,
|
|
461
|
+
'packages/graphql/schema.test.ts': `import { describe, expect, it } from '@jest/globals';
|
|
462
|
+
import { graphql } from 'graphql';
|
|
463
|
+
|
|
464
|
+
import { schema } from './schema.js';
|
|
465
|
+
|
|
466
|
+
describe('packages/graphql/schema.ts', () => {
|
|
467
|
+
it('resolves query fields', async () => {
|
|
468
|
+
await expect(
|
|
469
|
+
graphql({
|
|
470
|
+
schema,
|
|
471
|
+
source: '{ hello test { ok message } }',
|
|
472
|
+
}),
|
|
473
|
+
).resolves.toEqual({
|
|
474
|
+
data: {
|
|
475
|
+
hello: 'Hello from GraphQL',
|
|
476
|
+
test: {
|
|
477
|
+
ok: true,
|
|
478
|
+
message: 'GraphQL works',
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('resolves the ping mutation', async () => {
|
|
485
|
+
await expect(
|
|
486
|
+
graphql({
|
|
487
|
+
schema,
|
|
488
|
+
source: 'mutation Ping($message: String) { ping(message: $message) }',
|
|
489
|
+
variableValues: {
|
|
490
|
+
message: 'hello',
|
|
491
|
+
},
|
|
492
|
+
}),
|
|
493
|
+
).resolves.toEqual({
|
|
494
|
+
data: {
|
|
495
|
+
ping: 'pong: hello',
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
`,
|
|
501
|
+
'packages/graphql/schema.ts': `import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLBoolean } from 'graphql';
|
|
502
|
+
|
|
503
|
+
const TestType = new GraphQLObjectType({
|
|
504
|
+
name: 'Test',
|
|
505
|
+
fields: {
|
|
506
|
+
ok: {
|
|
507
|
+
type: GraphQLBoolean,
|
|
508
|
+
},
|
|
509
|
+
message: {
|
|
510
|
+
type: GraphQLString,
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
const QueryType = new GraphQLObjectType({
|
|
516
|
+
name: 'Query',
|
|
517
|
+
fields: {
|
|
518
|
+
hello: {
|
|
519
|
+
type: GraphQLString,
|
|
520
|
+
resolve: () => 'Hello from GraphQL',
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
test: {
|
|
524
|
+
type: TestType,
|
|
525
|
+
resolve: () => ({
|
|
526
|
+
ok: true,
|
|
527
|
+
message: 'GraphQL works',
|
|
528
|
+
}),
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
const MutationType = new GraphQLObjectType({
|
|
534
|
+
name: 'Mutation',
|
|
535
|
+
fields: {
|
|
536
|
+
ping: {
|
|
537
|
+
type: GraphQLString,
|
|
538
|
+
args: {
|
|
539
|
+
message: {
|
|
540
|
+
type: GraphQLString,
|
|
9
541
|
},
|
|
542
|
+
},
|
|
543
|
+
resolve: (_source, args: { message: string }) => {
|
|
544
|
+
return \`pong: \${args.message}\`;
|
|
545
|
+
},
|
|
10
546
|
},
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
547
|
+
},
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
export const schema = new GraphQLSchema({
|
|
551
|
+
query: QueryType,
|
|
552
|
+
mutation: MutationType,
|
|
553
|
+
});
|
|
554
|
+
`,
|
|
555
|
+
'workspaces/graphql/bin/build.sh': `#!/usr/bin/env sh
|
|
556
|
+
|
|
557
|
+
set -e
|
|
558
|
+
|
|
559
|
+
scriptdir="$PWD/workspaces/graphql";
|
|
560
|
+
distdir="dist/graphql";
|
|
561
|
+
|
|
562
|
+
NODE_ENV=production npx webpack --config $scriptdir/webpack.config.ts
|
|
563
|
+
|
|
564
|
+
cp $scriptdir/package.json $distdir/package.json
|
|
565
|
+
npm pkg delete "type" --prefix $distdir
|
|
566
|
+
npm pkg delete "private" --prefix $distdir
|
|
567
|
+
`,
|
|
568
|
+
'workspaces/graphql/bin/start.sh': `#!/usr/bin/env sh
|
|
569
|
+
|
|
570
|
+
set -e
|
|
571
|
+
|
|
572
|
+
scriptdir="$PWD/workspaces/graphql";
|
|
573
|
+
|
|
574
|
+
NODE_ENV=production LOG_LEVEL=info tsx $scriptdir/index.ts
|
|
575
|
+
`,
|
|
576
|
+
'workspaces/graphql/doc.mdx': `import { Meta, Markdown } from '@storybook/addon-docs/blocks';
|
|
577
|
+
import ReadMe from './README.md?raw';
|
|
578
|
+
|
|
579
|
+
<Meta title="Workspaces/Graphql" />
|
|
580
|
+
|
|
581
|
+
<Markdown>{ReadMe}</Markdown>
|
|
582
|
+
`,
|
|
583
|
+
'workspaces/graphql/index.test.ts': `import { describe, expect, it, jest } from '@jest/globals';
|
|
584
|
+
import type { APIGatewayProxyEvent } from '@vyriy/router';
|
|
585
|
+
|
|
586
|
+
const apiMock = jest.fn((handler) => ({
|
|
587
|
+
handler,
|
|
588
|
+
}));
|
|
589
|
+
const serverMock = jest.fn();
|
|
590
|
+
|
|
591
|
+
jest.mock('@vyriy/handler', () => ({
|
|
592
|
+
api: apiMock,
|
|
593
|
+
}));
|
|
594
|
+
|
|
595
|
+
jest.mock('@vyriy/server', () => ({
|
|
596
|
+
server: serverMock,
|
|
597
|
+
}));
|
|
598
|
+
|
|
599
|
+
describe('workspaces/graphql/index.ts', () => {
|
|
600
|
+
const getPostEvent = (body: string | null, path = '/'): APIGatewayProxyEvent =>
|
|
601
|
+
({
|
|
602
|
+
body,
|
|
603
|
+
headers: {},
|
|
604
|
+
httpMethod: 'POST',
|
|
605
|
+
path,
|
|
606
|
+
pathParameters: null,
|
|
607
|
+
queryStringParameters: null,
|
|
608
|
+
}) as unknown as APIGatewayProxyEvent;
|
|
609
|
+
|
|
610
|
+
const getEvent = (path = '/'): APIGatewayProxyEvent =>
|
|
611
|
+
({
|
|
612
|
+
body: null,
|
|
613
|
+
headers: {},
|
|
614
|
+
httpMethod: 'GET',
|
|
615
|
+
path,
|
|
616
|
+
pathParameters: null,
|
|
617
|
+
queryStringParameters: null,
|
|
618
|
+
}) as unknown as APIGatewayProxyEvent;
|
|
619
|
+
|
|
620
|
+
it('starts the server with the API router handler', async () => {
|
|
621
|
+
await import('./index.js');
|
|
622
|
+
|
|
623
|
+
expect(apiMock).toHaveBeenCalledTimes(1);
|
|
624
|
+
expect(serverMock).toHaveBeenCalledTimes(1);
|
|
625
|
+
expect(serverMock).toHaveBeenCalledWith(apiMock.mock.results[0]?.value);
|
|
626
|
+
|
|
627
|
+
const handler = apiMock.mock.calls[0]?.[0] as (event: APIGatewayProxyEvent) => Promise<{
|
|
628
|
+
body: string;
|
|
629
|
+
headers?: Record<string, string>;
|
|
630
|
+
statusCode: number;
|
|
631
|
+
}>;
|
|
632
|
+
|
|
633
|
+
await expect(
|
|
634
|
+
handler(
|
|
635
|
+
getPostEvent(
|
|
636
|
+
JSON.stringify({
|
|
637
|
+
query: '{ hello test { ok message } }',
|
|
638
|
+
}),
|
|
639
|
+
),
|
|
640
|
+
),
|
|
641
|
+
).resolves.toEqual({
|
|
642
|
+
body: JSON.stringify({
|
|
643
|
+
data: {
|
|
644
|
+
hello: 'Hello from GraphQL',
|
|
645
|
+
test: {
|
|
646
|
+
ok: true,
|
|
647
|
+
message: 'GraphQL works',
|
|
648
|
+
},
|
|
14
649
|
},
|
|
650
|
+
}),
|
|
651
|
+
statusCode: 200,
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
const graphiqlResponse = await handler(getEvent());
|
|
655
|
+
|
|
656
|
+
expect(graphiqlResponse).toEqual({
|
|
657
|
+
body: expect.stringContaining('<title>GraphiQL</title>'),
|
|
658
|
+
headers: {
|
|
659
|
+
'Content-Type': 'text/html; charset=UTF-8',
|
|
660
|
+
},
|
|
661
|
+
statusCode: 200,
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
await expect(handler(getEvent('/healthcheck'))).resolves.toEqual({
|
|
665
|
+
body: JSON.stringify({
|
|
666
|
+
message: 'Not Found',
|
|
667
|
+
}),
|
|
668
|
+
statusCode: 404,
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
`,
|
|
673
|
+
'workspaces/graphql/index.ts': `import { server } from '@vyriy/server';
|
|
674
|
+
import { api } from '@vyriy/handler';
|
|
675
|
+
|
|
676
|
+
import { router } from '@p/graphql';
|
|
677
|
+
|
|
678
|
+
server(api(async (event) => router.route(event)));
|
|
679
|
+
`,
|
|
680
|
+
'workspaces/graphql/package.json': `{
|
|
681
|
+
"name": "@w/graphql",
|
|
682
|
+
"type": "module",
|
|
683
|
+
"private": true
|
|
684
|
+
}
|
|
685
|
+
`,
|
|
686
|
+
'workspaces/graphql/README.md': `# @w/graphql
|
|
687
|
+
|
|
688
|
+
GraphQL runtime workspace for the application.
|
|
689
|
+
|
|
690
|
+
This workspace starts the HTTP server and mounts the reusable \`@p/graphql\` router through \`@vyriy/handler\` and \`@vyriy/server\`.
|
|
691
|
+
|
|
692
|
+
## Routes
|
|
693
|
+
|
|
694
|
+
- \`GET /\` - serves the embedded GraphiQL page.
|
|
695
|
+
- \`POST /\` - executes GraphQL requests against the package schema.
|
|
696
|
+
|
|
697
|
+
Example request body:
|
|
698
|
+
|
|
699
|
+
\`\`\`json
|
|
700
|
+
{
|
|
701
|
+
"query": "{ hello test { ok message } }"
|
|
702
|
+
}
|
|
703
|
+
\`\`\`
|
|
704
|
+
|
|
705
|
+
## Development
|
|
706
|
+
|
|
707
|
+
Start the local GraphQL server:
|
|
708
|
+
|
|
709
|
+
\`\`\`bash
|
|
710
|
+
yarn start:graphql
|
|
711
|
+
\`\`\`
|
|
712
|
+
|
|
713
|
+
Build the deployable bundle:
|
|
714
|
+
|
|
715
|
+
\`\`\`bash
|
|
716
|
+
yarn build:graphql
|
|
717
|
+
\`\`\`
|
|
718
|
+
|
|
719
|
+
The build output is written to \`dist/graphql\`.
|
|
720
|
+
|
|
721
|
+
## Implementation
|
|
722
|
+
|
|
723
|
+
The workspace entrypoint is \`index.ts\`. It wraps the shared router with \`api()\` and passes it to \`server()\`:
|
|
724
|
+
|
|
725
|
+
\`\`\`ts
|
|
726
|
+
server(api(async (event) => router.route(event)));
|
|
727
|
+
\`\`\`
|
|
728
|
+
`,
|
|
729
|
+
'workspaces/graphql/webpack.config.ts': `import { path } from '@vyriy/path';
|
|
730
|
+
import { ssr, external } from '@vyriy/webpack-config';
|
|
731
|
+
|
|
732
|
+
export default ssr(
|
|
733
|
+
'@w/graphql',
|
|
734
|
+
{
|
|
735
|
+
path: path('dist', 'graphql'),
|
|
736
|
+
filename: 'index.js',
|
|
737
|
+
library: { type: 'commonjs2' },
|
|
738
|
+
},
|
|
739
|
+
(config) => ({
|
|
740
|
+
...config,
|
|
741
|
+
externals: [external({ allowlist: [/^@p/, /^@w/, /^@vyriy/] })],
|
|
742
|
+
}),
|
|
743
|
+
);
|
|
744
|
+
`,
|
|
745
|
+
}),
|
|
746
|
+
ci: {
|
|
747
|
+
...base.ci,
|
|
15
748
|
},
|
|
749
|
+
deploy: {},
|
|
16
750
|
};
|
|
@@ -5,6 +5,7 @@ import { ssr } from './ssr.js';
|
|
|
5
5
|
import { ssg } from './ssg.js';
|
|
6
6
|
import { spa } from './spa.js';
|
|
7
7
|
import { rest } from './rest.js';
|
|
8
|
+
import { gql } from './gql.js';
|
|
8
9
|
export const presets = {
|
|
9
10
|
base: {
|
|
10
11
|
name: 'Base',
|
|
@@ -41,4 +42,9 @@ export const presets = {
|
|
|
41
42
|
description: 'Preset for simple REST API',
|
|
42
43
|
preset: rest,
|
|
43
44
|
},
|
|
45
|
+
gql: {
|
|
46
|
+
name: 'GraphQL',
|
|
47
|
+
description: 'Preset for GraphQL API',
|
|
48
|
+
preset: gql,
|
|
49
|
+
},
|
|
44
50
|
};
|
|
@@ -48,7 +48,6 @@ export const rest = {
|
|
|
48
48
|
'@vyriy/storybook-config': `^${packageJson.version}`,
|
|
49
49
|
storybook: packageJson.peerDependencies.storybook,
|
|
50
50
|
'@vyriy/path': `^${packageJson.version}`,
|
|
51
|
-
vyriy: `^${packageJson.version}`,
|
|
52
51
|
husky: packageJson.peerDependencies.husky,
|
|
53
52
|
'npm-run-all2': packageJson.peerDependencies['npm-run-all2'],
|
|
54
53
|
'cross-env': packageJson.peerDependencies['cross-env'],
|
|
@@ -57,7 +56,6 @@ export const rest = {
|
|
|
57
56
|
'@vyriy/handler': `^${packageJson.version}`,
|
|
58
57
|
'@vyriy/server': `^${packageJson.version}`,
|
|
59
58
|
tsx: packageJson.peerDependencies.tsx,
|
|
60
|
-
webpack: packageJson.peerDependencies.webpack,
|
|
61
59
|
'webpack-cli': packageJson.peerDependencies['webpack-cli'],
|
|
62
60
|
'@vyriy/router': `^${packageJson.version}`,
|
|
63
61
|
'@vyriy/html': `^${packageJson.version}`,
|
|
@@ -51,14 +51,12 @@ export const spa = {
|
|
|
51
51
|
'@vyriy/storybook-config': `^${packageJson.version}`,
|
|
52
52
|
storybook: packageJson.peerDependencies.storybook,
|
|
53
53
|
'@vyriy/path': `^${packageJson.version}`,
|
|
54
|
-
vyriy: `^${packageJson.version}`,
|
|
55
54
|
husky: packageJson.peerDependencies.husky,
|
|
56
55
|
'npm-run-all2': packageJson.peerDependencies['npm-run-all2'],
|
|
57
56
|
'cross-env': packageJson.peerDependencies['cross-env'],
|
|
58
57
|
rimraf: packageJson.peerDependencies.rimraf,
|
|
59
58
|
'@vyriy/webpack-config': `^${packageJson.version}`,
|
|
60
59
|
tsx: packageJson.peerDependencies.tsx,
|
|
61
|
-
webpack: packageJson.peerDependencies.webpack,
|
|
62
60
|
'webpack-cli': packageJson.peerDependencies['webpack-cli'],
|
|
63
61
|
react: packageJson.peerDependencies.react,
|
|
64
62
|
'react-dom': packageJson.peerDependencies['react-dom'],
|
|
@@ -51,7 +51,6 @@ export const ssg = {
|
|
|
51
51
|
'@vyriy/storybook-config': `^${packageJson.version}`,
|
|
52
52
|
storybook: packageJson.peerDependencies.storybook,
|
|
53
53
|
'@vyriy/path': `^${packageJson.version}`,
|
|
54
|
-
vyriy: `^${packageJson.version}`,
|
|
55
54
|
husky: packageJson.peerDependencies.husky,
|
|
56
55
|
'npm-run-all2': packageJson.peerDependencies['npm-run-all2'],
|
|
57
56
|
'cross-env': packageJson.peerDependencies['cross-env'],
|
|
@@ -59,7 +58,6 @@ export const ssg = {
|
|
|
59
58
|
'@vyriy/webpack-config': `^${packageJson.version}`,
|
|
60
59
|
'@vyriy/script': `^${packageJson.version}`,
|
|
61
60
|
tsx: packageJson.peerDependencies.tsx,
|
|
62
|
-
webpack: packageJson.peerDependencies.webpack,
|
|
63
61
|
'webpack-cli': packageJson.peerDependencies['webpack-cli'],
|
|
64
62
|
react: packageJson.peerDependencies.react,
|
|
65
63
|
'react-dom': packageJson.peerDependencies['react-dom'],
|
|
@@ -51,7 +51,6 @@ export const ssr = {
|
|
|
51
51
|
'@vyriy/storybook-config': `^${packageJson.version}`,
|
|
52
52
|
storybook: packageJson.peerDependencies.storybook,
|
|
53
53
|
'@vyriy/path': `^${packageJson.version}`,
|
|
54
|
-
vyriy: `^${packageJson.version}`,
|
|
55
54
|
husky: packageJson.peerDependencies.husky,
|
|
56
55
|
'npm-run-all2': packageJson.peerDependencies['npm-run-all2'],
|
|
57
56
|
'cross-env': packageJson.peerDependencies['cross-env'],
|
|
@@ -60,7 +59,6 @@ export const ssr = {
|
|
|
60
59
|
'@vyriy/handler': `^${packageJson.version}`,
|
|
61
60
|
'@vyriy/server': `^${packageJson.version}`,
|
|
62
61
|
tsx: packageJson.peerDependencies.tsx,
|
|
63
|
-
webpack: packageJson.peerDependencies.webpack,
|
|
64
62
|
'webpack-cli': packageJson.peerDependencies['webpack-cli'],
|
|
65
63
|
react: packageJson.peerDependencies.react,
|
|
66
64
|
'react-dom': packageJson.peerDependencies['react-dom'],
|
package/commands/types.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
export type Command = () => Promise<number>;
|
|
2
|
-
export type
|
|
2
|
+
export type EnvironmentCheckResult = {
|
|
3
3
|
ok: boolean;
|
|
4
4
|
message: string;
|
|
5
5
|
};
|
|
6
|
-
export type
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}>;
|
|
6
|
+
export type Node = () => EnvironmentCheckResult;
|
|
7
|
+
export type Corepack = () => Promise<EnvironmentCheckResult>;
|
|
8
|
+
export type Yarn = () => Promise<EnvironmentCheckResult>;
|
|
10
9
|
export type CreateOptions = {
|
|
11
10
|
readonly directory: string;
|
|
12
11
|
readonly dryRun: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vyriy",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "Interactive project master for calm cloud-ready applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "./bin/vyriy.js",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"@types/react-dom": "^19.2.3",
|
|
14
14
|
"cross-env": "^10.1.0",
|
|
15
15
|
"eslint": "^10.4.0",
|
|
16
|
+
"graphql": "^16.14.0",
|
|
16
17
|
"husky": "^9.1.7",
|
|
17
18
|
"jest": "^30.4.2",
|
|
18
19
|
"npm-run-all2": "^9.0.1",
|
|
@@ -25,8 +26,7 @@
|
|
|
25
26
|
"stylelint": "^17.12.0",
|
|
26
27
|
"tsx": "^4.22.3",
|
|
27
28
|
"typescript": "^6.0.3",
|
|
28
|
-
"webpack": "^
|
|
29
|
-
"webpack-cli": "^7.0.2"
|
|
29
|
+
"webpack-cli": "^7.0.3"
|
|
30
30
|
},
|
|
31
31
|
"peerDependenciesMeta": {
|
|
32
32
|
"@types/react": {
|
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
"eslint": {
|
|
42
42
|
"optional": true
|
|
43
43
|
},
|
|
44
|
+
"graphql": {
|
|
45
|
+
"optional": true
|
|
46
|
+
},
|
|
44
47
|
"husky": {
|
|
45
48
|
"optional": true
|
|
46
49
|
},
|
|
@@ -77,9 +80,6 @@
|
|
|
77
80
|
"typescript": {
|
|
78
81
|
"optional": true
|
|
79
82
|
},
|
|
80
|
-
"webpack": {
|
|
81
|
-
"optional": true
|
|
82
|
-
},
|
|
83
83
|
"webpack-cli": {
|
|
84
84
|
"optional": true
|
|
85
85
|
}
|