carboncanvas-cli 0.1.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/CONTRACT.md +94 -0
- package/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/build.js +32 -0
- package/dist/build.js.map +1 -0
- package/dist/canvasConfig.js +19 -0
- package/dist/canvasConfig.js.map +1 -0
- package/dist/constants.js +16 -0
- package/dist/constants.js.map +1 -0
- package/dist/detect.js +80 -0
- package/dist/detect.js.map +1 -0
- package/dist/errors.js +11 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.js +160 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.js +25 -0
- package/dist/logger.js.map +1 -0
- package/dist/loginCommand.js +33 -0
- package/dist/loginCommand.js.map +1 -0
- package/dist/names.js +87 -0
- package/dist/names.js.map +1 -0
- package/dist/output.js +88 -0
- package/dist/output.js.map +1 -0
- package/dist/pack.js +65 -0
- package/dist/pack.js.map +1 -0
- package/dist/prompt.js +38 -0
- package/dist/prompt.js.map +1 -0
- package/dist/publishCommand.js +129 -0
- package/dist/publishCommand.js.map +1 -0
- package/dist/stamp.js +67 -0
- package/dist/stamp.js.map +1 -0
- package/dist/tokenStore.js +67 -0
- package/dist/tokenStore.js.map +1 -0
- package/dist/upload.js +83 -0
- package/dist/upload.js.map +1 -0
- package/dist/verify.js +25 -0
- package/dist/verify.js.map +1 -0
- package/dist/walk.js +44 -0
- package/dist/walk.js.map +1 -0
- package/package.json +42 -0
package/dist/names.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Name-preservation check (plan §6.2 / amendment B.5).
|
|
2
|
+
//
|
|
3
|
+
// The Inspector's pitch is *real component names* off the live fibers, but
|
|
4
|
+
// `vite build` mangles function names unless `esbuild.keepNames` is set. We
|
|
5
|
+
// sample a few PascalCase component names from the project's src/ and grep the
|
|
6
|
+
// built JS: if they're gone, the Inspector will show minified names — warn loudly
|
|
7
|
+
// and point at the fix. This never blocks publishing.
|
|
8
|
+
import { promises as fs } from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { walkDir, readBundleText } from './walk.js';
|
|
11
|
+
async function isDir(p) {
|
|
12
|
+
try {
|
|
13
|
+
return (await fs.stat(p)).isDirectory();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// Extract PascalCase component-ish identifiers from source: default/named
|
|
20
|
+
// function+const exports and class/function declarations.
|
|
21
|
+
export function extractComponentNames(source) {
|
|
22
|
+
const names = new Set();
|
|
23
|
+
const patterns = [
|
|
24
|
+
/export\s+default\s+function\s+([A-Z][A-Za-z0-9]+)/g,
|
|
25
|
+
/export\s+function\s+([A-Z][A-Za-z0-9]+)/g,
|
|
26
|
+
/export\s+const\s+([A-Z][A-Za-z0-9]+)\s*[:=]/g,
|
|
27
|
+
/^\s*function\s+([A-Z][A-Za-z0-9]+)/gm,
|
|
28
|
+
/^\s*class\s+([A-Z][A-Za-z0-9]+)/gm,
|
|
29
|
+
];
|
|
30
|
+
for (const re of patterns) {
|
|
31
|
+
let m;
|
|
32
|
+
while ((m = re.exec(source)) !== null) {
|
|
33
|
+
// Skip 1-char and all-caps constants like `URL`, keep real component names.
|
|
34
|
+
const name = m[1];
|
|
35
|
+
if (name.length >= 3 && /[a-z]/.test(name))
|
|
36
|
+
names.add(name);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return [...names];
|
|
40
|
+
}
|
|
41
|
+
// Sample up to `limit` distinct component names from src/**/*.{jsx,tsx}.
|
|
42
|
+
export async function sampleComponentNames(projectRoot, limit = 8) {
|
|
43
|
+
const srcDir = path.join(projectRoot, 'src');
|
|
44
|
+
const scanRoot = (await isDir(srcDir)) ? srcDir : projectRoot;
|
|
45
|
+
const files = (await walkDir(scanRoot)).filter((f) => /\.(jsx|tsx)$/.test(f.rel));
|
|
46
|
+
const found = new Set();
|
|
47
|
+
for (const f of files) {
|
|
48
|
+
const src = await fs.readFile(f.abs, 'utf8');
|
|
49
|
+
for (const n of extractComponentNames(src)) {
|
|
50
|
+
found.add(n);
|
|
51
|
+
if (found.size >= limit)
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
if (found.size >= limit)
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
return [...found];
|
|
58
|
+
}
|
|
59
|
+
// Check whether sampled names survive as whole-word identifiers in the bundle.
|
|
60
|
+
export function checkNamePreservationInText(sampled, bundleText) {
|
|
61
|
+
if (sampled.length === 0) {
|
|
62
|
+
return { sampled, preserved: [], missing: [], likelyMinified: false, checked: false };
|
|
63
|
+
}
|
|
64
|
+
const preserved = [];
|
|
65
|
+
const missing = [];
|
|
66
|
+
for (const name of sampled) {
|
|
67
|
+
// Whole-word match so `Foo` doesn't match inside `Foobar`/minified soup.
|
|
68
|
+
const re = new RegExp(`\\b${name}\\b`);
|
|
69
|
+
if (re.test(bundleText))
|
|
70
|
+
preserved.push(name);
|
|
71
|
+
else
|
|
72
|
+
missing.push(name);
|
|
73
|
+
}
|
|
74
|
+
// Majority gone → the keepNames preset likely wasn't applied.
|
|
75
|
+
const likelyMinified = missing.length > preserved.length;
|
|
76
|
+
return { sampled, preserved, missing, likelyMinified, checked: true };
|
|
77
|
+
}
|
|
78
|
+
export async function checkNamePreservation(projectRoot, distDir) {
|
|
79
|
+
const sampled = await sampleComponentNames(projectRoot);
|
|
80
|
+
const bundleText = await readBundleText(distDir, ['.js', '.mjs', '.cjs']);
|
|
81
|
+
return checkNamePreservationInText(sampled, bundleText);
|
|
82
|
+
}
|
|
83
|
+
export const MINIFIED_NAMES_WARNING = 'Built JS appears to have MINIFIED component names — the Inspector will show ' +
|
|
84
|
+
'names like `t`/`Bn` instead of your real components. Add `esbuild: { keepNames: true }` ' +
|
|
85
|
+
'to your vite.config (or spread the Carbon Canvas vite preset when it lands), then rebuild. ' +
|
|
86
|
+
'Publishing anyway.';
|
|
87
|
+
//# sourceMappingURL=names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"names.js","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"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"}
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Resolve the built output directory and validate it is a real static bundle.
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
async function isDir(p) {
|
|
5
|
+
try {
|
|
6
|
+
return (await fs.stat(p)).isDirectory();
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
async function pathExists(p) {
|
|
13
|
+
try {
|
|
14
|
+
await fs.access(p);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Framework default output dir names, in preference order.
|
|
22
|
+
const FRAMEWORK_DIRS = {
|
|
23
|
+
vite: ['dist'],
|
|
24
|
+
cra: ['build'],
|
|
25
|
+
astro: ['dist'],
|
|
26
|
+
sveltekit: ['build'],
|
|
27
|
+
unknown: [],
|
|
28
|
+
};
|
|
29
|
+
// Common fallbacks scanned when the framework is unknown / its default is absent.
|
|
30
|
+
const FALLBACK_DIRS = ['dist', 'build', 'out', 'public'];
|
|
31
|
+
// Find the output dir. `explicitDist` (from --dist) short-circuits everything.
|
|
32
|
+
// Otherwise try the framework default, then common fallbacks — returning the
|
|
33
|
+
// first that exists as a directory. Returns null if nothing plausible is found.
|
|
34
|
+
export async function findOutputDir(root, framework, explicitDist) {
|
|
35
|
+
if (explicitDist) {
|
|
36
|
+
const abs = path.isAbsolute(explicitDist) ? explicitDist : path.join(root, explicitDist);
|
|
37
|
+
return (await isDir(abs)) ? abs : null;
|
|
38
|
+
}
|
|
39
|
+
const candidates = [...FRAMEWORK_DIRS[framework], ...FALLBACK_DIRS];
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
for (const c of candidates) {
|
|
42
|
+
if (seen.has(c))
|
|
43
|
+
continue;
|
|
44
|
+
seen.add(c);
|
|
45
|
+
const abs = path.join(root, c);
|
|
46
|
+
if (await isDir(abs))
|
|
47
|
+
return abs;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
// Server-runtime manifests that mean "this is NOT a static bundle" (plan §6.1 /
|
|
52
|
+
// B.3). We look inside the dist dir AND at the project root, because a Next/Nuxt
|
|
53
|
+
// build scatters these across both.
|
|
54
|
+
const SERVER_MANIFESTS_IN_DIST = ['functions', 'server.js', 'server.mjs', '_worker.js'];
|
|
55
|
+
const SERVER_MANIFESTS_IN_ROOT = [
|
|
56
|
+
path.join('.next', 'standalone'),
|
|
57
|
+
path.join('.output', 'server'),
|
|
58
|
+
path.join('.svelte-kit', 'output', 'server'),
|
|
59
|
+
];
|
|
60
|
+
export const NON_STATIC_MESSAGE = 'This looks like it needs a server. `publish` covers static builds only — ' +
|
|
61
|
+
'deploy it yourself (Vercel/Netlify/your host) and point Carbon Canvas at the URL.';
|
|
62
|
+
// Validate the dist dir is a static bundle: index.html at the root, and no
|
|
63
|
+
// server manifest present. `projectRoot` lets us catch root-level manifests
|
|
64
|
+
// (defaults to the dist dir's parent).
|
|
65
|
+
export async function validateStatic(distDir, projectRoot) {
|
|
66
|
+
const root = projectRoot ?? path.dirname(distDir);
|
|
67
|
+
// Refuse first on server manifests, so a build that emitted BOTH an index.html
|
|
68
|
+
// and a server bundle (SSR-with-prerender) is still correctly refused.
|
|
69
|
+
for (const m of SERVER_MANIFESTS_IN_DIST) {
|
|
70
|
+
if (await pathExists(path.join(distDir, m))) {
|
|
71
|
+
return { ok: false, reason: NON_STATIC_MESSAGE, serverManifest: m };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
for (const m of SERVER_MANIFESTS_IN_ROOT) {
|
|
75
|
+
if (await pathExists(path.join(root, m))) {
|
|
76
|
+
return { ok: false, reason: NON_STATIC_MESSAGE, serverManifest: m };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!(await pathExists(path.join(distDir, 'index.html')))) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
reason: `No index.html at the root of "${distDir}". ` +
|
|
83
|
+
'A static bundle must have an index.html entry — pass --dist to point at the right directory.',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return { ok: true };
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
|
package/dist/pack.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Package the static output into a .tar.gz, respecting the size caps and
|
|
2
|
+
// stamping the version meta into the PACKAGED copy of index.html (never the
|
|
3
|
+
// user's file). Structured as small testable pieces + one orchestrator.
|
|
4
|
+
import { promises as fs } from 'node:fs';
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import { create as tarCreate } from 'tar';
|
|
8
|
+
import { walkDir } from './walk.js';
|
|
9
|
+
import { stampMeta } from './stamp.js';
|
|
10
|
+
import { MAX_BUNDLE_BYTES, MAX_BUNDLE_FILES } from './constants.js';
|
|
11
|
+
export class CapError extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'CapError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export const DEFAULT_CAPS = {
|
|
18
|
+
maxBytes: MAX_BUNDLE_BYTES,
|
|
19
|
+
maxFiles: MAX_BUNDLE_FILES,
|
|
20
|
+
};
|
|
21
|
+
function humanBytes(n) {
|
|
22
|
+
if (n >= 1024 * 1024)
|
|
23
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
24
|
+
if (n >= 1024)
|
|
25
|
+
return `${(n / 1024).toFixed(1)} KB`;
|
|
26
|
+
return `${n} B`;
|
|
27
|
+
}
|
|
28
|
+
// Enforce the caps on a walked file list. Throws CapError with the exact limit.
|
|
29
|
+
export function enforceCaps(files, caps = DEFAULT_CAPS) {
|
|
30
|
+
if (files.length > caps.maxFiles) {
|
|
31
|
+
throw new CapError(`Too many files: ${files.length} exceeds the ${caps.maxFiles}-file limit. ` +
|
|
32
|
+
'Trim the build output (e.g. drop sourcemaps) and retry.');
|
|
33
|
+
}
|
|
34
|
+
const total = files.reduce((sum, f) => sum + f.size, 0);
|
|
35
|
+
if (total > caps.maxBytes) {
|
|
36
|
+
throw new CapError(`Bundle too large: ${humanBytes(total)} exceeds the ${humanBytes(caps.maxBytes)} limit. ` +
|
|
37
|
+
'Trim the build output (e.g. drop sourcemaps / large media) and retry.');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Pack the dist dir: validate caps, copy to a staging dir, stamp index.html,
|
|
41
|
+
// tar.gz it. Returns the tar path + staging dir (for cleanup).
|
|
42
|
+
export async function packBundle(opts) {
|
|
43
|
+
const caps = opts.caps ?? DEFAULT_CAPS;
|
|
44
|
+
const files = await walkDir(opts.distDir);
|
|
45
|
+
enforceCaps(files, caps);
|
|
46
|
+
const totalBytes = files.reduce((sum, f) => sum + f.size, 0);
|
|
47
|
+
const tmpBase = await fs.mkdtemp(path.join(os.tmpdir(), 'carboncanvas-'));
|
|
48
|
+
const stagingDir = path.join(tmpBase, 'bundle');
|
|
49
|
+
await fs.cp(opts.distDir, stagingDir, { recursive: true });
|
|
50
|
+
if (opts.metaContent) {
|
|
51
|
+
const indexPath = path.join(stagingDir, 'index.html');
|
|
52
|
+
try {
|
|
53
|
+
const html = await fs.readFile(indexPath, 'utf8');
|
|
54
|
+
await fs.writeFile(indexPath, stampMeta(html, opts.metaContent));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// No index.html to stamp (validateStatic runs first in the real flow, so
|
|
58
|
+
// this is only hit if a caller skips it). Non-fatal — pack as-is.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const tarPath = path.join(tmpBase, 'bundle.tar.gz');
|
|
62
|
+
await tarCreate({ file: tarPath, gzip: true, cwd: stagingDir, portable: true }, ['.']);
|
|
63
|
+
return { tarPath, stagingDir, fileCount: files.length, totalBytes };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=pack.js.map
|
package/dist/pack.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack.js","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"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"}
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Minimal interactive prompt for `carboncanvas login` (paste-token). Reads one
|
|
2
|
+
// line from stdin with echo muted so a pasted token isn't left on screen. Falls
|
|
3
|
+
// back to a plain read when stdin isn't a TTY (e.g. piped input in CI).
|
|
4
|
+
import * as readline from 'node:readline';
|
|
5
|
+
export async function promptSecret(question) {
|
|
6
|
+
const input = process.stdin;
|
|
7
|
+
const output = process.stderr; // prompt goes to stderr, keeping stdout clean
|
|
8
|
+
if (!input.isTTY) {
|
|
9
|
+
// Non-interactive: read a single line without muting.
|
|
10
|
+
const rl = readline.createInterface({ input, output });
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
rl.question(question, (answer) => {
|
|
13
|
+
rl.close();
|
|
14
|
+
resolve(answer.trim());
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
const rl = readline.createInterface({ input, output, terminal: true });
|
|
20
|
+
// Mute echo: override the internal write so typed/pasted chars aren't shown.
|
|
21
|
+
const rlAny = rl;
|
|
22
|
+
const origWrite = rlAny._writeToOutput.bind(rlAny);
|
|
23
|
+
let muted = false;
|
|
24
|
+
rlAny._writeToOutput = (s) => {
|
|
25
|
+
if (!muted)
|
|
26
|
+
origWrite(s);
|
|
27
|
+
};
|
|
28
|
+
output.write(question);
|
|
29
|
+
muted = true;
|
|
30
|
+
rl.question('', (answer) => {
|
|
31
|
+
muted = false;
|
|
32
|
+
output.write('\n');
|
|
33
|
+
rl.close();
|
|
34
|
+
resolve(answer.trim());
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,gFAAgF;AAChF,wEAAwE;AAExE,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,8CAA8C;IAE7E,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACjB,sDAAsD;QACtD,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;YACrC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;gBAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACzB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACrC,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,6EAA6E;QAC7E,MAAM,KAAK,GAAG,EAAwD,CAAC;QACvE,MAAM,SAAS,GAAG,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnD,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,KAAK,CAAC,cAAc,GAAG,CAAC,CAAS,EAAE,EAAE;YACnC,IAAI,CAAC,KAAK;gBAAE,SAAS,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvB,KAAK,GAAG,IAAI,CAAC;QACb,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE;YACzB,KAAK,GAAG,KAAK,CAAC;YACd,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnB,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// `carboncanvas publish` — the whole flow (plan §4), composed from the pure
|
|
2
|
+
// modules. Thin orchestration: detect → build → resolve dist → validate static →
|
|
3
|
+
// canvas-config check → name-preservation check → stamp + pack → upload.
|
|
4
|
+
import { promises as fs } from 'node:fs';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import * as log from './logger.js';
|
|
7
|
+
import { UserError } from './errors.js';
|
|
8
|
+
import { detectProject, runScriptCommand, readPackageJson } from './detect.js';
|
|
9
|
+
import { runBuild, BuildError } from './build.js';
|
|
10
|
+
import { findOutputDir, validateStatic } from './output.js';
|
|
11
|
+
import { findCanvasId, NO_CANVAS_ID_WARNING } from './canvasConfig.js';
|
|
12
|
+
import { checkNamePreservation, MINIFIED_NAMES_WARNING } from './names.js';
|
|
13
|
+
import { readGitInfo, resolveLabel, deployContent } from './stamp.js';
|
|
14
|
+
import { packBundle, CapError } from './pack.js';
|
|
15
|
+
import { publishBundle } from './upload.js';
|
|
16
|
+
import { resolveToken } from './tokenStore.js';
|
|
17
|
+
export async function publishCommand(opts) {
|
|
18
|
+
const root = path.resolve(opts.dir ?? process.cwd());
|
|
19
|
+
// 0. Auth — resolved before doing expensive work.
|
|
20
|
+
const token = await resolveToken(opts.serviceUrl, opts.token);
|
|
21
|
+
if (!token) {
|
|
22
|
+
throw new UserError(`Not logged in for ${opts.serviceUrl}.`, 'Run `carboncanvas login` (or pass --token / set CARBON_TOKEN).');
|
|
23
|
+
}
|
|
24
|
+
// 1. Detect + 2. Build (skipped when --dist points at a pre-built dir).
|
|
25
|
+
let framework = 'unknown';
|
|
26
|
+
let pkg = null;
|
|
27
|
+
if (opts.dist) {
|
|
28
|
+
log.step(`Using pre-built directory: ${opts.dist} (skipping build)`);
|
|
29
|
+
// Best-effort read of package.json for name sampling; not required with --dist.
|
|
30
|
+
pkg = await readPackageJson(root).catch(() => null);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const detected = await detectProject(root);
|
|
34
|
+
pkg = detected.packageJson;
|
|
35
|
+
framework = detected.framework;
|
|
36
|
+
if (!detected.buildScript) {
|
|
37
|
+
throw new UserError(`No build script found in ${path.join(root, 'package.json')}.`, 'Add a "build" script, or pass --dist <dir> to publish a pre-built output.');
|
|
38
|
+
}
|
|
39
|
+
const cmd = runScriptCommand(detected.packageManager, detected.buildScript);
|
|
40
|
+
log.step(`Building with: ${cmd} (framework: ${framework}, pm: ${detected.packageManager})`);
|
|
41
|
+
try {
|
|
42
|
+
await runBuild(cmd, root);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err instanceof BuildError) {
|
|
46
|
+
throw new UserError(err.message, 'Fix the build locally, then re-run publish. (We never upload a broken build.)');
|
|
47
|
+
}
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// 3. Resolve the output dir.
|
|
52
|
+
const distDir = await findOutputDir(root, framework, opts.dist);
|
|
53
|
+
if (!distDir) {
|
|
54
|
+
throw new UserError(opts.dist
|
|
55
|
+
? `--dist "${opts.dist}" is not a directory (resolved from ${root}).`
|
|
56
|
+
: `Could not find a build output directory under ${root}.`, 'Pass --dist <dir> to point at the built static output.');
|
|
57
|
+
}
|
|
58
|
+
log.info(`Output directory: ${distDir}`);
|
|
59
|
+
// 4. Validate static-ness — REFUSE loudly on server manifests / missing index.
|
|
60
|
+
const stat = await validateStatic(distDir, root);
|
|
61
|
+
if (!stat.ok) {
|
|
62
|
+
const detail = stat.serverManifest ? ` (found "${stat.serverManifest}")` : '';
|
|
63
|
+
throw new UserError((stat.reason ?? 'Not a static bundle.') + detail);
|
|
64
|
+
}
|
|
65
|
+
// 5. Canvas-config check (publish injects NOTHING — just warn).
|
|
66
|
+
const { canvasId: detectedCanvasId } = await findCanvasId(distDir);
|
|
67
|
+
if (!detectedCanvasId) {
|
|
68
|
+
log.warn(NO_CANVAS_ID_WARNING);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
log.info(`Detected canvasId in bundle: ${detectedCanvasId}`);
|
|
72
|
+
}
|
|
73
|
+
// 6. Name-preservation check — warn if the Inspector would show minified names.
|
|
74
|
+
const nameCheck = await checkNamePreservation(root, distDir);
|
|
75
|
+
if (nameCheck.checked && nameCheck.likelyMinified) {
|
|
76
|
+
log.warn(MINIFIED_NAMES_WARNING +
|
|
77
|
+
` (missing: ${nameCheck.missing.slice(0, 5).join(', ')})`);
|
|
78
|
+
}
|
|
79
|
+
// 7. Version stamp + 8. Package.
|
|
80
|
+
const git = await readGitInfo(root);
|
|
81
|
+
const label = resolveLabel(opts.label, git);
|
|
82
|
+
const metaContent = deployContent(label, git.shortSha);
|
|
83
|
+
log.step(`Packaging (cc-deploy: ${metaContent}) …`);
|
|
84
|
+
let pack;
|
|
85
|
+
try {
|
|
86
|
+
pack = await packBundle({ distDir, metaContent });
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
if (err instanceof CapError)
|
|
90
|
+
throw new UserError(err.message);
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
93
|
+
log.info(`Packed ${pack.fileCount} files (${(pack.totalBytes / 1024 / 1024).toFixed(1)} MB).`);
|
|
94
|
+
// 9. Build the publish meta + upload.
|
|
95
|
+
const meta = {
|
|
96
|
+
label,
|
|
97
|
+
branch: git.branch ?? undefined,
|
|
98
|
+
commit: git.shortSha ?? undefined,
|
|
99
|
+
};
|
|
100
|
+
if (opts.fresh) {
|
|
101
|
+
meta.fresh = true; // throwaway canvas — do NOT target an existing id
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const canvasId = opts.canvas ?? detectedCanvasId ?? undefined;
|
|
105
|
+
if (canvasId)
|
|
106
|
+
meta.canvasId = canvasId;
|
|
107
|
+
}
|
|
108
|
+
if (opts.name)
|
|
109
|
+
meta.slug = opts.name;
|
|
110
|
+
log.step(`Uploading to ${opts.serviceUrl}/api/publish …`);
|
|
111
|
+
try {
|
|
112
|
+
const result = await publishBundle(opts.serviceUrl, token, pack.tarPath, meta, opts.fetchImpl);
|
|
113
|
+
log.success('Published.');
|
|
114
|
+
log.info('');
|
|
115
|
+
log.info(` Review link : ${result.url}`);
|
|
116
|
+
log.info(` Dashboard : ${result.dashboardUrl}`);
|
|
117
|
+
log.info(` Canvas : ${result.canvasId} (deploy ${result.deployId}, slug ${result.slug})`);
|
|
118
|
+
if (result.expiresAt)
|
|
119
|
+
log.info(` Expires : ${result.expiresAt}`);
|
|
120
|
+
log.info('');
|
|
121
|
+
// The one machine-readable line on stdout.
|
|
122
|
+
log.out(result.url);
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
// Best-effort cleanup of the staging + tar tmp dir.
|
|
126
|
+
await fs.rm(path.dirname(pack.tarPath), { recursive: true, force: true }).catch(() => undefined);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=publishCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publishCommand.js","sourceRoot":"","sources":["../src/publishCommand.ts"],"names":[],"mappings":"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"}
|
package/dist/stamp.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Version stamp (plan §6b.2). Inject exactly one
|
|
2
|
+
// <meta name="cc-deploy" content="<label>@<shortsha>">
|
|
3
|
+
// into the PACKAGED copy of index.html (never the user's file on disk). The
|
|
4
|
+
// comments client reads it and attributes feedback to a version.
|
|
5
|
+
//
|
|
6
|
+
// stampMeta is pure & idempotent: calling it twice yields the same output (any
|
|
7
|
+
// existing cc-deploy meta is stripped first).
|
|
8
|
+
import { execFile } from 'node:child_process';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
|
+
// Match a cc-deploy meta line WITH its own leading indent + trailing newline,
|
|
12
|
+
// but NOT the following line's indentation (so re-stamping is byte-identical).
|
|
13
|
+
const CC_DEPLOY_META_RE = /[ \t]*<meta\s+name=["']cc-deploy["'][^>]*>[ \t]*\r?\n?/gi;
|
|
14
|
+
// Build the `<label>@<shortsha>` content string. shortsha omitted (and the `@`
|
|
15
|
+
// dropped) when unavailable — e.g. outside a git repo.
|
|
16
|
+
export function deployContent(label, shortSha) {
|
|
17
|
+
const safeLabel = label.trim() || 'preview';
|
|
18
|
+
return shortSha ? `${safeLabel}@${shortSha}` : safeLabel;
|
|
19
|
+
}
|
|
20
|
+
// Insert/replace the cc-deploy meta. Idempotent. Prefers to inject right after
|
|
21
|
+
// <head>; falls back to before </head>, then to the top of the document.
|
|
22
|
+
export function stampMeta(html, content) {
|
|
23
|
+
const stripped = html.replace(CC_DEPLOY_META_RE, '');
|
|
24
|
+
const tag = `<meta name="cc-deploy" content="${escapeAttr(content)}">`;
|
|
25
|
+
const headOpen = /<head[^>]*>/i;
|
|
26
|
+
if (headOpen.test(stripped)) {
|
|
27
|
+
return stripped.replace(headOpen, (m) => `${m}\n ${tag}`);
|
|
28
|
+
}
|
|
29
|
+
const headClose = /<\/head>/i;
|
|
30
|
+
if (headClose.test(stripped)) {
|
|
31
|
+
return stripped.replace(headClose, ` ${tag}\n</head>`);
|
|
32
|
+
}
|
|
33
|
+
// No <head> at all (fragment) — prepend.
|
|
34
|
+
return `${tag}\n${stripped}`;
|
|
35
|
+
}
|
|
36
|
+
function escapeAttr(s) {
|
|
37
|
+
return s.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');
|
|
38
|
+
}
|
|
39
|
+
// Read git branch + short SHA from the project dir. Returns nulls gracefully when
|
|
40
|
+
// not a git repo / git absent (so `publish` still works outside version control).
|
|
41
|
+
export async function readGitInfo(cwd) {
|
|
42
|
+
async function run(args) {
|
|
43
|
+
try {
|
|
44
|
+
const { stdout } = await execFileAsync('git', args, { cwd });
|
|
45
|
+
const v = stdout.trim();
|
|
46
|
+
return v || null;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const branch = await run(['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
53
|
+
const shortSha = await run(['rev-parse', '--short', 'HEAD']);
|
|
54
|
+
return {
|
|
55
|
+
branch: branch === 'HEAD' ? null : branch, // detached HEAD → no branch label
|
|
56
|
+
shortSha,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Resolve the deploy label: --label wins, else the git branch, else "preview".
|
|
60
|
+
export function resolveLabel(explicit, git) {
|
|
61
|
+
if (explicit && explicit.trim())
|
|
62
|
+
return explicit.trim();
|
|
63
|
+
if (git.branch)
|
|
64
|
+
return git.branch;
|
|
65
|
+
return 'preview';
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=stamp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stamp.js","sourceRoot":"","sources":["../src/stamp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Token persistence for `carboncanvas login`.
|
|
2
|
+
//
|
|
3
|
+
// Stored at ~/.carboncanvas/config.json (mode 0600), keyed by serviceUrl so an
|
|
4
|
+
// author can hold tokens for prod and a self-hosted backend at once:
|
|
5
|
+
//
|
|
6
|
+
// { "tokens": { "https://quilt-production-65f6.up.railway.app": "cc_..." } }
|
|
7
|
+
//
|
|
8
|
+
// Resolution order for the active token (see resolveToken):
|
|
9
|
+
// 1. --token flag (handled by the caller)
|
|
10
|
+
// 2. CARBON_TOKEN env var
|
|
11
|
+
// 3. the stored token for the active serviceUrl
|
|
12
|
+
import { promises as fs } from 'node:fs';
|
|
13
|
+
import * as os from 'node:os';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import { CONFIG_DIR_NAME, CONFIG_FILE_NAME } from './constants.js';
|
|
16
|
+
export function configDir() {
|
|
17
|
+
return path.join(os.homedir(), CONFIG_DIR_NAME);
|
|
18
|
+
}
|
|
19
|
+
export function configPath() {
|
|
20
|
+
return path.join(configDir(), CONFIG_FILE_NAME);
|
|
21
|
+
}
|
|
22
|
+
function normalizeUrl(serviceUrl) {
|
|
23
|
+
return serviceUrl.replace(/\/+$/, '');
|
|
24
|
+
}
|
|
25
|
+
export async function readConfig() {
|
|
26
|
+
try {
|
|
27
|
+
const raw = await fs.readFile(configPath(), 'utf8');
|
|
28
|
+
const parsed = JSON.parse(raw);
|
|
29
|
+
return { tokens: parsed.tokens ?? {} };
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { tokens: {} };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export async function writeToken(serviceUrl, token) {
|
|
36
|
+
const dir = configDir();
|
|
37
|
+
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
38
|
+
const cfg = await readConfig();
|
|
39
|
+
cfg.tokens[normalizeUrl(serviceUrl)] = token;
|
|
40
|
+
await fs.writeFile(configPath(), JSON.stringify(cfg, null, 2) + '\n', { mode: 0o600 });
|
|
41
|
+
// mkdir/writeFile honor mode only on create; enforce on existing files too.
|
|
42
|
+
await fs.chmod(configPath(), 0o600).catch(() => undefined);
|
|
43
|
+
}
|
|
44
|
+
export async function clearToken(serviceUrl) {
|
|
45
|
+
const cfg = await readConfig();
|
|
46
|
+
const key = normalizeUrl(serviceUrl);
|
|
47
|
+
if (!(key in cfg.tokens))
|
|
48
|
+
return false;
|
|
49
|
+
delete cfg.tokens[key];
|
|
50
|
+
await fs.writeFile(configPath(), JSON.stringify(cfg, null, 2) + '\n', { mode: 0o600 });
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
export async function storedToken(serviceUrl) {
|
|
54
|
+
const cfg = await readConfig();
|
|
55
|
+
return cfg.tokens[normalizeUrl(serviceUrl)];
|
|
56
|
+
}
|
|
57
|
+
// Resolve the token to use for a request. Explicit `--token` wins, then env, then
|
|
58
|
+
// the stored token. Returns undefined when none is available (caller errors out).
|
|
59
|
+
export async function resolveToken(serviceUrl, explicit) {
|
|
60
|
+
if (explicit && explicit.trim())
|
|
61
|
+
return explicit.trim();
|
|
62
|
+
const env = process.env.CARBON_TOKEN;
|
|
63
|
+
if (env && env.trim())
|
|
64
|
+
return env.trim();
|
|
65
|
+
return storedToken(serviceUrl);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=tokenStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenStore.js","sourceRoot":"","sources":["../src/tokenStore.ts"],"names":[],"mappings":"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"}
|