@milesroxas/create-design-hub 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -2
  3. package/index.mjs +154 -0
  4. package/lib.mjs +65 -0
  5. package/package.json +37 -3
  6. package/setup/.fallowrc.jsonc +6 -0
  7. package/setup/app.html +861 -0
  8. package/setup/lib.mjs +147 -0
  9. package/setup/lib.test.mjs +96 -0
  10. package/setup/server.mjs +444 -0
  11. package/setup/server.test.mjs +99 -0
  12. package/setup/slug.mjs +15 -0
  13. package/template/.claude/launch.json +18 -0
  14. package/template/.claude/settings.json +35 -0
  15. package/template/.design-hub/checks.json +9 -0
  16. package/template/.env.example +28 -0
  17. package/template/.impeccable/config.json +16 -0
  18. package/template/AGENTS.md +133 -0
  19. package/template/CLAUDE.md +1 -0
  20. package/template/DESIGN.md +126 -0
  21. package/template/Design Hub.command +11 -0
  22. package/template/PRODUCT.md +65 -0
  23. package/template/README.md +20 -0
  24. package/template/_gitattributes +2 -0
  25. package/template/_gitignore +41 -0
  26. package/template/api/review.ts +5 -0
  27. package/template/docs/.obsidian/app.json +6 -0
  28. package/template/docs/.obsidian/appearance.json +1 -0
  29. package/template/docs/.obsidian/core-plugins.json +32 -0
  30. package/template/docs/README.md +19 -0
  31. package/template/docs/sprints/sample/guide.md +69 -0
  32. package/template/kit/AppShell.tsx +149 -0
  33. package/template/kit/SiteShell.tsx +75 -0
  34. package/template/kit/app-shell.css +70 -0
  35. package/template/kit/icons.ts +11 -0
  36. package/template/kit/shell.ts +59 -0
  37. package/template/kit/site-shell.css +27 -0
  38. package/template/kit/tokens.css +249 -0
  39. package/template/package.json +38 -0
  40. package/template/plans/README.md +11 -0
  41. package/template/prototypes/sample-list/README.md +51 -0
  42. package/template/prototypes/sample-list/fixtures.json +87 -0
  43. package/template/prototypes/sample-list/index.tsx +122 -0
  44. package/template/prototypes/sample-list/styles.css +25 -0
  45. package/template/proxy.ts +4 -0
  46. package/template/sketches/paper/README.md +3 -0
  47. package/template/tsconfig.json +10 -0
  48. package/template/vercel.json +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Miles Roxas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,15 @@
