create-shaderpad 1.0.0-beta.67
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/bin/index.js +222 -0
- package/package.json +33 -0
- package/template-basic-js/index.html +12 -0
- package/template-basic-js/package-lock.json +1148 -0
- package/template-basic-js/package.json +18 -0
- package/template-basic-js/public/favicon.svg +1 -0
- package/template-basic-js/src/main.js +12 -0
- package/template-basic-js/src/shaders/fragment.glsl +46 -0
- package/template-basic-js/vite.config.js +9 -0
- package/template-basic-ts/index.html +12 -0
- package/template-basic-ts/package-lock.json +1163 -0
- package/template-basic-ts/package.json +19 -0
- package/template-basic-ts/public/favicon.svg +1 -0
- package/template-basic-ts/src/main.ts +12 -0
- package/template-basic-ts/src/shaders/fragment.glsl +46 -0
- package/template-basic-ts/src/shaders.d.ts +4 -0
- package/template-basic-ts/src/vite-env.d.ts +1 -0
- package/template-basic-ts/tsconfig.json +20 -0
- package/template-basic-ts/vite.config.ts +9 -0
- package/template-face-js/index.html +12 -0
- package/template-face-js/package-lock.json +1162 -0
- package/template-face-js/package.json +20 -0
- package/template-face-js/public/favicon.svg +1 -0
- package/template-face-js/src/main.js +51 -0
- package/template-face-js/src/shaders/fragment.glsl +17 -0
- package/template-face-js/vite.config.js +13 -0
- package/template-face-ts/index.html +12 -0
- package/template-face-ts/package-lock.json +1177 -0
- package/template-face-ts/package.json +21 -0
- package/template-face-ts/public/favicon.svg +1 -0
- package/template-face-ts/src/main.ts +51 -0
- package/template-face-ts/src/shaders/fragment.glsl +17 -0
- package/template-face-ts/src/shaders.d.ts +4 -0
- package/template-face-ts/src/vite-env.d.ts +1 -0
- package/template-face-ts/tsconfig.json +20 -0
- package/template-face-ts/vite.config.ts +13 -0
package/bin/index.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { spawnSync } = require('node:child_process');
|
|
6
|
+
|
|
7
|
+
const DEFAULT_TARGET_DIR = 'shaderpad-app';
|
|
8
|
+
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
9
|
+
const templateDefinitions = {
|
|
10
|
+
'basic-ts': {
|
|
11
|
+
label: 'Basic + TypeScript',
|
|
12
|
+
hint: 'default',
|
|
13
|
+
dir: 'template-basic-ts',
|
|
14
|
+
},
|
|
15
|
+
'basic-js': {
|
|
16
|
+
label: 'Basic + JavaScript',
|
|
17
|
+
dir: 'template-basic-js',
|
|
18
|
+
},
|
|
19
|
+
'face-ts': {
|
|
20
|
+
label: 'Face Plugin + TypeScript',
|
|
21
|
+
hint: 'webcam + MediaPipe',
|
|
22
|
+
dir: 'template-face-ts',
|
|
23
|
+
},
|
|
24
|
+
'face-js': {
|
|
25
|
+
label: 'Face Plugin + JavaScript',
|
|
26
|
+
hint: 'webcam + MediaPipe',
|
|
27
|
+
dir: 'template-face-js',
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const defaultTemplate = 'basic-ts';
|
|
31
|
+
|
|
32
|
+
function printHelp() {
|
|
33
|
+
console.log(`Usage: create-shaderpad [project-name] [--template <name>] [--skip-install]
|
|
34
|
+
|
|
35
|
+
Scaffold a ShaderPad starter project from one of the built-in templates.
|
|
36
|
+
|
|
37
|
+
Templates:
|
|
38
|
+
basic-ts Basic starter with TypeScript
|
|
39
|
+
basic-js Basic starter with JavaScript
|
|
40
|
+
face-ts Face plugin starter with TypeScript
|
|
41
|
+
face-js Face plugin starter with JavaScript
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
--template <name> Choose a template (default: ${defaultTemplate})
|
|
45
|
+
--skip-install, --no-install Copy files without running npm install
|
|
46
|
+
--help, -h Show this message
|
|
47
|
+
`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function toValidPackageName(projectName) {
|
|
51
|
+
return projectName
|
|
52
|
+
.trim()
|
|
53
|
+
.toLowerCase()
|
|
54
|
+
.replace(/\s+/g, '-')
|
|
55
|
+
.replace(/^[._]+/, '')
|
|
56
|
+
.replace(/[^a-z0-9-~]+/g, '-')
|
|
57
|
+
.replace(/-+/g, '-')
|
|
58
|
+
.replace(/^-|-$/g, '');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function copyTemplateFiles(sourceDir, targetDir) {
|
|
62
|
+
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
63
|
+
const sourcePath = path.join(sourceDir, entry.name);
|
|
64
|
+
const targetPath = path.join(targetDir, entry.name);
|
|
65
|
+
fs.cpSync(sourcePath, targetPath, { recursive: true });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function fail(message) {
|
|
70
|
+
console.error(`\n${message}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function parseArgs(args) {
|
|
75
|
+
const options = {
|
|
76
|
+
template: undefined,
|
|
77
|
+
shouldInstall: true,
|
|
78
|
+
inputDir: undefined,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
82
|
+
const arg = args[i];
|
|
83
|
+
if (arg === '--help' || arg === '-h') {
|
|
84
|
+
printHelp();
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
if (arg === '--skip-install' || arg === '--no-install') {
|
|
88
|
+
options.shouldInstall = false;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (arg === '--template') {
|
|
92
|
+
options.template = args[i + 1];
|
|
93
|
+
i += 1;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (arg.startsWith('-')) {
|
|
97
|
+
fail(`Unknown option: ${arg}`);
|
|
98
|
+
}
|
|
99
|
+
if (!options.inputDir) {
|
|
100
|
+
options.inputDir = arg;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
fail(`Unexpected extra argument: ${arg}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return options;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function promptForMissingOptions(options) {
|
|
110
|
+
const prompts = await import('@clack/prompts');
|
|
111
|
+
const { cancel, intro, isCancel, outro, select, text } = prompts;
|
|
112
|
+
|
|
113
|
+
intro('create-shaderpad');
|
|
114
|
+
|
|
115
|
+
let inputDir = options.inputDir;
|
|
116
|
+
if (!inputDir) {
|
|
117
|
+
const answer = await text({
|
|
118
|
+
message: 'Where should we create your project?',
|
|
119
|
+
placeholder: DEFAULT_TARGET_DIR,
|
|
120
|
+
initialValue: DEFAULT_TARGET_DIR,
|
|
121
|
+
validate(value) {
|
|
122
|
+
if (!value || !String(value).trim()) {
|
|
123
|
+
return 'Please enter a directory name.';
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
if (isCancel(answer)) {
|
|
128
|
+
cancel('Cancelled.');
|
|
129
|
+
process.exit(0);
|
|
130
|
+
}
|
|
131
|
+
inputDir = String(answer);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let templateKey = options.template;
|
|
135
|
+
if (!templateKey) {
|
|
136
|
+
const answer = await select({
|
|
137
|
+
message: 'Which starter do you want?',
|
|
138
|
+
initialValue: defaultTemplate,
|
|
139
|
+
options: Object.entries(templateDefinitions).map(([value, template]) => ({
|
|
140
|
+
value,
|
|
141
|
+
label: template.label,
|
|
142
|
+
hint: template.hint,
|
|
143
|
+
})),
|
|
144
|
+
});
|
|
145
|
+
if (isCancel(answer)) {
|
|
146
|
+
cancel('Cancelled.');
|
|
147
|
+
process.exit(0);
|
|
148
|
+
}
|
|
149
|
+
templateKey = String(answer);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
outro(`Using ${templateDefinitions[templateKey].label}`);
|
|
153
|
+
return { inputDir, templateKey };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function main() {
|
|
157
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
158
|
+
const hasPromptContext = process.stdin.isTTY && process.stdout.isTTY;
|
|
159
|
+
const { inputDir, templateKey } =
|
|
160
|
+
hasPromptContext && (!parsed.inputDir || !parsed.template)
|
|
161
|
+
? await promptForMissingOptions(parsed)
|
|
162
|
+
: {
|
|
163
|
+
inputDir: parsed.inputDir ?? DEFAULT_TARGET_DIR,
|
|
164
|
+
templateKey: parsed.template ?? defaultTemplate,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const template = templateDefinitions[templateKey];
|
|
168
|
+
if (!template) {
|
|
169
|
+
fail(
|
|
170
|
+
`Unknown template "${templateKey}". Expected one of: ${Object.keys(templateDefinitions).join(', ')}`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const templateDir = path.resolve(__dirname, '..', template.dir);
|
|
175
|
+
const targetDir = path.resolve(process.cwd(), inputDir);
|
|
176
|
+
const projectName =
|
|
177
|
+
inputDir === '.' ? path.basename(process.cwd()) : path.basename(path.normalize(targetDir));
|
|
178
|
+
|
|
179
|
+
if (!fs.existsSync(templateDir)) {
|
|
180
|
+
fail(`Template directory not found: ${templateDir}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (fs.existsSync(targetDir)) {
|
|
184
|
+
const existingEntries = fs.readdirSync(targetDir);
|
|
185
|
+
if (existingEntries.length > 0) {
|
|
186
|
+
fail(`Target directory is not empty: ${targetDir}`);
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
copyTemplateFiles(templateDir, targetDir);
|
|
193
|
+
|
|
194
|
+
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
195
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
196
|
+
packageJson.name = toValidPackageName(projectName) || 'shaderpad-app';
|
|
197
|
+
fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
198
|
+
|
|
199
|
+
console.log(
|
|
200
|
+
`\nScaffolded ${template.label} in ${path.relative(process.cwd(), targetDir) || '.'}`,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
if (parsed.shouldInstall) {
|
|
204
|
+
console.log('\nInstalling dependencies...\n');
|
|
205
|
+
const result = spawnSync(npmCommand, ['install'], {
|
|
206
|
+
cwd: targetDir,
|
|
207
|
+
stdio: 'inherit',
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
if (result.status !== 0) {
|
|
211
|
+
fail('npm install failed.');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const relativeTargetDir = path.relative(process.cwd(), targetDir) || '.';
|
|
216
|
+
const cdCommand = inputDir === '.' ? '' : `cd ${JSON.stringify(relativeTargetDir)}\n`;
|
|
217
|
+
console.log(`\nNext steps:\n${cdCommand}npm run dev\n`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
main().catch(error => {
|
|
221
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
222
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-shaderpad",
|
|
3
|
+
"version": "1.0.0-beta.67",
|
|
4
|
+
"description": "Scaffold a ShaderPad starter project.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"shaderpad",
|
|
7
|
+
"create",
|
|
8
|
+
"scaffold",
|
|
9
|
+
"vite",
|
|
10
|
+
"webgl"
|
|
11
|
+
],
|
|
12
|
+
"author": "rileyjshaw",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"bin": {
|
|
15
|
+
"create-shaderpad": "./bin/index.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"bin",
|
|
19
|
+
"template-basic-js",
|
|
20
|
+
"template-basic-ts",
|
|
21
|
+
"template-face-js",
|
|
22
|
+
"template-face-ts"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@clack/prompts": "^1.1.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
|
+
<title>Built with ShaderPad</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<script type="module" src="/src/main.js"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|