@twobirds/microcomponents 0.3.0 → 0.4.0
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 +77 -34
- package/dist/MC.min.js +1 -1
- package/dist/autoload.min.js +1 -1
- package/dist/cli/create.d.ts +53 -0
- package/dist/cli/create.d.ts.map +1 -0
- package/dist/cli/create.js +419 -0
- package/dist/cli/dispatch.d.ts +43 -0
- package/dist/cli/dispatch.d.ts.map +1 -0
- package/dist/cli/dispatch.js +251 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +12 -0
- package/dist/cli/scaffold.d.ts +68 -0
- package/dist/cli/scaffold.d.ts.map +1 -0
- package/dist/cli/scaffold.js +109 -0
- package/dist/cli/writeScaffold.d.ts +37 -0
- package/dist/cli/writeScaffold.d.ts.map +1 -0
- package/dist/cli/writeScaffold.js +60 -0
- package/dist/core/MC.d.ts +50 -9
- package/dist/core/MC.d.ts.map +1 -1
- package/dist/core/MC.js +78 -33
- package/dist/core/elements.d.ts +11 -8
- package/dist/core/elements.d.ts.map +1 -1
- package/dist/core/elements.js +12 -7
- package/dist/core/mcGuards.d.ts +2 -2
- package/dist/elements.min.js +1 -1
- package/dist/formValues.min.js +1 -1
- package/dist/microcomponents.min.js +1 -1
- package/dist/observables.min.js +1 -1
- package/dist/state/formValues.d.ts +11 -5
- package/dist/state/formValues.d.ts.map +1 -1
- package/dist/state/formValues.js +18 -11
- package/dist/state/observables.d.ts +30 -13
- package/dist/state/observables.d.ts.map +1 -1
- package/dist/state/observables.js +64 -35
- package/dist/state/placeholders.d.ts.map +1 -1
- package/dist/state/placeholders.js +16 -11
- package/dist/types/mcTypes.d.ts +16 -7
- package/dist/types/mcTypes.d.ts.map +1 -1
- package/dist/utils/autoload.d.ts +6 -3
- package/dist/utils/autoload.d.ts.map +1 -1
- package/dist/utils/autoload.js +2 -2
- package/dist/utils/events.d.ts +21 -28
- package/dist/utils/events.d.ts.map +1 -1
- package/dist/utils/events.js +23 -29
- package/dist/utils/helpers.d.ts +19 -19
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/helpers.js +19 -19
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -2
- package/dist/utils/resolveComponentPath.d.ts +36 -0
- package/dist/utils/resolveComponentPath.d.ts.map +1 -0
- package/dist/utils/resolveComponentPath.js +39 -0
- package/package.json +19 -13
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Project scaffolding for `mc create --namespace xxx`.
|
|
3
|
+
|
|
4
|
+
Pure, filesystem-free logic: given a namespace it returns the files of a complete,
|
|
5
|
+
working Micro Components dev environment and their contents. The starter component's
|
|
6
|
+
path is not re-derived here — it comes from {@link tagPath}, the same primitive the
|
|
7
|
+
runtime autoload loader uses, so the scaffold and the runtime can never disagree about
|
|
8
|
+
where a component lives.
|
|
9
|
+
|
|
10
|
+
The namespace is the first segment of every component tag, so `xxx` makes `src/xxx/`
|
|
11
|
+
the home of tags like `<xxx-app>` (tagPath `xxx-app` → `xxx/app`). The generated
|
|
12
|
+
environment mirrors the repo's own demos: a Vite dev server serving `src/` straight,
|
|
13
|
+
a page shell at that root that turns on `autoload(true)`, and a starter component with
|
|
14
|
+
companion `targetHtml` / `targetCss` assets. Because the page shell is the autoload
|
|
15
|
+
base, `import.meta.url` + `tagPath('xxx-app')` resolves to `src/xxx/app.js` — the
|
|
16
|
+
compiled starter.
|
|
17
|
+
|
|
18
|
+
@module CLI/Project
|
|
19
|
+
*/
|
|
20
|
+
import { tagPath } from '../utils/resolveComponentPath.js';
|
|
21
|
+
import { kebabToPascal } from '../utils/helpers.js';
|
|
22
|
+
/** The starter component tag for a namespace: `xxx` → `xxx-app`. */
|
|
23
|
+
function starterTag(namespace) {
|
|
24
|
+
return `${namespace}-app`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
Whether a string is a valid project namespace: 1 to 4 lowercase letters.
|
|
28
|
+
|
|
29
|
+
@example
|
|
30
|
+
```ts
|
|
31
|
+
console.log(isValidNamespace('mc'));
|
|
32
|
+
console.log(isValidNamespace('mcxyz'));
|
|
33
|
+
console.log(isValidNamespace('toolong'));
|
|
34
|
+
```
|
|
35
|
+
@param ns - Candidate namespace.
|
|
36
|
+
*/
|
|
37
|
+
export function isValidNamespace(ns) {
|
|
38
|
+
return /^[a-z]{1,4}$/.test(ns);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
Builds the files of a complete dev environment for a namespace.
|
|
42
|
+
|
|
43
|
+
@example
|
|
44
|
+
```ts
|
|
45
|
+
const { namespace, files } = scaffoldProject('mc');
|
|
46
|
+
console.log(namespace);
|
|
47
|
+
console.log(Object.keys(files));
|
|
48
|
+
```
|
|
49
|
+
@param namespace - 1 to 4 lowercase letters; becomes the `src/<namespace>/` folder and
|
|
50
|
+
the first segment of every component tag.
|
|
51
|
+
@throws If `namespace` is not 1 to 4 lowercase letters.
|
|
52
|
+
*/
|
|
53
|
+
export function scaffoldProject(namespace) {
|
|
54
|
+
if (!isValidNamespace(namespace)) {
|
|
55
|
+
throw new Error(`invalid namespace "${namespace}": must be 1 to 4 lowercase letters (e.g. "mc")`);
|
|
56
|
+
}
|
|
57
|
+
const tag = starterTag(namespace);
|
|
58
|
+
const base = tagPath(tag);
|
|
59
|
+
const files = {};
|
|
60
|
+
files['package.json'] = packageJson(namespace);
|
|
61
|
+
files['tsconfig.json'] = tsconfig();
|
|
62
|
+
files['tsconfig.vc.json'] = tsconfigVc();
|
|
63
|
+
files['vite.config.ts'] = viteConfig(namespace);
|
|
64
|
+
files['.gitignore'] = gitignore();
|
|
65
|
+
files['README.md'] = readme(namespace, tag);
|
|
66
|
+
files['src/index.html'] = shell(namespace, tag);
|
|
67
|
+
files[`src/${base}.ts`] = appSource(namespace, tag);
|
|
68
|
+
files[`src/${base}.target.html`] = appHtml(namespace);
|
|
69
|
+
files[`src/${base}.target.css`] = appCss(tag);
|
|
70
|
+
return { files, namespace };
|
|
71
|
+
}
|
|
72
|
+
function packageJson(namespace) {
|
|
73
|
+
return JSON.stringify({
|
|
74
|
+
name: `${namespace}-app`,
|
|
75
|
+
private: true,
|
|
76
|
+
type: 'module',
|
|
77
|
+
scripts: {
|
|
78
|
+
dev: 'vite',
|
|
79
|
+
build: 'vite build',
|
|
80
|
+
preview: 'vite preview',
|
|
81
|
+
typecheck: 'tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.vc.json',
|
|
82
|
+
},
|
|
83
|
+
dependencies: {
|
|
84
|
+
'@twobirds/microcomponents': '^0.4.0',
|
|
85
|
+
},
|
|
86
|
+
devDependencies: {
|
|
87
|
+
vite: '^8.1.5',
|
|
88
|
+
typescript: '^6.0.3',
|
|
89
|
+
'@types/node': '^26.6.5',
|
|
90
|
+
},
|
|
91
|
+
}, null, '\t');
|
|
92
|
+
}
|
|
93
|
+
function tsconfig() {
|
|
94
|
+
return JSON.stringify({
|
|
95
|
+
compilerOptions: {
|
|
96
|
+
target: 'ESNext',
|
|
97
|
+
module: 'ESNext',
|
|
98
|
+
moduleResolution: 'Bundler',
|
|
99
|
+
lib: ['ESNext', 'DOM', 'DOM.Iterable'],
|
|
100
|
+
strict: true,
|
|
101
|
+
esModuleInterop: true,
|
|
102
|
+
skipLibCheck: true,
|
|
103
|
+
noEmit: true,
|
|
104
|
+
},
|
|
105
|
+
include: ['src/**/*.ts'],
|
|
106
|
+
}, null, '\t');
|
|
107
|
+
}
|
|
108
|
+
function tsconfigVc() {
|
|
109
|
+
return JSON.stringify({
|
|
110
|
+
compilerOptions: {
|
|
111
|
+
target: 'ESNext',
|
|
112
|
+
module: 'ESNext',
|
|
113
|
+
moduleResolution: 'Bundler',
|
|
114
|
+
lib: ['ESNext', 'DOM'],
|
|
115
|
+
types: ['node'],
|
|
116
|
+
strict: true,
|
|
117
|
+
esModuleInterop: true,
|
|
118
|
+
skipLibCheck: true,
|
|
119
|
+
noEmit: true,
|
|
120
|
+
},
|
|
121
|
+
files: ['vite.config.ts'],
|
|
122
|
+
}, null, '\t');
|
|
123
|
+
}
|
|
124
|
+
function gitignore() {
|
|
125
|
+
return `node_modules/
|
|
126
|
+
dist/
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
function readme(namespace, tag) {
|
|
130
|
+
return `# ${namespace} App
|
|
131
|
+
|
|
132
|
+
A Micro Components project scaffolded by \`mc create --namespace ${namespace}\`.
|
|
133
|
+
|
|
134
|
+
## Install
|
|
135
|
+
|
|
136
|
+
\`\`\`sh
|
|
137
|
+
pnpm install
|
|
138
|
+
\`\`\`
|
|
139
|
+
|
|
140
|
+
## Dev
|
|
141
|
+
|
|
142
|
+
\`\`\`sh
|
|
143
|
+
pnpm dev
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
146
|
+
## Build
|
|
147
|
+
|
|
148
|
+
\`\`\`sh
|
|
149
|
+
pnpm build
|
|
150
|
+
\`\`\`
|
|
151
|
+
|
|
152
|
+
Components compile to \`dist/\` at their own autoload path, and companion \`.css\`, \`.html\` and
|
|
153
|
+
\`.json\` files are copied next to them, so the built output loads like the dev server.
|
|
154
|
+
|
|
155
|
+
## Type-check
|
|
156
|
+
|
|
157
|
+
\`\`\`sh
|
|
158
|
+
pnpm typecheck
|
|
159
|
+
\`\`\`
|
|
160
|
+
|
|
161
|
+
\`tsconfig.json\` checks \`src/\`; \`tsconfig.vc.json\` checks \`vite.config.ts\`, which lives outside
|
|
162
|
+
\`src/\` and needs node types for its \`node:\` imports.
|
|
163
|
+
|
|
164
|
+
## Components
|
|
165
|
+
|
|
166
|
+
Components live in \`src/${namespace}/\`. The starter is \`<${tag}>\` (\`src/${namespace}/app.ts\`).
|
|
167
|
+
|
|
168
|
+
Add a component with:
|
|
169
|
+
|
|
170
|
+
\`\`\`sh
|
|
171
|
+
npx mc new ${namespace}-name --target-html --target-css
|
|
172
|
+
\`\`\`
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
function viteConfig(namespace) {
|
|
176
|
+
return `/**
|
|
177
|
+
* Dev and build config for a Micro Components project.
|
|
178
|
+
*
|
|
179
|
+
* \`src/\` is the Vite root, so the page shell and every component source are
|
|
180
|
+
* served straight from source with no build step. Companion \`.css\`, \`.html\` and
|
|
181
|
+
* \`.json\` files are served as raw text — Vite would otherwise answer a \`.html\`
|
|
182
|
+
* request with its own transformed page (injecting \`/@vite/client\` into the
|
|
183
|
+
* template) and a \`.css\` request with a JS module, neither of which
|
|
184
|
+
* \`loadAssets()\` can consume. \`copyCompanions\` mirrors the same files into
|
|
185
|
+
* \`dist/\` so the built output stays autoload-ready, and every component under
|
|
186
|
+
* \`src/<namespace>/\` is a build entry, emitted at its own autoload path
|
|
187
|
+
* (\`mc/app.ts\` → \`dist/mc/app.js\`).
|
|
188
|
+
*
|
|
189
|
+
* @module ViteConfig
|
|
190
|
+
*/
|
|
191
|
+
|
|
192
|
+
import { defineConfig, type Plugin, type ResolvedConfig } from 'vite';
|
|
193
|
+
import { cp, mkdir, readdir, readFile } from 'node:fs/promises';
|
|
194
|
+
import path from 'node:path';
|
|
195
|
+
|
|
196
|
+
const projectRoot = import.meta.dirname;
|
|
197
|
+
const sourceRoot = path.join(projectRoot, 'src');
|
|
198
|
+
const namespace = '${namespace}';
|
|
199
|
+
|
|
200
|
+
const pageShells = new Set(['index.html', 'index-dev.html']);
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Whether a served file is a companion asset that must stay raw text, rather
|
|
204
|
+
* than one of the page shells Vite is allowed to transform.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* \`\`\`ts
|
|
208
|
+
* isCompanionAsset('app.target.html');
|
|
209
|
+
* isCompanionAsset('index.html');
|
|
210
|
+
* \`\`\`
|
|
211
|
+
*
|
|
212
|
+
* @param name Basename of the requested file.
|
|
213
|
+
*/
|
|
214
|
+
function isCompanionAsset(name: string): boolean {
|
|
215
|
+
if (pageShells.has(name)) return false;
|
|
216
|
+
return name.endsWith('.css') || name.endsWith('.json') || name.endsWith('.html');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Serves companion \`.css\`, \`.html\` and \`.json\` files as raw text, ahead of the
|
|
221
|
+
* Vite transform middleware. Without it a \`.html\` request returns the transformed
|
|
222
|
+
* page shell and a \`.css\` request returns a JS module, so \`loadAssets()\` receives
|
|
223
|
+
* markup it cannot use.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* \`\`\`ts
|
|
227
|
+
* plugins: [rawAssets(sourceRoot)],
|
|
228
|
+
* \`\`\`
|
|
229
|
+
*
|
|
230
|
+
* @param root Source root that mirrors the \`src/\` tree.
|
|
231
|
+
*/
|
|
232
|
+
function rawAssets(root: string): Plugin {
|
|
233
|
+
return {
|
|
234
|
+
name: 'raw-assets',
|
|
235
|
+
enforce: 'pre',
|
|
236
|
+
configureServer(server) {
|
|
237
|
+
server.middlewares.use(async (req, res, next) => {
|
|
238
|
+
const url = req.url?.split('?')[0];
|
|
239
|
+
if (!url) return next();
|
|
240
|
+
const file = decodeURIComponent(url);
|
|
241
|
+
const name = file.split('/').pop() ?? '';
|
|
242
|
+
if (!isCompanionAsset(name)) return next();
|
|
243
|
+
try {
|
|
244
|
+
const text = await readFile(path.join(root, file), 'utf-8');
|
|
245
|
+
res.setHeader('Content-Type', name.endsWith('.css') ? 'text/css' : 'text/plain');
|
|
246
|
+
res.end(text);
|
|
247
|
+
} catch {
|
|
248
|
+
next();
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Copies every companion asset from \`src/\` into the build output, preserving the
|
|
257
|
+
* directory tree. Autoload resolves \`<ns-app>\` to \`<ns>/app.js\` relative to the
|
|
258
|
+
* page shell, so \`dist/\` needs the compiled component *and* its companion files
|
|
259
|
+
* at the same relative path.
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* \`\`\`ts
|
|
263
|
+
* plugins: [copyCompanions(sourceRoot)],
|
|
264
|
+
* \`\`\`
|
|
265
|
+
*
|
|
266
|
+
* @param root Source root to walk.
|
|
267
|
+
*/
|
|
268
|
+
function copyCompanions(root: string): Plugin {
|
|
269
|
+
let outDir = '';
|
|
270
|
+
return {
|
|
271
|
+
name: 'copy-companions',
|
|
272
|
+
apply: 'build',
|
|
273
|
+
configResolved(config: ResolvedConfig) {
|
|
274
|
+
outDir = path.resolve(config.root, config.build.outDir);
|
|
275
|
+
},
|
|
276
|
+
async closeBundle() {
|
|
277
|
+
async function walk(dir: string, rel: string): Promise<void> {
|
|
278
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
279
|
+
const src = path.join(dir, entry.name);
|
|
280
|
+
if (entry.isDirectory()) {
|
|
281
|
+
await walk(src, path.join(rel, entry.name));
|
|
282
|
+
} else if (isCompanionAsset(entry.name)) {
|
|
283
|
+
const dst = path.join(outDir, rel, entry.name);
|
|
284
|
+
await mkdir(path.dirname(dst), { recursive: true });
|
|
285
|
+
await cp(src, dst);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
await walk(root, '');
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Names a build chunk after its source path, so a component lands at the autoload
|
|
296
|
+
* path (\`src/mc/app.ts\` → \`dist/mc/app.js\`). Rollup truncates chunk names at the
|
|
297
|
+
* last \`/\`, so \`info.name\` alone cannot carry a folder — the facade module id is
|
|
298
|
+
* the reliable source.
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* \`\`\`ts
|
|
302
|
+
* entryName({ name: 'mc/app', facadeModuleId: '/project/src/mc/app.ts' });
|
|
303
|
+
* entryName({ name: 'index', facadeModuleId: '/project/src/index.html' });
|
|
304
|
+
* \`\`\`
|
|
305
|
+
*
|
|
306
|
+
* @param info Rollup chunk info.
|
|
307
|
+
*/
|
|
308
|
+
function entryName(info: { name: string; facadeModuleId: string | null }): string {
|
|
309
|
+
const facade = info.facadeModuleId;
|
|
310
|
+
if (!facade || facade.endsWith('.html')) return \`\${info.name}.js\`;
|
|
311
|
+
return path.relative(sourceRoot, facade).replace(/\\\\/g, '/').replace(/\\.(ts|js)$/, '.js');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Collects every component source as a build entry, keyed by its path relative to
|
|
316
|
+
* \`src/\`. Plain \`.js\` files count too, so the rapid-prototyping path stays
|
|
317
|
+
* buildable. New components are picked up automatically — no config edit needed.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* \`\`\`ts
|
|
321
|
+
* const entries: Record<string, string> = {};
|
|
322
|
+
* await collectEntries(path.join(sourceRoot, 'mc'), 'mc', entries);
|
|
323
|
+
* \`\`\`
|
|
324
|
+
*
|
|
325
|
+
* @param dir Directory to walk.
|
|
326
|
+
* @param rel Path of that directory relative to \`src/\`.
|
|
327
|
+
* @param entries Accumulated entries, mutated in place.
|
|
328
|
+
*/
|
|
329
|
+
async function collectEntries(dir: string, rel: string, entries: Record<string, string>): Promise<void> {
|
|
330
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
331
|
+
const full = path.join(dir, entry.name);
|
|
332
|
+
if (entry.isDirectory()) {
|
|
333
|
+
await collectEntries(full, path.join(rel, entry.name), entries);
|
|
334
|
+
} else if (entry.name.endsWith('.ts') || entry.name.endsWith('.js')) {
|
|
335
|
+
entries[path.join(rel, entry.name).replace(/\\.(ts|js)$/, '')] = full;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export default defineConfig(async () => {
|
|
341
|
+
const entries: Record<string, string> = {};
|
|
342
|
+
await collectEntries(path.join(sourceRoot, namespace), namespace, entries);
|
|
343
|
+
return {
|
|
344
|
+
root: sourceRoot,
|
|
345
|
+
build: {
|
|
346
|
+
outDir: path.join(projectRoot, 'dist'),
|
|
347
|
+
emptyOutDir: true,
|
|
348
|
+
rollupOptions: {
|
|
349
|
+
input: { index: path.join(sourceRoot, 'index.html'), ...entries },
|
|
350
|
+
output: { entryFileNames: (info: { name: string; facadeModuleId: string | null }) => entryName(info) },
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
plugins: [rawAssets(sourceRoot), copyCompanions(sourceRoot)],
|
|
354
|
+
};
|
|
355
|
+
});
|
|
356
|
+
`;
|
|
357
|
+
}
|
|
358
|
+
function shell(namespace, tag) {
|
|
359
|
+
return `<!doctype html>
|
|
360
|
+
<html lang="en">
|
|
361
|
+
<head>
|
|
362
|
+
<meta charset="UTF-8" />
|
|
363
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
364
|
+
<title>${namespace} App</title>
|
|
365
|
+
<script type="module">
|
|
366
|
+
import * as m from '@twobirds/microcomponents';
|
|
367
|
+
m.autoload(true);
|
|
368
|
+
Object.assign(window, m);
|
|
369
|
+
</script>
|
|
370
|
+
</head>
|
|
371
|
+
<body>
|
|
372
|
+
<${tag}></${tag}>
|
|
373
|
+
</body>
|
|
374
|
+
</html>
|
|
375
|
+
`;
|
|
376
|
+
}
|
|
377
|
+
function appSource(namespace, tag) {
|
|
378
|
+
const className = kebabToPascal(tag);
|
|
379
|
+
return `import { DC, defineCE, parse, processAssets, type McEvent } from '@twobirds/microcomponents';
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
\`${tag}\` — the starter micro component.
|
|
383
|
+
|
|
384
|
+
@example
|
|
385
|
+
\`\`\`html
|
|
386
|
+
<${tag}></${tag}>
|
|
387
|
+
\`\`\`
|
|
388
|
+
*/
|
|
389
|
+
class ${className} extends DC {
|
|
390
|
+
static assets = ['targetHtml', 'targetCss'];
|
|
391
|
+
|
|
392
|
+
data = { title: 'Micro Components' };
|
|
393
|
+
|
|
394
|
+
// Fill the template \`{key}\` tokens once, instead of binding reactive state.
|
|
395
|
+
oneAssets(ev: McEvent<Record<string, string>>): void {
|
|
396
|
+
processAssets(this, {
|
|
397
|
+
targetHtml: parse(ev.detail.data.targetHtml, this.data),
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
defineCE('${tag}', ${className});
|
|
403
|
+
`;
|
|
404
|
+
}
|
|
405
|
+
function appHtml(namespace) {
|
|
406
|
+
return `<div class="app">
|
|
407
|
+
<h1>{title}</h1>
|
|
408
|
+
<p>Namespace scaffold ready. Edit <code>src/${namespace}/app.ts</code>.</p>
|
|
409
|
+
</div>
|
|
410
|
+
`;
|
|
411
|
+
}
|
|
412
|
+
function appCss(tag) {
|
|
413
|
+
return `${tag} {
|
|
414
|
+
display: block;
|
|
415
|
+
padding: 2rem;
|
|
416
|
+
font-family: system-ui, sans-serif;
|
|
417
|
+
}
|
|
418
|
+
`;
|
|
419
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Command dispatcher for the `mc` CLI.
|
|
3
|
+
|
|
4
|
+
Kept free of any top-level side effects so the routing can be unit-tested:
|
|
5
|
+
`index.ts` is the thin executable entry that calls {@link main}, while this
|
|
6
|
+
module only exports the logic. Commands that wrap a package.json script
|
|
7
|
+
(`clean`, `check`) are deliberately thin — they delegate to the script so the
|
|
8
|
+
script stays the single source of truth. When the current project defines no
|
|
9
|
+
such script, the CLI runs its own implementation against the current project,
|
|
10
|
+
so the command works in any consumer project.
|
|
11
|
+
|
|
12
|
+
@module CLI/Dispatch
|
|
13
|
+
*/
|
|
14
|
+
export type Runner = (script: string) => Promise<number>;
|
|
15
|
+
/**
|
|
16
|
+
Runs a package.json script through pnpm and resolves with its exit code.
|
|
17
|
+
|
|
18
|
+
Thin wrappers like `clean` and `check` delegate here so the script — not the
|
|
19
|
+
CLI — remains the single source of truth for what each command does. If the
|
|
20
|
+
current project defines no such script, the CLI runs its own implementation
|
|
21
|
+
against the current project, so the command works in any consumer project.
|
|
22
|
+
|
|
23
|
+
@example
|
|
24
|
+
```ts
|
|
25
|
+
const code = await runScript('check');
|
|
26
|
+
```
|
|
27
|
+
*/
|
|
28
|
+
export declare function runScript(script: string): Promise<number>;
|
|
29
|
+
/**
|
|
30
|
+
Parses `argv` and routes it to the matching command, returning a process exit
|
|
31
|
+
code. `run` is injectable so the script-wrapping commands can be tested without
|
|
32
|
+
spawning anything.
|
|
33
|
+
|
|
34
|
+
@example
|
|
35
|
+
```ts
|
|
36
|
+
const code = await main(['clean'], async (script) => {
|
|
37
|
+
console.log(`would run ${script}`);
|
|
38
|
+
return 0;
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
*/
|
|
42
|
+
export declare function main(argv: string[], run?: Runner): Promise<number>;
|
|
43
|
+
//# sourceMappingURL=dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/cli/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;EAYE;AAiDF,MAAM,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAmFzD;;;;;;;;;;;;EAYE;AACF,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ/D;AAED;;;;;;;;;;;;EAYE;AACF,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,GAAE,MAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0FnF"}
|