1
- # Temporary Holding Version
1
+ # @milesroxas/create-design-hub
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Scaffolds a Design Hub project: a thin folder of prototypes, kit materials and docs that installs the `@milesroxas/design-hub` package and keeps nothing else.
4
+
5
+ ```bash
6
+ npm create @milesroxas/design-hub@latest my-project
7
+ cd my-project
8
+ pnpm dev
9
+ ```
10
+
11
+ The scaffolder pins `@milesroxas/design-hub` to its own version, starts a git repo, installs dependencies, copies the skills into `.claude/skills/` and makes the first commit. Pass `--help` for the flags, including `--visibility` for creating a GitHub repo and `--link` for developing against a local checkout of the package.
12
+
13
+ People without a terminal can run the setup app instead: see the `setup/` folder.
14
+
15
+ MIT. Source: https://github.com/milesroxas/design-hub
package/index.mjs ADDED
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ // npm create @milesroxas/design-hub -- --path <absolute path> [--owner <gh account>] [--visibility private|public|none]
3
+ //
4
+ // Copies the template into the folder, names the project after it, pins the
5
+ // design-hub version, starts a git repo, installs, and puts the miles-*
6
+ // skills in place. With --visibility private or public it also creates the
7
+ // GitHub repo and pushes. Flags only, never a prompt, one JSON object on
8
+ // stdout, `--help` prints the schema, a second run finds the project and
9
+ // changes nothing.
10
+ import { spawnSync } from 'node:child_process';
11
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
12
+ import { basename, dirname, join } from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ import { parseArgs } from 'node:util';
15
+ import { projectManifest, RENAMES, SKIP, SKIP_DIRS, SPEC, validate } from './lib.mjs';
16
+
17
+ const HERE = dirname(fileURLToPath(import.meta.url));
18
+ const TEMPLATE = join(HERE, 'template');
19
+
20
+ const print = (value) => console.log(JSON.stringify(value, null, 2));
21
+ function fail(error, detail = {}) {
22
+ print({ ok: false, error, ...detail });
23
+ process.exit(1);
24
+ }
25
+ function run(cmd, args, cwd) {
26
+ const out = spawnSync(cmd, args, { cwd, encoding: 'utf8', env: process.env });
27
+ if (out.error)
28
+ fail(`${cmd} is not installed.`, {
29
+ fix:
30
+ cmd === 'gh'
31
+ ? 'brew install gh, then gh auth login'
32
+ : cmd === 'pnpm'
33
+ ? 'corepack enable'
34
+ : `Install ${cmd}.`,
35
+ });
36
+ if (out.status !== 0)
37
+ fail(`${cmd} ${args[0]} failed.`, {
38
+ command: [cmd, ...args].join(' '),
39
+ stderr: out.stderr.trim().slice(-600),
40
+ });
41
+ return out.stdout.trim();
42
+ }
43
+
44
+ if (process.argv.includes('--help')) {
45
+ print(SPEC);
46
+ process.exit(0);
47
+ }
48
+
49
+ let values;
50
+ try {
51
+ ({ values } = parseArgs({
52
+ options: {
53
+ path: { type: 'string' },
54
+ owner: { type: 'string' },
55
+ visibility: { type: 'string' },
56
+ link: { type: 'string' },
57
+ 'no-install': { type: 'boolean' },
58
+ },
59
+ strict: true,
60
+ }));
61
+ } catch (error) {
62
+ fail(error.message, { fix: 'npm create @milesroxas/design-hub -- --help' });
63
+ }
64
+
65
+ const [major, minor] = process.versions.node.split('.').map(Number);
66
+ if (major < 22 || (major === 22 && minor < 18))
67
+ fail('Node 22.18 or newer is needed.', {
68
+ found: process.versions.node,
69
+ fix: 'Install the current LTS from https://nodejs.org',
70
+ });
71
+
72
+ const errors = validate(values);
73
+ if (Object.keys(errors).length) fail(Object.values(errors).join(' '), { errors });
74
+ const { path, visibility = 'none', link } = values;
75
+ const name = basename(path);
76
+ const next = `pnpm --dir "${path}" bootstrap`;
77
+
78
+ // A second run finds the project already there and changes nothing.
79
+ if (existsSync(join(path, 'package.json')) && existsSync(join(path, '.git'))) {
80
+ const remote =
81
+ spawnSync('git', ['-C', path, 'remote', 'get-url', 'origin'], { encoding: 'utf8' }).stdout?.trim() ||
82
+ null;
83
+ print({ ok: true, path, repo: remote, created: false, next });
84
+ process.exit(0);
85
+ }
86
+ if (existsSync(path) && readdirSync(path).length)
87
+ fail(`${path} exists and is not empty.`, { fix: 'Pick another --path.' });
88
+
89
+ /** The template, file by file: renamed dotfiles, no local state, no installed skills. */
90
+ function copyTemplate(from, to, depth = 0) {
91
+ mkdirSync(to, { recursive: true });
92
+ for (const entry of readdirSync(from, { withFileTypes: true })) {
93
+ if (SKIP.has(entry.name) && depth === 0) continue;
94
+ const keep = SKIP_DIRS[basename(from)];
95
+ if (keep && depth === 1 && !keep.has(entry.name)) continue;
96
+ const dest = join(to, RENAMES[entry.name] ?? entry.name);
97
+ if (entry.isDirectory()) copyTemplate(join(from, entry.name), dest, depth + 1);
98
+ else cpSync(join(from, entry.name), dest);
99
+ }
100
+ }
101
+
102
+ mkdirSync(dirname(path), { recursive: true });
103
+ copyTemplate(TEMPLATE, path);
104
+ // design-hub and create-design-hub publish in lockstep, so this package's version is the hub's.
105
+ const { version } = JSON.parse(readFileSync(join(HERE, 'package.json'), 'utf8'));
106
+ const template = JSON.parse(readFileSync(join(TEMPLATE, 'package.json'), 'utf8'));
107
+ writeFileSync(
108
+ join(path, 'package.json'),
109
+ `${JSON.stringify(projectManifest(template, { name, version, link }), null, 2)}\n`,
110
+ );
111
+ run('git', ['init', '-b', 'main'], path);
112
+
113
+ if (!values['no-install']) {
114
+ run('pnpm', ['install'], path);
115
+ run(join(path, 'node_modules', '.bin', 'design-hub'), ['skills'], path);
116
+ }
117
+ run('git', ['add', '-A'], path);
118
+ run(
119
+ 'git',
120
+ [
121
+ '-c',
122
+ 'user.name=Design Hub',
123
+ '-c',
124
+ 'user.email=hub@localhost',
125
+ 'commit',
126
+ '-q',
127
+ '-m',
128
+ `${name}: a Design Hub project`,
129
+ ],
130
+ path,
131
+ );
132
+
133
+ let repo = null;
134
+ if (visibility !== 'none') {
135
+ run('gh', ['auth', 'status']);
136
+ const owner = values.owner ?? run('gh', ['api', 'user', '--jq', '.login']);
137
+ run(
138
+ 'gh',
139
+ [
140
+ 'repo',
141
+ 'create',
142
+ `${owner}/${name}`,
143
+ `--${visibility}`,
144
+ '--source',
145
+ path,
146
+ '--remote',
147
+ 'origin',
148
+ '--push',
149
+ ],
150
+ path,
151
+ );
152
+ repo = `https://github.com/${owner}/${name}`;
153
+ }
154
+ print({ ok: true, path, repo, created: true, next });
package/lib.mjs ADDED
@@ -0,0 +1,65 @@
1
+ // The pure parts of create-design-hub: the rules a new project folder must
2
+ // meet and the edits the template gets. No I/O here, so every rule is tested
3
+ // without a disk.
4
+ import { basename, isAbsolute } from 'node:path';
5
+
6
+ export const SPEC = {
7
+ command: 'npm create @milesroxas/design-hub -- --path <folder>',
8
+ summary: 'Create a new project from the Design Hub template, as a local git repo or on GitHub.',
9
+ flags: {
10
+ '--path': {
11
+ type: 'string',
12
+ required: true,
13
+ description: 'Absolute folder for the project; its name is the repo name.',
14
+ },
15
+ '--owner': {
16
+ type: 'string',
17
+ description: 'GitHub account or org that owns the repo. Default: the signed-in gh account.',
18
+ },
19
+ '--visibility': {
20
+ type: 'string',
21
+ choices: ['private', 'public', 'none'],
22
+ default: 'none',
23
+ description: 'none: a local git repo only.',
24
+ },
25
+ '--link': {
26
+ type: 'string',
27
+ description:
28
+ 'Developing Design Hub itself: a local path to packages/design-hub, installed with the link: protocol.',
29
+ },
30
+ '--no-install': { type: 'boolean', description: 'Copy the template and stop before pnpm install.' },
31
+ },
32
+ output: '{ ok, path, repo, created, next }',
33
+ };
34
+
35
+ /** Files the template stores under another name, because npm drops dotfiles of these names from a package. */
36
+ export const RENAMES = { _gitignore: '.gitignore', _gitattributes: '.gitattributes' };
37
+
38
+ /** What is copied from the template: everything but what a project makes for itself. */
39
+ export const SKIP = new Set(['node_modules', 'dist', '.env', 'pnpm-lock.yaml']);
40
+ export const SKIP_DIRS = {
41
+ '.design-hub': new Set(['checks.json']),
42
+ '.claude': new Set(['settings.json', 'launch.json']),
43
+ };
44
+
45
+ /** The problems with a create request, by flag; empty when it is valid. */
46
+ export function validate({ path, visibility = 'none', template, link }) {
47
+ const errors = {};
48
+ if (!path || !isAbsolute(path)) errors.path = '--path takes an absolute folder.';
49
+ else if (!/^[\w.-]+$/.test(basename(path)))
50
+ errors.path = 'The folder name becomes the repo name: letters, digits, dot, dash, underscore.';
51
+ if (!['private', 'public', 'none'].includes(visibility))
52
+ errors.visibility = '--visibility takes private, public or none.';
53
+ if (template !== undefined && !/^[\w.-]+\/[\w.-]+$/.test(template))
54
+ errors.template = '--template takes owner/repo.';
55
+ if (link !== undefined && !isAbsolute(link)) errors.link = '--link takes an absolute path.';
56
+ return errors;
57
+ }
58
+
59
+ /** The project's package.json: named after its folder, pinned to the design-hub version that made it. */
60
+ export function projectManifest(template, { name, version, link }) {
61
+ const pkg = JSON.parse(JSON.stringify(template));
62
+ pkg.name = name;
63
+ pkg.dependencies['@milesroxas/design-hub'] = link ? `link:${link}` : `^${version}`;
64
+ return pkg;
65
+ }
package/package.json CHANGED
@@ -1,6 +1,40 @@
1
1
  {
2
2
  "name": "@milesroxas/create-design-hub",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1",
4
+ "description": "Create a Design Hub project: the thin folder of prototypes, kit materials and docs that installs the @milesroxas/design-hub package. Runs as `npm create @milesroxas/design-hub`.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/milesroxas/design-hub.git",
9
+ "directory": "packages/create-design-hub"
10
+ },
11
+ "type": "module",
12
+ "engines": {
13
+ "node": ">=22.18"
14
+ },
15
+ "bin": {
16
+ "create-design-hub": "./index.mjs"
17
+ },
18
+ "files": [
19
+ "index.mjs",
20
+ "lib.mjs",
21
+ "setup",
22
+ "template",
23
+ "!template/node_modules",
24
+ "!template/.design-hub/*",
25
+ "template/.design-hub/checks.json",
26
+ "!template/.claude/skills",
27
+ "!template/dist",
28
+ "!template/.env",
29
+ "README.md",
30
+ "LICENSE"
31
+ ],
32
+ "devDependencies": {
33
+ "@biomejs/biome": "2.6.3",
34
+ "@milesroxas/design-hub": "0.1.1"
35
+ },
36
+ "scripts": {
37
+ "check": "biome check --config-path ../design-hub/configs/biome .",
38
+ "test": "node --test \"*.test.mjs\" \"setup/*.test.mjs\""
39
+ }
6
40
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
3
+ // The setup app has no package.json: `Design Hub.command` runs server.mjs,
4
+ // and prototypes' `pnpm test` runs the tests.
5
+ "entry": ["server.mjs", "*.test.mjs"]
6
+ }