create-ampless 1.0.0-alpha.78 → 1.0.0-alpha.79
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/dist/index.js +411 -32
- package/dist/templates/plugin-local/README.md +34 -0
- package/dist/templates/plugin-local/index.ts +39 -0
- package/dist/templates/plugin-standalone/CHANGELOG.md +1 -0
- package/dist/templates/plugin-standalone/README.ja.md +43 -0
- package/dist/templates/plugin-standalone/README.md +43 -0
- package/dist/templates/plugin-standalone/package.json +47 -0
- package/dist/templates/plugin-standalone/src/index.test.ts +16 -0
- package/dist/templates/plugin-standalone/src/index.ts +29 -0
- package/dist/templates/plugin-standalone/tsconfig.json +16 -0
- package/dist/templates/plugin-standalone/tsup.config.ts +8 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { spinner as spinner2, outro as
|
|
5
|
-
import { existsSync as
|
|
6
|
-
import { basename as
|
|
4
|
+
import { spinner as spinner2, outro as outro4, log as log6 } from "@clack/prompts";
|
|
5
|
+
import { existsSync as existsSync8 } from "fs";
|
|
6
|
+
import { basename as basename4, resolve as resolve5 } from "path";
|
|
7
7
|
|
|
8
8
|
// src/prompts.ts
|
|
9
9
|
import * as p from "@clack/prompts";
|
|
@@ -106,6 +106,9 @@ function sharedTemplateDir() {
|
|
|
106
106
|
function templatePath(theme) {
|
|
107
107
|
return resolve(templatesDir, theme);
|
|
108
108
|
}
|
|
109
|
+
function pluginTemplateDir(mode) {
|
|
110
|
+
return resolve(templatesDir, mode === "local" ? "plugin-local" : "plugin-standalone");
|
|
111
|
+
}
|
|
109
112
|
|
|
110
113
|
// src/gitignore.ts
|
|
111
114
|
var DEFAULT_GITIGNORE = `# Dependencies
|
|
@@ -269,6 +272,20 @@ async function scaffold(sharedDir, themesRoot, destDir, opts) {
|
|
|
269
272
|
// src/args.ts
|
|
270
273
|
var VALID_THEMES = ["blog", "minimal", "landing", "corporate", "docs", "dads"];
|
|
271
274
|
var VALID_PLUGINS = ["seo", "rss", "webhook"];
|
|
275
|
+
var VALID_PLUGIN_TRUST_LEVELS = [
|
|
276
|
+
"untrusted",
|
|
277
|
+
"trusted",
|
|
278
|
+
"privileged"
|
|
279
|
+
];
|
|
280
|
+
var VALID_PLUGIN_CAPABILITIES = [
|
|
281
|
+
"publicHead",
|
|
282
|
+
"publicBody",
|
|
283
|
+
"metadata",
|
|
284
|
+
"eventHooks",
|
|
285
|
+
"adminSettings",
|
|
286
|
+
"writePublicAsset",
|
|
287
|
+
"schema"
|
|
288
|
+
];
|
|
272
289
|
var STRING_FLAGS = /* @__PURE__ */ new Set([
|
|
273
290
|
"--site-name",
|
|
274
291
|
"--themes",
|
|
@@ -279,7 +296,11 @@ var STRING_FLAGS = /* @__PURE__ */ new Set([
|
|
|
279
296
|
"--aws-region",
|
|
280
297
|
"--domain",
|
|
281
298
|
"--subdomain",
|
|
282
|
-
"--iam-service-role"
|
|
299
|
+
"--iam-service-role",
|
|
300
|
+
// Phase 5 `plugin <name>` flags
|
|
301
|
+
"--trust-level",
|
|
302
|
+
"--capabilities",
|
|
303
|
+
"--description"
|
|
283
304
|
]);
|
|
284
305
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
285
306
|
"--deploy",
|
|
@@ -291,7 +312,10 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
291
312
|
"--create-iam-role",
|
|
292
313
|
"--skip-confirm",
|
|
293
314
|
"--help",
|
|
294
|
-
"-h"
|
|
315
|
+
"-h",
|
|
316
|
+
// Phase 5 `plugin <name>` mode flags
|
|
317
|
+
"--local",
|
|
318
|
+
"--standalone"
|
|
295
319
|
]);
|
|
296
320
|
function parseDeployArgs(argv) {
|
|
297
321
|
const out = {
|
|
@@ -299,6 +323,7 @@ function parseDeployArgs(argv) {
|
|
|
299
323
|
mount: false,
|
|
300
324
|
upgrade: false,
|
|
301
325
|
copyTheme: false,
|
|
326
|
+
createPlugin: false,
|
|
302
327
|
dryRun: false,
|
|
303
328
|
noInstall: false,
|
|
304
329
|
githubPrivate: false,
|
|
@@ -346,6 +371,18 @@ function parseDeployArgs(argv) {
|
|
|
346
371
|
case "-h":
|
|
347
372
|
out.help = true;
|
|
348
373
|
break;
|
|
374
|
+
case "--local":
|
|
375
|
+
if (out.pluginMode === "standalone") {
|
|
376
|
+
throw new Error("Cannot combine --local and --standalone");
|
|
377
|
+
}
|
|
378
|
+
out.pluginMode = "local";
|
|
379
|
+
break;
|
|
380
|
+
case "--standalone":
|
|
381
|
+
if (out.pluginMode === "local") {
|
|
382
|
+
throw new Error("Cannot combine --local and --standalone");
|
|
383
|
+
}
|
|
384
|
+
out.pluginMode = "standalone";
|
|
385
|
+
break;
|
|
349
386
|
}
|
|
350
387
|
continue;
|
|
351
388
|
}
|
|
@@ -401,6 +438,31 @@ function parseDeployArgs(argv) {
|
|
|
401
438
|
case "--iam-service-role":
|
|
402
439
|
out.iamServiceRole = value;
|
|
403
440
|
break;
|
|
441
|
+
case "--trust-level": {
|
|
442
|
+
if (!VALID_PLUGIN_TRUST_LEVELS.includes(value)) {
|
|
443
|
+
throw new Error(
|
|
444
|
+
`Invalid --trust-level "${value}". Valid values: ${VALID_PLUGIN_TRUST_LEVELS.join(", ")}`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
out.pluginTrustLevel = value;
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
case "--capabilities": {
|
|
451
|
+
const caps = value.split(",").map((c) => c.trim()).filter(Boolean);
|
|
452
|
+
const invalid = caps.filter(
|
|
453
|
+
(c) => !VALID_PLUGIN_CAPABILITIES.includes(c)
|
|
454
|
+
);
|
|
455
|
+
if (invalid.length > 0) {
|
|
456
|
+
throw new Error(
|
|
457
|
+
`Invalid capability(ies): ${invalid.join(", ")}. Valid values: ${VALID_PLUGIN_CAPABILITIES.join(", ")}`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
out.pluginCapabilities = caps;
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
case "--description":
|
|
464
|
+
out.pluginDescription = value;
|
|
465
|
+
break;
|
|
404
466
|
}
|
|
405
467
|
continue;
|
|
406
468
|
}
|
|
@@ -424,6 +486,14 @@ function parseDeployArgs(argv) {
|
|
|
424
486
|
out.copyThemeTarget = raw;
|
|
425
487
|
continue;
|
|
426
488
|
}
|
|
489
|
+
if (raw === "plugin" && out.projectName === void 0 && !out.createPlugin && !out.upgrade && !out.copyTheme) {
|
|
490
|
+
out.createPlugin = true;
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
if (out.createPlugin && out.pluginName === void 0) {
|
|
494
|
+
out.pluginName = raw;
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
427
497
|
if (out.projectName === void 0) {
|
|
428
498
|
out.projectName = raw;
|
|
429
499
|
} else {
|
|
@@ -436,8 +506,10 @@ var HELP_TEXT = `create-ampless \u2014 scaffold an ampless project
|
|
|
436
506
|
|
|
437
507
|
Usage:
|
|
438
508
|
npx create-ampless@latest <project-name> [options]
|
|
439
|
-
npx create-ampless@latest --mount [options]
|
|
440
|
-
npx create-ampless@latest upgrade [options]
|
|
509
|
+
npx create-ampless@latest --mount [options] # in an existing project dir
|
|
510
|
+
npx create-ampless@latest upgrade [options] # in an existing project dir
|
|
511
|
+
npx create-ampless@latest copy-theme <src> <dst> # in an existing project dir
|
|
512
|
+
npx create-ampless@latest plugin <name> [options] # scaffold a plugin (Phase 5)
|
|
441
513
|
|
|
442
514
|
Options:
|
|
443
515
|
--site-name <name> Site display name (default: "My Blog")
|
|
@@ -488,6 +560,32 @@ copy-theme <source> <target>
|
|
|
488
560
|
it alone). Run inside an existing ampless project.
|
|
489
561
|
|
|
490
562
|
Example: npx create-ampless@latest copy-theme blog my-blog
|
|
563
|
+
|
|
564
|
+
plugin <name>
|
|
565
|
+
Scaffold an ampless plugin. Two modes:
|
|
566
|
+
|
|
567
|
+
--local Default. Writes plugins/<name>/index.ts inside
|
|
568
|
+
the current ampless site. The site itself is the
|
|
569
|
+
build / publish unit; the plugin file is just
|
|
570
|
+
code that sits there. Plugin name is a kebab-case
|
|
571
|
+
identifier (e.g. "site-verification").
|
|
572
|
+
|
|
573
|
+
--standalone Writes a complete npm package at ./<name>/ ready
|
|
574
|
+
for 'npm publish'. Plugin name should be the npm
|
|
575
|
+
package name (e.g. "@scope/ampless-plugin-foo"
|
|
576
|
+
or "ampless-plugin-foo"); the kebab segment
|
|
577
|
+
becomes the AmplessPlugin.name.
|
|
578
|
+
|
|
579
|
+
--trust-level <value> 'untrusted' (default), 'trusted', or 'privileged'
|
|
580
|
+
--capabilities <list> Comma-separated. Valid: publicHead, publicBody,
|
|
581
|
+
metadata, eventHooks, adminSettings,
|
|
582
|
+
writePublicAsset, schema
|
|
583
|
+
(default: publicHead,adminSettings)
|
|
584
|
+
--description "<text>" Optional one-line description
|
|
585
|
+
|
|
586
|
+
Examples:
|
|
587
|
+
npx create-ampless@latest plugin site-verification
|
|
588
|
+
npx create-ampless@latest plugin @ishinao/ampless-plugin-foo --standalone
|
|
491
589
|
`;
|
|
492
590
|
|
|
493
591
|
// src/deploy-prompts.ts
|
|
@@ -2210,12 +2308,289 @@ async function runCopyTheme(args) {
|
|
|
2210
2308
|
}
|
|
2211
2309
|
}
|
|
2212
2310
|
|
|
2213
|
-
// src/
|
|
2311
|
+
// src/plugin.ts
|
|
2312
|
+
import { cp as cp4, mkdir as mkdir3, readFile as readFile5, writeFile as writeFile5, readdir as readdir5 } from "fs/promises";
|
|
2313
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2314
|
+
import { join as join5, extname as extname3, basename as basename3 } from "path";
|
|
2315
|
+
import {
|
|
2316
|
+
log as log5,
|
|
2317
|
+
outro as outro3,
|
|
2318
|
+
text as text3,
|
|
2319
|
+
select as select2,
|
|
2320
|
+
multiselect as multiselect2,
|
|
2321
|
+
isCancel as isCancel3,
|
|
2322
|
+
cancel as cancel3
|
|
2323
|
+
} from "@clack/prompts";
|
|
2214
2324
|
import pc5 from "picocolors";
|
|
2325
|
+
var SCAFFOLD_AMPLESS_VERSION = "1.0.0-alpha.22";
|
|
2326
|
+
var TEXT_EXTENSIONS3 = /* @__PURE__ */ new Set([
|
|
2327
|
+
".json",
|
|
2328
|
+
".md",
|
|
2329
|
+
".ts",
|
|
2330
|
+
".tsx",
|
|
2331
|
+
".js",
|
|
2332
|
+
".jsx",
|
|
2333
|
+
".mjs",
|
|
2334
|
+
".cjs",
|
|
2335
|
+
".html",
|
|
2336
|
+
".css",
|
|
2337
|
+
".env",
|
|
2338
|
+
".txt",
|
|
2339
|
+
".yaml",
|
|
2340
|
+
".yml",
|
|
2341
|
+
".toml",
|
|
2342
|
+
".gitignore"
|
|
2343
|
+
]);
|
|
2344
|
+
function toKebab(s) {
|
|
2345
|
+
return s.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()).replace(/^-/, "");
|
|
2346
|
+
}
|
|
2347
|
+
function toCamelCase(kebab) {
|
|
2348
|
+
return kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
2349
|
+
}
|
|
2350
|
+
function toPascalCase(kebab) {
|
|
2351
|
+
const c = toCamelCase(kebab);
|
|
2352
|
+
return c.charAt(0).toUpperCase() + c.slice(1);
|
|
2353
|
+
}
|
|
2354
|
+
function toDisplayName(kebab) {
|
|
2355
|
+
const words = kebab.split("-");
|
|
2356
|
+
return words.map((w, i) => i === 0 ? w.charAt(0).toUpperCase() + w.slice(1) : w).join(" ");
|
|
2357
|
+
}
|
|
2358
|
+
function sanitizeForJsComment(s) {
|
|
2359
|
+
return s.replace(/\*\//g, "*\\/").replace(/[\r\n]+/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
2360
|
+
}
|
|
2361
|
+
function lastSegment(packageName) {
|
|
2362
|
+
return packageName.replace(/^@[^/]+\//, "");
|
|
2363
|
+
}
|
|
2364
|
+
function pluginNameFromPackage(packageName) {
|
|
2365
|
+
const segment = lastSegment(packageName);
|
|
2366
|
+
if (segment.startsWith("ampless-plugin-")) {
|
|
2367
|
+
return segment.slice("ampless-plugin-".length);
|
|
2368
|
+
}
|
|
2369
|
+
if (segment.startsWith("plugin-")) {
|
|
2370
|
+
return segment.slice("plugin-".length);
|
|
2371
|
+
}
|
|
2372
|
+
return segment;
|
|
2373
|
+
}
|
|
2374
|
+
async function substituteFile2(filePath, vars) {
|
|
2375
|
+
const ext = extname3(filePath) || (basename3(filePath) === ".gitignore" ? ".gitignore" : "");
|
|
2376
|
+
if (!TEXT_EXTENSIONS3.has(ext)) return;
|
|
2377
|
+
const content = await readFile5(filePath, "utf-8");
|
|
2378
|
+
const replaced = content.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`);
|
|
2379
|
+
if (replaced !== content) await writeFile5(filePath, replaced, "utf-8");
|
|
2380
|
+
}
|
|
2381
|
+
async function substituteDir2(dir, vars) {
|
|
2382
|
+
const entries = await readdir5(dir, { withFileTypes: true });
|
|
2383
|
+
await Promise.all(
|
|
2384
|
+
entries.map(async (entry) => {
|
|
2385
|
+
const fullPath = join5(dir, entry.name);
|
|
2386
|
+
if (entry.isDirectory()) {
|
|
2387
|
+
await substituteDir2(fullPath, vars);
|
|
2388
|
+
} else {
|
|
2389
|
+
await substituteFile2(fullPath, vars);
|
|
2390
|
+
}
|
|
2391
|
+
})
|
|
2392
|
+
);
|
|
2393
|
+
}
|
|
2394
|
+
var KEBAB_RE = /^[a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*$/;
|
|
2395
|
+
var PACKAGE_NAME_RE = /^(@[a-z][a-z0-9-]*\/)?[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
2396
|
+
async function runCreatePluginIn(destDir, args) {
|
|
2397
|
+
const mode = args.pluginMode ?? "local";
|
|
2398
|
+
let rawName = args.pluginName;
|
|
2399
|
+
if (!rawName) {
|
|
2400
|
+
if (args.skipConfirm === true) {
|
|
2401
|
+
throw new Error(
|
|
2402
|
+
mode === "local" ? "Plugin name is required when --skip-confirm is set. Pass it as the positional argument: `create-ampless plugin <name> --skip-confirm`." : "Plugin package name is required when --skip-confirm is set. Pass it as the positional argument: `create-ampless plugin <package-name> --standalone --skip-confirm`."
|
|
2403
|
+
);
|
|
2404
|
+
}
|
|
2405
|
+
const answer = await text3({
|
|
2406
|
+
message: mode === "local" ? 'Plugin name (kebab-case, e.g. "site-verification"):' : 'Plugin package name (e.g. "@scope/ampless-plugin-foo" or "ampless-plugin-foo"):',
|
|
2407
|
+
validate(v) {
|
|
2408
|
+
if (!v) return "Plugin name is required";
|
|
2409
|
+
const re = mode === "local" ? KEBAB_RE : PACKAGE_NAME_RE;
|
|
2410
|
+
if (!re.test(v)) {
|
|
2411
|
+
return mode === "local" ? 'Must be kebab-case starting with a lowercase letter (e.g. "site-verification")' : 'Must be a valid npm package name (e.g. "ampless-plugin-foo" or "@scope/ampless-plugin-foo")';
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
});
|
|
2415
|
+
if (isCancel3(answer)) {
|
|
2416
|
+
cancel3("Cancelled.");
|
|
2417
|
+
process.exit(0);
|
|
2418
|
+
}
|
|
2419
|
+
rawName = answer;
|
|
2420
|
+
} else {
|
|
2421
|
+
const re = mode === "local" ? KEBAB_RE : PACKAGE_NAME_RE;
|
|
2422
|
+
if (!re.test(rawName)) {
|
|
2423
|
+
throw new Error(
|
|
2424
|
+
mode === "local" ? `Invalid plugin name "${rawName}". Must be kebab-case (e.g. "site-verification").` : `Invalid plugin name "${rawName}". Must be a valid npm package name (e.g. "ampless-plugin-foo" or "@scope/ampless-plugin-foo").`
|
|
2425
|
+
);
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
const skipConfirm = args.skipConfirm === true;
|
|
2429
|
+
let trustLevel = args.pluginTrustLevel;
|
|
2430
|
+
if (!trustLevel) {
|
|
2431
|
+
if (skipConfirm) {
|
|
2432
|
+
trustLevel = "untrusted";
|
|
2433
|
+
} else {
|
|
2434
|
+
const answer = await select2({
|
|
2435
|
+
message: "Trust level:",
|
|
2436
|
+
options: VALID_PLUGIN_TRUST_LEVELS.map((t) => ({
|
|
2437
|
+
value: t,
|
|
2438
|
+
label: t,
|
|
2439
|
+
hint: t === "untrusted" ? "No special permissions (default)" : t === "trusted" ? "Can read site config" : "Full access to internal APIs"
|
|
2440
|
+
}))
|
|
2441
|
+
});
|
|
2442
|
+
if (isCancel3(answer)) {
|
|
2443
|
+
cancel3("Cancelled.");
|
|
2444
|
+
process.exit(0);
|
|
2445
|
+
}
|
|
2446
|
+
trustLevel = answer;
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
let capabilities = args.pluginCapabilities;
|
|
2450
|
+
if (!capabilities) {
|
|
2451
|
+
if (skipConfirm) {
|
|
2452
|
+
capabilities = ["publicHead", "adminSettings"];
|
|
2453
|
+
} else {
|
|
2454
|
+
const answer = await multiselect2({
|
|
2455
|
+
message: "Capabilities (space to toggle, enter to confirm):",
|
|
2456
|
+
options: VALID_PLUGIN_CAPABILITIES.map((c) => ({
|
|
2457
|
+
value: c,
|
|
2458
|
+
label: c
|
|
2459
|
+
})),
|
|
2460
|
+
initialValues: ["publicHead", "adminSettings"],
|
|
2461
|
+
required: false
|
|
2462
|
+
});
|
|
2463
|
+
if (isCancel3(answer)) {
|
|
2464
|
+
cancel3("Cancelled.");
|
|
2465
|
+
process.exit(0);
|
|
2466
|
+
}
|
|
2467
|
+
capabilities = answer;
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
const rawDescription = args.pluginDescription ?? "";
|
|
2471
|
+
const description = sanitizeForJsComment(rawDescription);
|
|
2472
|
+
const descriptionJson = JSON.stringify(rawDescription);
|
|
2473
|
+
const nameKebab = mode === "local" ? rawName : pluginNameFromPackage(rawName);
|
|
2474
|
+
const packageName = mode === "standalone" ? rawName : nameKebab;
|
|
2475
|
+
if (!KEBAB_RE.test(nameKebab)) {
|
|
2476
|
+
throw new Error(
|
|
2477
|
+
`Invalid plugin name: "${nameKebab}" (derived from "${rawName}" by stripping the npm scope and the conventional ampless-plugin- prefix) is not valid kebab-case. Each segment must start with a letter and contain only letters and digits. Examples: "site-verification", "image-zoom".`
|
|
2478
|
+
);
|
|
2479
|
+
}
|
|
2480
|
+
const nameCamelCase = toCamelCase(nameKebab);
|
|
2481
|
+
const NamePascalCase = toPascalCase(nameKebab);
|
|
2482
|
+
const DisplayName = toDisplayName(nameKebab);
|
|
2483
|
+
void toKebab;
|
|
2484
|
+
let outputDir;
|
|
2485
|
+
if (mode === "local") {
|
|
2486
|
+
const problem = validateMountableProject(destDir);
|
|
2487
|
+
if (problem) {
|
|
2488
|
+
throw new Error(problem);
|
|
2489
|
+
}
|
|
2490
|
+
outputDir = join5(destDir, "plugins", nameKebab);
|
|
2491
|
+
if (existsSync7(outputDir)) {
|
|
2492
|
+
throw new Error(
|
|
2493
|
+
`Plugin directory already exists: plugins/${nameKebab}/
|
|
2494
|
+
Remove it first or choose a different name.`
|
|
2495
|
+
);
|
|
2496
|
+
}
|
|
2497
|
+
} else {
|
|
2498
|
+
const nameSegment = lastSegment(rawName);
|
|
2499
|
+
outputDir = join5(destDir, nameSegment);
|
|
2500
|
+
if (existsSync7(outputDir)) {
|
|
2501
|
+
throw new Error(
|
|
2502
|
+
`Directory already exists: ${nameSegment}/
|
|
2503
|
+
Remove it first or choose a different name.`
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
const templateDir = pluginTemplateDir(mode);
|
|
2508
|
+
if (!existsSync7(templateDir)) {
|
|
2509
|
+
throw new Error(
|
|
2510
|
+
`Plugin template not found: ${templateDir}
|
|
2511
|
+
The ${mode === "local" ? "plugin-local" : "plugin-standalone"} template directory has not been added to the repo yet. Run \`git pull\` to pick up the latest templates.`
|
|
2512
|
+
);
|
|
2513
|
+
}
|
|
2514
|
+
await mkdir3(outputDir, { recursive: true });
|
|
2515
|
+
await cp4(templateDir, outputDir, { recursive: true });
|
|
2516
|
+
const capabilitiesList = capabilities && capabilities.length > 0 ? capabilities.map((c) => `'${c}'`).join(", ") : "";
|
|
2517
|
+
const capabilitiesJsonArray = capabilities && capabilities.length > 0 ? capabilities.map((c) => `"${c}"`).join(", ") : "";
|
|
2518
|
+
const vars = {
|
|
2519
|
+
// Use camelCase keys so they survive substituteVars' `\w+` regex.
|
|
2520
|
+
// Templates reference them as `{{nameKebab}}` (not `{{name-kebab}}`).
|
|
2521
|
+
nameKebab,
|
|
2522
|
+
nameCamelCase,
|
|
2523
|
+
NameCamelCase: NamePascalCase,
|
|
2524
|
+
packageName,
|
|
2525
|
+
description,
|
|
2526
|
+
descriptionJson,
|
|
2527
|
+
trustLevel: trustLevel ?? "untrusted",
|
|
2528
|
+
capabilitiesList,
|
|
2529
|
+
capabilitiesJsonArray,
|
|
2530
|
+
DisplayName,
|
|
2531
|
+
displayNameJa: DisplayName,
|
|
2532
|
+
// same as English for now; maintainers can localise
|
|
2533
|
+
amplessVersion: SCAFFOLD_AMPLESS_VERSION
|
|
2534
|
+
};
|
|
2535
|
+
await substituteDir2(outputDir, vars);
|
|
2536
|
+
return {
|
|
2537
|
+
mode,
|
|
2538
|
+
outputDir,
|
|
2539
|
+
pluginName: nameKebab,
|
|
2540
|
+
packageName: mode === "standalone" ? packageName : void 0
|
|
2541
|
+
};
|
|
2542
|
+
}
|
|
2543
|
+
async function runCreatePlugin(args) {
|
|
2544
|
+
const destDir = process.cwd();
|
|
2545
|
+
try {
|
|
2546
|
+
const result = await runCreatePluginIn(destDir, args);
|
|
2547
|
+
if (result.mode === "local") {
|
|
2548
|
+
const rel = `plugins/${result.pluginName}/`;
|
|
2549
|
+
const factoryName = `${toCamelCase(result.pluginName)}Plugin`;
|
|
2550
|
+
outro3(
|
|
2551
|
+
`${pc5.green("\u2714")} Plugin scaffolded at ${pc5.bold(rel)}
|
|
2552
|
+
|
|
2553
|
+
Register it in ${pc5.cyan("cms.config.ts")}:
|
|
2554
|
+
|
|
2555
|
+
import ${factoryName} from './plugins/${result.pluginName}'
|
|
2556
|
+
|
|
2557
|
+
export default defineConfig({
|
|
2558
|
+
// ...
|
|
2559
|
+
plugins: [
|
|
2560
|
+
${factoryName}(),
|
|
2561
|
+
],
|
|
2562
|
+
})
|
|
2563
|
+
|
|
2564
|
+
Then restart the dev server to pick up the new plugin.`
|
|
2565
|
+
);
|
|
2566
|
+
} else {
|
|
2567
|
+
const dirName = result.packageName ? lastSegment(result.packageName) : result.pluginName;
|
|
2568
|
+
outro3(
|
|
2569
|
+
`${pc5.green("\u2714")} Standalone plugin scaffolded at ${pc5.bold(dirName + "/")}
|
|
2570
|
+
|
|
2571
|
+
Next steps:
|
|
2572
|
+
${pc5.cyan(`cd ${dirName}`)}
|
|
2573
|
+
${pc5.cyan("pnpm install")}
|
|
2574
|
+
${pc5.cyan("pnpm test")}
|
|
2575
|
+
${pc5.cyan("pnpm build")}
|
|
2576
|
+
${pc5.cyan("pnpm publish --access public --tag alpha")}
|
|
2577
|
+
|
|
2578
|
+
Then add the published package to your ampless site:
|
|
2579
|
+
${pc5.cyan(`pnpm add ${result.packageName ?? result.pluginName}`)}`
|
|
2580
|
+
);
|
|
2581
|
+
}
|
|
2582
|
+
} catch (err) {
|
|
2583
|
+
log5.error(err instanceof Error ? err.message : String(err));
|
|
2584
|
+
process.exit(1);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
// src/index.ts
|
|
2589
|
+
import pc6 from "picocolors";
|
|
2215
2590
|
var DEFAULT_THEMES = VALID_THEMES;
|
|
2216
2591
|
function buildNonInteractiveOpts(args) {
|
|
2217
2592
|
const projectName = args.projectName ?? (() => {
|
|
2218
|
-
const b =
|
|
2593
|
+
const b = basename4(process.cwd());
|
|
2219
2594
|
return b && b !== "/" ? b : "my-ampless-site";
|
|
2220
2595
|
})();
|
|
2221
2596
|
const siteName = args.siteName ?? "My Blog";
|
|
@@ -2236,17 +2611,17 @@ function warnIgnoredScaffoldFlags(args) {
|
|
|
2236
2611
|
if (args.plugins) ignored.push("--plugins");
|
|
2237
2612
|
if (args.projectName) ignored.push("<project-name> positional");
|
|
2238
2613
|
if (ignored.length > 0) {
|
|
2239
|
-
|
|
2614
|
+
log6.warn(`--mount mode ignores: ${ignored.join(", ")}`);
|
|
2240
2615
|
}
|
|
2241
2616
|
}
|
|
2242
2617
|
async function runMount(args) {
|
|
2243
2618
|
const destDir = process.cwd();
|
|
2244
|
-
const projectName =
|
|
2619
|
+
const projectName = basename4(destDir);
|
|
2245
2620
|
warnIgnoredScaffoldFlags(args);
|
|
2246
2621
|
const problem = validateMountableProject(destDir);
|
|
2247
2622
|
if (problem) {
|
|
2248
|
-
|
|
2249
|
-
|
|
2623
|
+
log6.error(problem);
|
|
2624
|
+
log6.info(
|
|
2250
2625
|
"Run `npx create-ampless@latest <name>` first to scaffold, or `cd` into a scaffolded project before passing --mount."
|
|
2251
2626
|
);
|
|
2252
2627
|
process.exit(1);
|
|
@@ -2260,23 +2635,23 @@ async function runMount(args) {
|
|
|
2260
2635
|
if (err instanceof PreflightError) {
|
|
2261
2636
|
process.exit(1);
|
|
2262
2637
|
}
|
|
2263
|
-
|
|
2638
|
+
log6.error(err instanceof Error ? err.message : String(err));
|
|
2264
2639
|
process.exit(1);
|
|
2265
2640
|
}
|
|
2266
2641
|
}
|
|
2267
2642
|
function printDeployResult(result) {
|
|
2268
2643
|
const lines = [
|
|
2269
|
-
`${
|
|
2644
|
+
`${pc6.green("\u2714")} Project deployed`,
|
|
2270
2645
|
``,
|
|
2271
|
-
` GitHub: ${
|
|
2272
|
-
` Amplify app: ${
|
|
2273
|
-
` Amplify URL: ${
|
|
2646
|
+
` GitHub: ${pc6.cyan(result.githubRepoUrl)}`,
|
|
2647
|
+
` Amplify app: ${pc6.cyan(result.amplifyAppId)}`,
|
|
2648
|
+
` Amplify URL: ${pc6.cyan(result.amplifyAppUrl)}`
|
|
2274
2649
|
];
|
|
2275
2650
|
if (result.domainUrl) {
|
|
2276
|
-
lines.push(` Custom domain: ${
|
|
2651
|
+
lines.push(` Custom domain: ${pc6.cyan(result.domainUrl)}`);
|
|
2277
2652
|
}
|
|
2278
2653
|
if (result.domainVerification && result.domainVerification.length > 0) {
|
|
2279
|
-
lines.push("", ` ${
|
|
2654
|
+
lines.push("", ` ${pc6.bold("Add these DNS records to verify the domain:")}`);
|
|
2280
2655
|
for (const v of result.domainVerification) {
|
|
2281
2656
|
lines.push(` ${v.cname} CNAME ${v.target}`);
|
|
2282
2657
|
}
|
|
@@ -2284,9 +2659,9 @@ function printDeployResult(result) {
|
|
|
2284
2659
|
lines.push(
|
|
2285
2660
|
"",
|
|
2286
2661
|
` First build is now running in Amplify Hosting.`,
|
|
2287
|
-
` Watch it at ${
|
|
2662
|
+
` Watch it at ${pc6.cyan(`https://console.aws.amazon.com/amplify/home#/${result.amplifyAppId}`)}`
|
|
2288
2663
|
);
|
|
2289
|
-
|
|
2664
|
+
outro4(lines.join("\n"));
|
|
2290
2665
|
}
|
|
2291
2666
|
async function main() {
|
|
2292
2667
|
const args = parseDeployArgs(process.argv.slice(2));
|
|
@@ -2295,7 +2670,7 @@ async function main() {
|
|
|
2295
2670
|
return;
|
|
2296
2671
|
}
|
|
2297
2672
|
for (const flag of args.unknown) {
|
|
2298
|
-
|
|
2673
|
+
log6.warn(`Unknown argument ignored: ${flag}`);
|
|
2299
2674
|
}
|
|
2300
2675
|
if (args.upgrade) {
|
|
2301
2676
|
await runUpgrade(args);
|
|
@@ -2305,6 +2680,10 @@ async function main() {
|
|
|
2305
2680
|
await runCopyTheme(args);
|
|
2306
2681
|
return;
|
|
2307
2682
|
}
|
|
2683
|
+
if (args.createPlugin) {
|
|
2684
|
+
await runCreatePlugin(args);
|
|
2685
|
+
return;
|
|
2686
|
+
}
|
|
2308
2687
|
if (args.mount) {
|
|
2309
2688
|
await runMount(args);
|
|
2310
2689
|
return;
|
|
@@ -2317,8 +2696,8 @@ async function main() {
|
|
|
2317
2696
|
}
|
|
2318
2697
|
if (!opts) return;
|
|
2319
2698
|
const destDir = resolve5(process.cwd(), opts.projectName);
|
|
2320
|
-
if (
|
|
2321
|
-
|
|
2699
|
+
if (existsSync8(destDir)) {
|
|
2700
|
+
log6.error(`Directory already exists: ${destDir}`);
|
|
2322
2701
|
process.exit(1);
|
|
2323
2702
|
}
|
|
2324
2703
|
const sharedDir = sharedTemplateDir();
|
|
@@ -2329,7 +2708,7 @@ async function main() {
|
|
|
2329
2708
|
s.stop("Done!");
|
|
2330
2709
|
} catch (err) {
|
|
2331
2710
|
s.stop("Failed.");
|
|
2332
|
-
|
|
2711
|
+
log6.error(String(err));
|
|
2333
2712
|
process.exit(1);
|
|
2334
2713
|
}
|
|
2335
2714
|
if (args.deploy) {
|
|
@@ -2342,18 +2721,18 @@ async function main() {
|
|
|
2342
2721
|
if (err instanceof PreflightError) {
|
|
2343
2722
|
process.exit(1);
|
|
2344
2723
|
}
|
|
2345
|
-
|
|
2724
|
+
log6.error(err instanceof Error ? err.message : String(err));
|
|
2346
2725
|
process.exit(1);
|
|
2347
2726
|
}
|
|
2348
2727
|
return;
|
|
2349
2728
|
}
|
|
2350
|
-
|
|
2351
|
-
`${
|
|
2729
|
+
outro4(
|
|
2730
|
+
`${pc6.green("\u2714")} Project created at ${pc6.bold(opts.projectName)}
|
|
2352
2731
|
|
|
2353
2732
|
Next steps:
|
|
2354
|
-
${
|
|
2355
|
-
${
|
|
2356
|
-
${
|
|
2733
|
+
${pc6.cyan("cd")} ${opts.projectName}
|
|
2734
|
+
${pc6.cyan("npm install")}
|
|
2735
|
+
${pc6.cyan("npm run sandbox")} ${pc6.dim("# deploy sandbox + start Next.js")}`
|
|
2357
2736
|
);
|
|
2358
2737
|
}
|
|
2359
2738
|
main().catch((err) => {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# {{nameKebab}}
|
|
2
|
+
|
|
3
|
+
{{description}}
|
|
4
|
+
|
|
5
|
+
Site-local plugin in `plugins/{{nameKebab}}/`. This directory is
|
|
6
|
+
user-owned: `update-ampless` never touches it.
|
|
7
|
+
|
|
8
|
+
## Register
|
|
9
|
+
|
|
10
|
+
Add to `cms.config.ts`:
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import {{nameCamelCase}}Plugin from './plugins/{{nameKebab}}'
|
|
14
|
+
|
|
15
|
+
export default defineConfig({
|
|
16
|
+
// ...
|
|
17
|
+
plugins: [
|
|
18
|
+
{{nameCamelCase}}Plugin(),
|
|
19
|
+
],
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Restart `next dev` and visit `/admin/plugins` to configure.
|
|
24
|
+
|
|
25
|
+
## Settings
|
|
26
|
+
|
|
27
|
+
(none yet — declare via `settings.public` in `index.ts`)
|
|
28
|
+
|
|
29
|
+
## Notes
|
|
30
|
+
|
|
31
|
+
- Trust level: `{{trustLevel}}`
|
|
32
|
+
- Capabilities: `{{capabilitiesList}}`
|
|
33
|
+
- When this plugin grows useful for more than one site, lift it into
|
|
34
|
+
its own npm package via `npx create-ampless plugin <name> --standalone`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { definePlugin, type AmplessPlugin } from 'ampless'
|
|
2
|
+
|
|
3
|
+
export interface {{NameCamelCase}}Options {
|
|
4
|
+
/** Optional namespace when registering multiple instances. */
|
|
5
|
+
instanceId?: string
|
|
6
|
+
// TODO: add the constructor options your plugin needs.
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* {{description}}
|
|
11
|
+
*
|
|
12
|
+
* Site-local plugin. Add to your `cms.config.ts`:
|
|
13
|
+
*
|
|
14
|
+
* import {{nameCamelCase}}Plugin from './plugins/{{nameKebab}}'
|
|
15
|
+
*
|
|
16
|
+
* export default defineConfig({
|
|
17
|
+
* // ...
|
|
18
|
+
* plugins: [
|
|
19
|
+
* {{nameCamelCase}}Plugin(),
|
|
20
|
+
* ],
|
|
21
|
+
* })
|
|
22
|
+
*/
|
|
23
|
+
export default function {{nameCamelCase}}Plugin(
|
|
24
|
+
options: {{NameCamelCase}}Options = {}
|
|
25
|
+
): AmplessPlugin {
|
|
26
|
+
const instanceId = options.instanceId ?? '{{nameKebab}}'
|
|
27
|
+
return definePlugin({
|
|
28
|
+
name: '{{nameKebab}}',
|
|
29
|
+
instanceId,
|
|
30
|
+
apiVersion: 1,
|
|
31
|
+
trust_level: '{{trustLevel}}',
|
|
32
|
+
displayName: { en: '{{DisplayName}}', ja: '{{displayNameJa}}' },
|
|
33
|
+
capabilities: [{{capabilitiesList}}],
|
|
34
|
+
// TODO: implement the surfaces declared in `capabilities`. The
|
|
35
|
+
// most common starting point is `publicHead(ctx)` returning a
|
|
36
|
+
// descriptor array. See the plugin author guide:
|
|
37
|
+
// https://github.com/heavymoons/ampless/blob/main/packages/ampless/docs/plugin-author-guide.md
|
|
38
|
+
})
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# {{packageName}}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
> English: [README.md](./README.md)
|
|
2
|
+
|
|
3
|
+
# {{packageName}}
|
|
4
|
+
|
|
5
|
+
> [Pre-release / alpha] {{description}}
|
|
6
|
+
|
|
7
|
+
## インストール
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install {{packageName}}@alpha
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 設定
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// cms.config.ts
|
|
17
|
+
import { defineConfig } from 'ampless'
|
|
18
|
+
import {{nameCamelCase}}Plugin from '{{packageName}}'
|
|
19
|
+
|
|
20
|
+
export default defineConfig({
|
|
21
|
+
// ...
|
|
22
|
+
plugins: [
|
|
23
|
+
{{nameCamelCase}}Plugin(),
|
|
24
|
+
],
|
|
25
|
+
})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`/admin/plugins` で設定。
|
|
29
|
+
|
|
30
|
+
## Trust level
|
|
31
|
+
|
|
32
|
+
`{{trustLevel}}`。Capabilities: `{{capabilitiesList}}`。
|
|
33
|
+
|
|
34
|
+
## Publish
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm install
|
|
38
|
+
pnpm test
|
|
39
|
+
pnpm build
|
|
40
|
+
pnpm publish --access public --tag alpha
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
API 詳細は [ampless plugin author guide](https://github.com/heavymoons/ampless/blob/main/packages/ampless/docs/plugin-author-guide.md) を参照。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
> 日本語版: [README.ja.md](./README.ja.md)
|
|
2
|
+
|
|
3
|
+
# {{packageName}}
|
|
4
|
+
|
|
5
|
+
> [Pre-release / alpha] {{description}}
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install {{packageName}}@alpha
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Configure
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// cms.config.ts
|
|
17
|
+
import { defineConfig } from 'ampless'
|
|
18
|
+
import {{nameCamelCase}}Plugin from '{{packageName}}'
|
|
19
|
+
|
|
20
|
+
export default defineConfig({
|
|
21
|
+
// ...
|
|
22
|
+
plugins: [
|
|
23
|
+
{{nameCamelCase}}Plugin(),
|
|
24
|
+
],
|
|
25
|
+
})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then go to `/admin/plugins` to configure.
|
|
29
|
+
|
|
30
|
+
## Trust level
|
|
31
|
+
|
|
32
|
+
`{{trustLevel}}`. Capabilities: `{{capabilitiesList}}`.
|
|
33
|
+
|
|
34
|
+
## Publishing
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm install
|
|
38
|
+
pnpm test
|
|
39
|
+
pnpm build
|
|
40
|
+
pnpm publish --access public --tag alpha
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
See the [ampless plugin author guide](https://github.com/heavymoons/ampless/blob/main/packages/ampless/docs/plugin-author-guide.md) for the full API.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{packageName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": {{descriptionJson}},
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch",
|
|
24
|
+
"lint": "tsc --noEmit",
|
|
25
|
+
"test": "vitest run --passWithNoTests"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"ampless": "^{{amplessVersion}}"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"tsup": "^8.5.1",
|
|
32
|
+
"typescript": "^5.7.0",
|
|
33
|
+
"vitest": "^4.1.7"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"ampless",
|
|
37
|
+
"plugin",
|
|
38
|
+
"ampless-plugin"
|
|
39
|
+
],
|
|
40
|
+
"amplessPlugin": {
|
|
41
|
+
"apiVersion": 1,
|
|
42
|
+
"name": "{{nameKebab}}",
|
|
43
|
+
"trustLevel": "{{trustLevel}}",
|
|
44
|
+
"capabilities": [{{capabilitiesJsonArray}}],
|
|
45
|
+
"displayName": { "en": "{{DisplayName}}", "ja": "{{displayNameJa}}" }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import {{nameCamelCase}}Plugin from './index.js'
|
|
3
|
+
|
|
4
|
+
describe('{{nameCamelCase}}Plugin', () => {
|
|
5
|
+
it('returns a plugin with the declared name + apiVersion', () => {
|
|
6
|
+
const plugin = {{nameCamelCase}}Plugin()
|
|
7
|
+
expect(plugin.name).toBe('{{nameKebab}}')
|
|
8
|
+
expect(plugin.apiVersion).toBe(1)
|
|
9
|
+
expect(plugin.trust_level).toBe('{{trustLevel}}')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('honors an explicit instanceId', () => {
|
|
13
|
+
const plugin = {{nameCamelCase}}Plugin({ instanceId: 'custom-instance' })
|
|
14
|
+
expect(plugin.instanceId).toBe('custom-instance')
|
|
15
|
+
})
|
|
16
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { definePlugin, type AmplessPlugin } from 'ampless'
|
|
2
|
+
|
|
3
|
+
export interface {{NameCamelCase}}Options {
|
|
4
|
+
/** Optional namespace when registering multiple instances. */
|
|
5
|
+
instanceId?: string
|
|
6
|
+
// TODO: add the constructor options your plugin needs.
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* {{description}}
|
|
11
|
+
*/
|
|
12
|
+
export default function {{nameCamelCase}}Plugin(
|
|
13
|
+
options: {{NameCamelCase}}Options = {}
|
|
14
|
+
): AmplessPlugin {
|
|
15
|
+
const instanceId = options.instanceId ?? '{{nameKebab}}'
|
|
16
|
+
return definePlugin({
|
|
17
|
+
name: '{{nameKebab}}',
|
|
18
|
+
packageName: '{{packageName}}',
|
|
19
|
+
instanceId,
|
|
20
|
+
apiVersion: 1,
|
|
21
|
+
trust_level: '{{trustLevel}}',
|
|
22
|
+
displayName: { en: '{{DisplayName}}', ja: '{{displayNameJa}}' },
|
|
23
|
+
capabilities: [{{capabilitiesList}}],
|
|
24
|
+
// TODO: implement the surfaces declared in `capabilities`. The
|
|
25
|
+
// most common starting point is `publicHead(ctx)` returning a
|
|
26
|
+
// descriptor array. See:
|
|
27
|
+
// https://github.com/heavymoons/ampless/blob/main/packages/ampless/docs/plugin-author-guide.md
|
|
28
|
+
})
|
|
29
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2022", "DOM"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"rootDir": "src",
|
|
13
|
+
"verbatimModuleSyntax": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"]
|
|
16
|
+
}
|