pattern-dump 0.1.0-alpha.0 → 0.1.0-alpha.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.
- package/CHANGELOG.md +55 -0
- package/README.md +14 -1
- package/dist/chunks/chunk-3WZHFYHZ.js +160 -0
- package/dist/dev-ui/main.css +38 -7
- package/dist/dev-ui/main.js +283 -74
- package/dist/index.js +3 -4
- package/dist/pd.js +588 -381
- package/dist/types/config/index.d.ts +19 -12
- package/dist/types/config/validate.d.ts +10 -0
- package/dist/types/contracts/dev-protocol.d.ts +26 -13
- package/package.json +3 -2
package/dist/pd.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ConfigError,
|
|
4
|
+
validateConfig
|
|
5
|
+
} from "./chunks/chunk-3WZHFYHZ.js";
|
|
2
6
|
|
|
3
7
|
// packages/discovery/src/index.ts
|
|
4
8
|
import { readdir } from "node:fs/promises";
|
|
@@ -28,7 +32,8 @@ function collapseComponentId(relNoExt) {
|
|
|
28
32
|
}
|
|
29
33
|
function deriveHandle(id) {
|
|
30
34
|
const parts = id.split("/");
|
|
31
|
-
|
|
35
|
+
const unnumbered = (seg) => seg.replace(/^\d+[-_]/, "");
|
|
36
|
+
return `${unnumbered(parts[0])}-${unnumbered(parts[parts.length - 1])}`;
|
|
32
37
|
}
|
|
33
38
|
async function buildDiscoveryMap(roots) {
|
|
34
39
|
const rootList = typeof roots === "string" ? [{ dir: roots }] : roots;
|
|
@@ -788,28 +793,21 @@ async function compileScriptFile(entryPoint) {
|
|
|
788
793
|
import { WebSocket, WebSocketServer } from "ws";
|
|
789
794
|
|
|
790
795
|
// packages/contracts/src/dev-protocol.ts
|
|
791
|
-
var
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
- **PD reporting gaps** \u2014 PD under-claiming (e.g. missing provenance) is not a subject defect.
|
|
807
|
-
- **Interactive / behavioural** \u2014 focus traps, keyboard flow, escape handling. Single static render only.
|
|
808
|
-
- **Data-masked** \u2014 a component that renders empty for want of data won't throw the violations it would
|
|
809
|
-
when populated; findings are annotated with render outcome wherever it is blank.
|
|
810
|
-
|
|
811
|
-
A component with zero findings is **not "clean" \u2014 it is "clean of static machine-detectable defects."**
|
|
812
|
-
That is the honest claim; never "clean".`;
|
|
796
|
+
var AUDIT_SCOPE = {
|
|
797
|
+
covered: [
|
|
798
|
+
{ name: "HTML content model", detail: "invalid nesting and content that isn't permitted where it appears (html-validate)." },
|
|
799
|
+
{ name: "Structural accessibility", detail: "labels, accessible names, ARIA attributes, roles and image alternatives (the axe-core rules that don't need layout)." },
|
|
800
|
+
{ name: "Schema", detail: "missing required fields and values of the wrong type, checked against each component's `<name>.schema.json`." },
|
|
801
|
+
{ name: "Render outcome", detail: "whether each component rendered, rendered empty or failed. It is reported with the component, not counted as a defect." }
|
|
802
|
+
],
|
|
803
|
+
notCovered: [
|
|
804
|
+
{ name: "Checks that need a rendered page", detail: "colour contrast, focusable content hidden with aria-hidden, and duplicate ids referenced by ARIA. They need real browser layout; the Console's Diagnostics tab runs them for the component on screen." },
|
|
805
|
+
{ name: "Interaction", detail: "focus traps, keyboard flow and Escape handling. The audit looks at a single static render." },
|
|
806
|
+
{ name: "Structure", detail: "unused components, shared bases and near-duplicate copies. The Relationships tab and `pd export` cover those." },
|
|
807
|
+
{ name: "Components that need data", detail: "a component that renders empty without data can't show the problems it would have with data. Its findings carry the render outcome." }
|
|
808
|
+
],
|
|
809
|
+
note: "No findings means no static, machine-detectable defects, not that a component is fully accessible or correct."
|
|
810
|
+
};
|
|
813
811
|
function buildAuditRollup(graph, opts = {}) {
|
|
814
812
|
const total = graph.components.length;
|
|
815
813
|
const affected = new Set(graph.occurrences.map((o) => o.component));
|
|
@@ -851,7 +849,7 @@ function buildAuditRollup(graph, opts = {}) {
|
|
|
851
849
|
renderOutcomes: { ok: outcomes["ok"] ?? 0, blank: outcomes["blank"] ?? 0, error: outcomes["error"] ?? 0 },
|
|
852
850
|
rulesByBlast,
|
|
853
851
|
perComponent,
|
|
854
|
-
scope:
|
|
852
|
+
scope: AUDIT_SCOPE
|
|
855
853
|
};
|
|
856
854
|
}
|
|
857
855
|
function previewUrl(id, variant, theme) {
|
|
@@ -991,7 +989,7 @@ function createHotReloader(port2 = Number(process.env["PD_WS_PORT"] ?? 4001)) {
|
|
|
991
989
|
// packages/dev-server/src/index.ts
|
|
992
990
|
import { createServer } from "node:http";
|
|
993
991
|
import { stat as stat5 } from "node:fs/promises";
|
|
994
|
-
import { join as
|
|
992
|
+
import { join as join20 } from "node:path";
|
|
995
993
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
996
994
|
|
|
997
995
|
// packages/dev-server/src/composition.ts
|
|
@@ -1182,7 +1180,7 @@ async function loadGlobalData(dataDirs) {
|
|
|
1182
1180
|
|
|
1183
1181
|
// packages/dev-server/src/surfacing.ts
|
|
1184
1182
|
import { mkdir, readFile as readFile5, writeFile } from "node:fs/promises";
|
|
1185
|
-
import { join as
|
|
1183
|
+
import { join as join7, relative as relative3 } from "node:path";
|
|
1186
1184
|
|
|
1187
1185
|
// packages/dev-server/src/flattening.ts
|
|
1188
1186
|
import { parse as parseHtml } from "node-html-parser";
|
|
@@ -1199,6 +1197,9 @@ function toSkelNode(el) {
|
|
|
1199
1197
|
}
|
|
1200
1198
|
return { tag: el.tagName ? el.tagName.toLowerCase() : "", attrs, children };
|
|
1201
1199
|
}
|
|
1200
|
+
function skeletonSize(nodes) {
|
|
1201
|
+
return nodes.reduce((n, node) => n + 1 + node.attrs.length + skeletonSize(node.children), 0);
|
|
1202
|
+
}
|
|
1202
1203
|
function toPreorder(nodes, depth, out) {
|
|
1203
1204
|
for (const n of nodes) {
|
|
1204
1205
|
out.push(`${depth}<${n.tag}${n.attrs.length ? " " + n.attrs.join(",") : ""}>`);
|
|
@@ -1270,6 +1271,7 @@ var DEFAULTS = {
|
|
|
1270
1271
|
prefilterCutoff: 0.6,
|
|
1271
1272
|
shingleK: 2,
|
|
1272
1273
|
minClusterSize: 2,
|
|
1274
|
+
minSkeletonSize: 3,
|
|
1273
1275
|
rootMatch: "exact"
|
|
1274
1276
|
// full root-class match — emphasis families (hoo-button vs -primary) stay distinct
|
|
1275
1277
|
};
|
|
@@ -1308,6 +1310,7 @@ function detectFlattening(inputs, opts = {}) {
|
|
|
1308
1310
|
for (const { id } of sorted) {
|
|
1309
1311
|
const s = skel.get(id);
|
|
1310
1312
|
if (!s.root) continue;
|
|
1313
|
+
if (skeletonSize(s.tree) < o.minSkeletonSize) continue;
|
|
1311
1314
|
const key = rootKey(s.root, o.rootMatch);
|
|
1312
1315
|
(buckets.get(key) ?? buckets.set(key, []).get(key)).push(id);
|
|
1313
1316
|
}
|
|
@@ -1423,6 +1426,259 @@ function pdScratchDir(root) {
|
|
|
1423
1426
|
return join5(tmpdir(), "pattern-dump", key);
|
|
1424
1427
|
}
|
|
1425
1428
|
|
|
1429
|
+
// packages/dev-server/src/style-index.ts
|
|
1430
|
+
import { dirname as dirname2, join as join6, relative as relative2, resolve as resolve4 } from "node:path";
|
|
1431
|
+
import { existsSync } from "node:fs";
|
|
1432
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1433
|
+
import postcss3 from "postcss";
|
|
1434
|
+
import { SourceMapConsumer } from "source-map";
|
|
1435
|
+
import * as sass2 from "sass";
|
|
1436
|
+
|
|
1437
|
+
// packages/dev-server/src/timing.ts
|
|
1438
|
+
var verboseTiming = !!process.env["PD_VERBOSE_TIMING"];
|
|
1439
|
+
|
|
1440
|
+
// packages/dev-server/src/style-index.ts
|
|
1441
|
+
function cssNodeText(node) {
|
|
1442
|
+
const text = node.toString();
|
|
1443
|
+
return node.type === "atrule" && !node.nodes ? `${text};` : text;
|
|
1444
|
+
}
|
|
1445
|
+
function joinCssChunks(chunks) {
|
|
1446
|
+
const charset = [];
|
|
1447
|
+
const imports = [];
|
|
1448
|
+
const rest = [];
|
|
1449
|
+
for (const chunk of chunks) {
|
|
1450
|
+
if (/^@charset\b/i.test(chunk)) charset.push(chunk);
|
|
1451
|
+
else if (/^@import\b/i.test(chunk)) imports.push(chunk);
|
|
1452
|
+
else rest.push(chunk);
|
|
1453
|
+
}
|
|
1454
|
+
return [...charset.slice(0, 1), ...imports, ...rest].join("\n");
|
|
1455
|
+
}
|
|
1456
|
+
var stripOrderPrefixes = (path) => path.split("/").map((seg) => seg.replace(/^\d+[-_]/, "")).join("/");
|
|
1457
|
+
var strippedIdCache = /* @__PURE__ */ new WeakMap();
|
|
1458
|
+
function strippedIds(componentIds) {
|
|
1459
|
+
let map = strippedIdCache.get(componentIds);
|
|
1460
|
+
if (!map) {
|
|
1461
|
+
map = /* @__PURE__ */ new Map();
|
|
1462
|
+
for (const id of componentIds) {
|
|
1463
|
+
const key = stripOrderPrefixes(id);
|
|
1464
|
+
map.set(key, map.has(key) ? null : id);
|
|
1465
|
+
}
|
|
1466
|
+
strippedIdCache.set(componentIds, map);
|
|
1467
|
+
}
|
|
1468
|
+
return map;
|
|
1469
|
+
}
|
|
1470
|
+
function resolveScssToComponent(absPath, stylesDir, componentIds) {
|
|
1471
|
+
const rel = relative2(stylesDir, absPath);
|
|
1472
|
+
let normalized = rel.replace(/\.scss$/, "").split("/").map((seg) => seg.replace(/^_/, "")).join("/");
|
|
1473
|
+
if (normalized.endsWith("/index")) normalized = normalized.slice(0, -6);
|
|
1474
|
+
const id = collapseComponentId(normalized);
|
|
1475
|
+
if (componentIds.has(id)) return id;
|
|
1476
|
+
return strippedIds(componentIds).get(collapseComponentId(stripOrderPrefixes(normalized))) ?? null;
|
|
1477
|
+
}
|
|
1478
|
+
function resolveSharedModulePath(absPath, stylesDir, keepBarrel = false) {
|
|
1479
|
+
const rel = relative2(stylesDir, absPath);
|
|
1480
|
+
if (rel.startsWith("..")) return null;
|
|
1481
|
+
const parts = rel.replace(/\.scss$/, "").split("/");
|
|
1482
|
+
const normalized = parts.map(
|
|
1483
|
+
(seg, i) => i === parts.length - 1 ? seg.replace(/^_/, "") : seg
|
|
1484
|
+
);
|
|
1485
|
+
if (!keepBarrel && normalized[normalized.length - 1] === "index") return null;
|
|
1486
|
+
return normalized.join("/");
|
|
1487
|
+
}
|
|
1488
|
+
function normalizeBaseEntry(entry) {
|
|
1489
|
+
const parts = entry.replace(/\\/g, "/").replace(/\.scss$/, "").split("/").filter((seg) => seg !== "" && seg !== ".");
|
|
1490
|
+
if (parts.length > 0) parts[parts.length - 1] = parts[parts.length - 1].replace(/^_/, "");
|
|
1491
|
+
return parts.join("/");
|
|
1492
|
+
}
|
|
1493
|
+
function documentBaseMatcher(stylesDir, base2) {
|
|
1494
|
+
const entries = (base2 ?? []).map(normalizeBaseEntry);
|
|
1495
|
+
if (entries.length === 0) return () => false;
|
|
1496
|
+
return (absPath) => {
|
|
1497
|
+
const rel = relative2(stylesDir, absPath);
|
|
1498
|
+
if (rel.startsWith("..")) return false;
|
|
1499
|
+
const parts = rel.replace(/\\/g, "/").replace(/\.scss$/, "").split("/");
|
|
1500
|
+
parts[parts.length - 1] = parts[parts.length - 1].replace(/^_/, "");
|
|
1501
|
+
const file = parts.join("/");
|
|
1502
|
+
return entries.some((e) => e === "" || file === e || file.startsWith(`${e}/`));
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
function missingDocumentBase(stylesDir, base2) {
|
|
1506
|
+
return (base2 ?? []).filter((entry) => {
|
|
1507
|
+
const e = normalizeBaseEntry(entry);
|
|
1508
|
+
if (e === "") return false;
|
|
1509
|
+
const dir = dirname2(join6(stylesDir, e));
|
|
1510
|
+
const name = e.split("/").pop();
|
|
1511
|
+
return ![join6(stylesDir, e), join6(dir, `${name}.scss`), join6(dir, `_${name}.scss`)].some(existsSync);
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1514
|
+
function extractClassesFromCompiled(consumer, root, entryFile, stylesDir, componentIds) {
|
|
1515
|
+
const classMap = /* @__PURE__ */ new Map();
|
|
1516
|
+
const baseModules = /* @__PURE__ */ new Map();
|
|
1517
|
+
root.walkRules((rule) => {
|
|
1518
|
+
const classMatches = rule.selector.match(/\.([\w-]+)/g);
|
|
1519
|
+
if (!classMatches) return;
|
|
1520
|
+
const pos = rule.source?.start;
|
|
1521
|
+
if (!pos) return;
|
|
1522
|
+
const original = consumer.originalPositionFor({ line: pos.line, column: pos.column - 1 });
|
|
1523
|
+
if (!original.source) return;
|
|
1524
|
+
const originPath = original.source.startsWith("file://") ? fileURLToPath2(original.source) : resolve4(dirname2(entryFile), original.source);
|
|
1525
|
+
const resolved = resolveScssToComponent(originPath, stylesDir, componentIds);
|
|
1526
|
+
const owner = resolved ?? "__base__";
|
|
1527
|
+
const modulePath = resolved === null ? resolveSharedModulePath(originPath, stylesDir, true) : null;
|
|
1528
|
+
for (const cls of classMatches) {
|
|
1529
|
+
const name = cls.slice(1);
|
|
1530
|
+
if (!classMap.has(name)) classMap.set(name, owner);
|
|
1531
|
+
if (modulePath && classMap.get(name) === "__base__" && !baseModules.has(name)) {
|
|
1532
|
+
baseModules.set(name, modulePath);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
return { classMap, baseModules };
|
|
1537
|
+
}
|
|
1538
|
+
async function attributeClassesFromFile(entryFile, stylesDir, componentIds) {
|
|
1539
|
+
try {
|
|
1540
|
+
const compiled = sass2.compile(entryFile, { sourceMap: true });
|
|
1541
|
+
if (!compiled.sourceMap) return null;
|
|
1542
|
+
const consumer = new SourceMapConsumer(compiled.sourceMap);
|
|
1543
|
+
const root = postcss3.parse(compiled.css);
|
|
1544
|
+
const { classMap } = extractClassesFromCompiled(consumer, root, entryFile, stylesDir, componentIds);
|
|
1545
|
+
return classMap;
|
|
1546
|
+
} catch {
|
|
1547
|
+
return null;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
async function patchClassOwnerIndex(entryFile, changedCompId, stylesDir, componentIds, index) {
|
|
1551
|
+
for (const [cls, owner] of index) {
|
|
1552
|
+
if (owner === changedCompId) index.delete(cls);
|
|
1553
|
+
}
|
|
1554
|
+
const fresh = await attributeClassesFromFile(entryFile, stylesDir, componentIds);
|
|
1555
|
+
if (!fresh) return false;
|
|
1556
|
+
for (const [name, owner] of fresh) {
|
|
1557
|
+
if (owner === changedCompId && !index.has(name)) index.set(name, owner);
|
|
1558
|
+
}
|
|
1559
|
+
return true;
|
|
1560
|
+
}
|
|
1561
|
+
async function buildAllStyleData(roots, componentIds, entryOverride) {
|
|
1562
|
+
if (verboseTiming) console.time("[dev-server:timing] full classOwnerIndex build");
|
|
1563
|
+
const styleUsesModules = /* @__PURE__ */ new Map();
|
|
1564
|
+
const classIndex = /* @__PURE__ */ new Map();
|
|
1565
|
+
const baseProvenance = /* @__PURE__ */ new Map();
|
|
1566
|
+
const componentCssBuckets = /* @__PURE__ */ new Map();
|
|
1567
|
+
const baseCssByRoot = [];
|
|
1568
|
+
const outsideRules = /* @__PURE__ */ new Map();
|
|
1569
|
+
for (const id of componentIds) styleUsesModules.set(id, []);
|
|
1570
|
+
const rootList = typeof roots === "string" ? [{ dir: roots, ...entryOverride ? { entries: entryOverride } : {} }] : roots;
|
|
1571
|
+
for (const styleRoot of rootList) {
|
|
1572
|
+
const stylesDir = styleRoot.dir;
|
|
1573
|
+
const inBase = documentBaseMatcher(stylesDir, styleRoot.base);
|
|
1574
|
+
const reportOutside = (styleRoot.base?.length ?? 0) > 0;
|
|
1575
|
+
const baseCssBucket = /* @__PURE__ */ new Set();
|
|
1576
|
+
baseCssByRoot.push([]);
|
|
1577
|
+
let entryFiles = [];
|
|
1578
|
+
if (styleRoot.entries && styleRoot.entries.length > 0) {
|
|
1579
|
+
entryFiles = [...styleRoot.entries];
|
|
1580
|
+
} else {
|
|
1581
|
+
try {
|
|
1582
|
+
entryFiles = await collectScssFiles(stylesDir);
|
|
1583
|
+
} catch {
|
|
1584
|
+
entryFiles = [];
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
entryFiles.sort();
|
|
1588
|
+
for (const entryFile of entryFiles) {
|
|
1589
|
+
let originPathFor2 = function(node) {
|
|
1590
|
+
const pos = node.source?.start;
|
|
1591
|
+
if (!pos) return null;
|
|
1592
|
+
const original = consumer.originalPositionFor({
|
|
1593
|
+
line: pos.line,
|
|
1594
|
+
column: pos.column - 1
|
|
1595
|
+
});
|
|
1596
|
+
if (!original.source) return null;
|
|
1597
|
+
return original.source.startsWith("file://") ? fileURLToPath2(original.source) : resolve4(dirname2(entryFile), original.source);
|
|
1598
|
+
};
|
|
1599
|
+
var originPathFor = originPathFor2;
|
|
1600
|
+
const compOwner = resolveScssToComponent(entryFile, stylesDir, componentIds);
|
|
1601
|
+
let compiled;
|
|
1602
|
+
try {
|
|
1603
|
+
compiled = sass2.compile(entryFile, { sourceMap: true });
|
|
1604
|
+
} catch {
|
|
1605
|
+
continue;
|
|
1606
|
+
}
|
|
1607
|
+
if (compOwner) {
|
|
1608
|
+
const deps = compiled.loadedUrls.filter((u) => u.protocol === "file:").map((u) => fileURLToPath2(u)).filter((p) => resolve4(p) !== resolve4(entryFile));
|
|
1609
|
+
for (const dep of deps) {
|
|
1610
|
+
const depComp = resolveScssToComponent(dep, stylesDir, componentIds);
|
|
1611
|
+
if (depComp !== null && depComp !== compOwner) {
|
|
1612
|
+
const rawRel = relative2(stylesDir, dep).replace(/\.scss$/, "");
|
|
1613
|
+
const modPath = rawRel.endsWith("/_index") ? rawRel.slice(0, -7) : rawRel;
|
|
1614
|
+
if (modPath) {
|
|
1615
|
+
const mArr = styleUsesModules.get(compOwner) ?? [];
|
|
1616
|
+
if (!mArr.includes(modPath)) {
|
|
1617
|
+
mArr.push(modPath);
|
|
1618
|
+
styleUsesModules.set(compOwner, mArr);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
} else if (depComp === null) {
|
|
1622
|
+
const modPath = resolveSharedModulePath(dep, stylesDir);
|
|
1623
|
+
if (modPath) {
|
|
1624
|
+
const mArr = styleUsesModules.get(compOwner) ?? [];
|
|
1625
|
+
if (!mArr.includes(modPath)) {
|
|
1626
|
+
mArr.push(modPath);
|
|
1627
|
+
styleUsesModules.set(compOwner, mArr);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
const rawMap = compiled.sourceMap;
|
|
1634
|
+
if (!rawMap) continue;
|
|
1635
|
+
const consumer = new SourceMapConsumer(rawMap);
|
|
1636
|
+
const root = postcss3.parse(compiled.css);
|
|
1637
|
+
root.each((node) => {
|
|
1638
|
+
const originPath = originPathFor2(node);
|
|
1639
|
+
if (originPath === null) return;
|
|
1640
|
+
const nodeText = cssNodeText(node);
|
|
1641
|
+
const owner = inBase(originPath) ? "__base__" : resolveScssToComponent(originPath, stylesDir, componentIds) ?? "__base__";
|
|
1642
|
+
if (owner === "__base__") {
|
|
1643
|
+
baseCssBucket.add(nodeText);
|
|
1644
|
+
if (reportOutside && !inBase(originPath)) {
|
|
1645
|
+
const seen = outsideRules.get(originPath) ?? /* @__PURE__ */ new Set();
|
|
1646
|
+
seen.add(nodeText);
|
|
1647
|
+
outsideRules.set(originPath, seen);
|
|
1648
|
+
}
|
|
1649
|
+
} else {
|
|
1650
|
+
const bucket = componentCssBuckets.get(owner) ?? /* @__PURE__ */ new Set();
|
|
1651
|
+
bucket.add(nodeText);
|
|
1652
|
+
componentCssBuckets.set(owner, bucket);
|
|
1653
|
+
}
|
|
1654
|
+
});
|
|
1655
|
+
const { classMap: perFileClasses, baseModules: perFileBaseModules } = extractClassesFromCompiled(consumer, root, entryFile, stylesDir, componentIds);
|
|
1656
|
+
for (const [name, owner] of perFileClasses) {
|
|
1657
|
+
if (!classIndex.has(name)) classIndex.set(name, owner);
|
|
1658
|
+
}
|
|
1659
|
+
for (const [name, mod] of perFileBaseModules) {
|
|
1660
|
+
if (!baseProvenance.has(name)) baseProvenance.set(name, mod);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
baseCssByRoot[baseCssByRoot.length - 1] = [...baseCssBucket];
|
|
1664
|
+
}
|
|
1665
|
+
for (const [k, v] of styleUsesModules) styleUsesModules.set(k, [...new Set(v)].sort());
|
|
1666
|
+
const componentCssMap = /* @__PURE__ */ new Map();
|
|
1667
|
+
for (const [id, bucket] of componentCssBuckets) {
|
|
1668
|
+
componentCssMap.set(id, joinCssChunks(bucket));
|
|
1669
|
+
}
|
|
1670
|
+
if (verboseTiming) console.timeEnd("[dev-server:timing] full classOwnerIndex build");
|
|
1671
|
+
return {
|
|
1672
|
+
scssModuleGraph: { styleUsesModules },
|
|
1673
|
+
classOwnerIndex: classIndex,
|
|
1674
|
+
baseProvenance,
|
|
1675
|
+
componentCssMap,
|
|
1676
|
+
// Packages' base rules first (later roots), the project's last — its own base wins the cascade.
|
|
1677
|
+
baseCss: joinCssChunks([...baseCssByRoot].reverse().flat()),
|
|
1678
|
+
baseOutsideList: new Map([...outsideRules].map(([path, rules]) => [path, rules.size]))
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1426
1682
|
// packages/dev-server/src/surfacing.ts
|
|
1427
1683
|
async function surfaceCollisions(collisions, cwd, hotreloader) {
|
|
1428
1684
|
const messages = collisions.map((col) => ({ type: "collision", handle: col.handle, incumbentPath: col.incumbentPath, quarantinedPath: col.quarantinedPath }));
|
|
@@ -1431,6 +1687,7 @@ async function surfaceCollisions(collisions, cwd, hotreloader) {
|
|
|
1431
1687
|
hotreloader.push(messages[i]);
|
|
1432
1688
|
console.warn(`[dev-server] HANDLE_COLLISION: "${col.handle}" \u2014 incumbent: ${col.incumbentPath} \u2014 quarantined: ${col.quarantinedPath}`);
|
|
1433
1689
|
}
|
|
1690
|
+
if (collisions.length > 0) console.warn(`[dev-server] collisions listed in ${join7(pdScratchDir(cwd), "CONFLICTS.generated.md")}`);
|
|
1434
1691
|
const md = collisions.length === 0 ? "# Conflicts\n\nNo handle collisions detected.\n" : [
|
|
1435
1692
|
"# Conflicts",
|
|
1436
1693
|
"",
|
|
@@ -1446,7 +1703,7 @@ async function surfaceCollisions(collisions, cwd, hotreloader) {
|
|
|
1446
1703
|
try {
|
|
1447
1704
|
const dir = pdScratchDir(cwd);
|
|
1448
1705
|
await mkdir(dir, { recursive: true });
|
|
1449
|
-
await writeFile(
|
|
1706
|
+
await writeFile(join7(dir, "CONFLICTS.generated.md"), md, "utf-8");
|
|
1450
1707
|
} catch (e) {
|
|
1451
1708
|
console.warn("[dev-server] Could not write CONFLICTS.generated.md to scratch dir:", e);
|
|
1452
1709
|
}
|
|
@@ -1467,21 +1724,33 @@ async function surfaceFlattening(discoveryMap, registry, cwd) {
|
|
|
1467
1724
|
const md = renderFlatteningReport(clusters);
|
|
1468
1725
|
if (clusters.length) {
|
|
1469
1726
|
const members = clusters.reduce((n, c3) => n + c3.members.length, 0);
|
|
1470
|
-
console.warn(`[dev-server] FLATTENED: ${clusters.length} cluster(s) across ${members} components \u2014 see FLATTENED.generated.md`);
|
|
1727
|
+
console.warn(`[dev-server] FLATTENED: ${clusters.length} cluster(s) across ${members} components \u2014 see ${join7(pdScratchDir(cwd), "FLATTENED.generated.md")}`);
|
|
1471
1728
|
}
|
|
1472
1729
|
try {
|
|
1473
1730
|
const dir = pdScratchDir(cwd);
|
|
1474
1731
|
await mkdir(dir, { recursive: true });
|
|
1475
|
-
await writeFile(
|
|
1732
|
+
await writeFile(join7(dir, "FLATTENED.generated.md"), md, "utf-8");
|
|
1476
1733
|
} catch (e) {
|
|
1477
1734
|
console.warn("[dev-server] Could not write FLATTENED.generated.md to scratch dir:", e);
|
|
1478
1735
|
}
|
|
1479
1736
|
}
|
|
1737
|
+
function surfaceDocumentBase(roots, outsideList, cwd) {
|
|
1738
|
+
for (const root of roots) {
|
|
1739
|
+
for (const entry of missingDocumentBase(root.dir, root.base)) {
|
|
1740
|
+
console.warn(`[dev-server] documentBase "${entry}" matches no stylesheet in ${relative3(cwd, root.dir) || "."}${root.source ? ` (${root.source})` : ""}`);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
if (outsideList.size === 0) return;
|
|
1744
|
+
const files = [...outsideList].sort((a, b) => b[1] - a[1]);
|
|
1745
|
+
const rules = files.reduce((n, [, count]) => n + count, 0);
|
|
1746
|
+
const examples = files.slice(0, 3).map(([path]) => relative3(cwd, path)).join(", ");
|
|
1747
|
+
console.log(`[dev-server] base layer: ${rules} rule(s) from ${files.length} file(s) that no component owns and documentBase does not list, e.g. ${examples}`);
|
|
1748
|
+
}
|
|
1480
1749
|
|
|
1481
1750
|
// packages/dev-server/src/diagnostics.ts
|
|
1482
1751
|
import { createHash as createHash2 } from "node:crypto";
|
|
1483
1752
|
import { readFile as readFile6, writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
|
|
1484
|
-
import { join as
|
|
1753
|
+
import { join as join8 } from "node:path";
|
|
1485
1754
|
|
|
1486
1755
|
// packages/a11y/src/producer.ts
|
|
1487
1756
|
import { createRequire } from "node:module";
|
|
@@ -1635,7 +1904,7 @@ function assembleGraph(audits) {
|
|
|
1635
1904
|
}
|
|
1636
1905
|
var GRAPH_ARTIFACT_VERSION = 1;
|
|
1637
1906
|
function graphArtifactPath(root) {
|
|
1638
|
-
return
|
|
1907
|
+
return join8(pdScratchDir(root), "diagnostics-graph.json");
|
|
1639
1908
|
}
|
|
1640
1909
|
async function readDiagnosticsArtifact(root) {
|
|
1641
1910
|
try {
|
|
@@ -1691,15 +1960,15 @@ async function persistSlices(root, slices, discoveryMap, globalData) {
|
|
|
1691
1960
|
}
|
|
1692
1961
|
|
|
1693
1962
|
// packages/dev-server/src/server-config.ts
|
|
1694
|
-
import { basename as basename2, join as
|
|
1963
|
+
import { basename as basename2, join as join9, resolve as resolve5 } from "node:path";
|
|
1695
1964
|
var DEFAULT_SWATCH_SLOTS = ["--bodyBackground", "--themePrimary", "--accent"];
|
|
1696
1965
|
function resolveServerConfig(config, cwd, opts = {}, packages = []) {
|
|
1697
1966
|
const port2 = opts.port ?? Number(process.env["PD_PORT"] ?? 4e3);
|
|
1698
1967
|
const wsPort = opts.wsPort ?? Number(process.env["PD_WS_PORT"] ?? port2 + 1);
|
|
1699
|
-
const patternsDir =
|
|
1700
|
-
const stylesDir =
|
|
1701
|
-
const stylesEntries = config.styles.entry ? (Array.isArray(config.styles.entry) ? config.styles.entry : [config.styles.entry]).map((e) =>
|
|
1702
|
-
const inPackage = (p, rel) =>
|
|
1968
|
+
const patternsDir = resolve5(cwd, config.patternsDir);
|
|
1969
|
+
const stylesDir = resolve5(cwd, config.styles.dir);
|
|
1970
|
+
const stylesEntries = config.styles.entry ? (Array.isArray(config.styles.entry) ? config.styles.entry : [config.styles.entry]).map((e) => resolve5(stylesDir, e)) : void 0;
|
|
1971
|
+
const inPackage = (p, rel) => join9(p.dir, rel);
|
|
1703
1972
|
const asList = (v) => v === void 0 ? [] : Array.isArray(v) ? v : [v];
|
|
1704
1973
|
const patternRoots = [
|
|
1705
1974
|
{ dir: patternsDir, ...config.exclude?.length ? { exclude: config.exclude } : {} },
|
|
@@ -1710,22 +1979,22 @@ function resolveServerConfig(config, cwd, opts = {}, packages = []) {
|
|
|
1710
1979
|
}))
|
|
1711
1980
|
];
|
|
1712
1981
|
const styleRoots = [
|
|
1713
|
-
{ dir: stylesDir, entries: stylesEntries },
|
|
1982
|
+
{ dir: stylesDir, entries: stylesEntries, base: config.documentBase },
|
|
1714
1983
|
...packages.filter((p) => p.manifest.styles).map((p) => {
|
|
1715
1984
|
const dir = inPackage(p, p.manifest.styles.dir);
|
|
1716
|
-
const entries = asList(p.manifest.styles.entry).map((e) =>
|
|
1717
|
-
return { dir, entries: entries.length ? entries : void 0, source: p.name };
|
|
1985
|
+
const entries = asList(p.manifest.styles.entry).map((e) => resolve5(dir, e));
|
|
1986
|
+
return { dir, entries: entries.length ? entries : void 0, source: p.name, base: p.manifest.styles.documentBase };
|
|
1718
1987
|
})
|
|
1719
1988
|
];
|
|
1720
1989
|
const themePackages = packages.filter((p) => p.manifest.themes);
|
|
1721
1990
|
const themeDirs = [
|
|
1722
|
-
...config.themes ? [
|
|
1991
|
+
...config.themes ? [resolve5(cwd, config.themes.dir)] : [],
|
|
1723
1992
|
...themePackages.map((p) => inPackage(p, p.manifest.themes.dir))
|
|
1724
1993
|
];
|
|
1725
1994
|
const switches = [config.themes?.switch, ...themePackages.map((p) => p.manifest.themes.switch)];
|
|
1726
1995
|
const themeSwitch = switches.includes("stylesheet") ? "stylesheet" : "attribute";
|
|
1727
1996
|
const assetRoots = [
|
|
1728
|
-
...asList(config.assets?.dir).map((d) =>
|
|
1997
|
+
...asList(config.assets?.dir).map((d) => resolve5(cwd, d)),
|
|
1729
1998
|
...packages.flatMap((p) => (p.manifest.assets ?? []).map((a) => inPackage(p, a)))
|
|
1730
1999
|
];
|
|
1731
2000
|
const globalScripts = [.../* @__PURE__ */ new Set([
|
|
@@ -1733,7 +2002,7 @@ function resolveServerConfig(config, cwd, opts = {}, packages = []) {
|
|
|
1733
2002
|
...config.globalScripts ?? []
|
|
1734
2003
|
])];
|
|
1735
2004
|
const scriptsDirs = [
|
|
1736
|
-
...config.scriptsDir ? [
|
|
2005
|
+
...config.scriptsDir ? [resolve5(cwd, config.scriptsDir)] : [],
|
|
1737
2006
|
...packages.filter((p) => p.manifest.scripts).map((p) => inPackage(p, p.manifest.scripts))
|
|
1738
2007
|
];
|
|
1739
2008
|
const documentBaseEnabled = Array.isArray(config.documentBase) && config.documentBase.length > 0 || packages.some((p) => (p.manifest.styles?.documentBase?.length ?? 0) > 0);
|
|
@@ -1748,11 +2017,11 @@ function resolveServerConfig(config, cwd, opts = {}, packages = []) {
|
|
|
1748
2017
|
styleRoots,
|
|
1749
2018
|
dataDirs: [
|
|
1750
2019
|
...packages.filter((p) => p.manifest.data).map((p) => inPackage(p, p.manifest.data)),
|
|
1751
|
-
...config.dataDir ? [
|
|
2020
|
+
...config.dataDir ? [resolve5(cwd, config.dataDir)] : []
|
|
1752
2021
|
],
|
|
1753
2022
|
helperPaths: [
|
|
1754
2023
|
...packages.filter((p) => p.manifest.helpers).map((p) => inPackage(p, p.manifest.helpers)),
|
|
1755
|
-
...config.helpers ? [
|
|
2024
|
+
...config.helpers ? [resolve5(cwd, config.helpers)] : []
|
|
1756
2025
|
],
|
|
1757
2026
|
scriptsDirs,
|
|
1758
2027
|
patternsDir,
|
|
@@ -1777,14 +2046,14 @@ function resolveServerConfig(config, cwd, opts = {}, packages = []) {
|
|
|
1777
2046
|
}
|
|
1778
2047
|
|
|
1779
2048
|
// packages/dev-server/src/packages.ts
|
|
1780
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1781
|
-
import { dirname as
|
|
2049
|
+
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
2050
|
+
import { dirname as dirname3, join as join10 } from "node:path";
|
|
1782
2051
|
function findPackageDir(name, root) {
|
|
1783
2052
|
let dir = root;
|
|
1784
2053
|
for (; ; ) {
|
|
1785
|
-
const candidate =
|
|
1786
|
-
if (
|
|
1787
|
-
const parent =
|
|
2054
|
+
const candidate = join10(dir, "node_modules", name);
|
|
2055
|
+
if (existsSync2(join10(candidate, "package.json"))) return candidate;
|
|
2056
|
+
const parent = dirname3(dir);
|
|
1788
2057
|
if (parent === dir) return null;
|
|
1789
2058
|
dir = parent;
|
|
1790
2059
|
}
|
|
@@ -1799,7 +2068,7 @@ function loadPatternPackages(references, root) {
|
|
|
1799
2068
|
if (!direct) return;
|
|
1800
2069
|
throw new Error(`pattern package "${name}" is not installed \u2014 run: npm install --save-dev ${name}`);
|
|
1801
2070
|
}
|
|
1802
|
-
const pkg = JSON.parse(readFileSync(
|
|
2071
|
+
const pkg = JSON.parse(readFileSync(join10(dir, "package.json"), "utf8"));
|
|
1803
2072
|
if (!pkg.patternDump) {
|
|
1804
2073
|
if (!direct) return;
|
|
1805
2074
|
throw new Error(
|
|
@@ -1855,7 +2124,7 @@ async function runDiagnosticsAudit(config, cwd) {
|
|
|
1855
2124
|
|
|
1856
2125
|
// packages/dev-server/src/static-build.ts
|
|
1857
2126
|
import { cp, mkdir as mkdir3, rm, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
|
|
1858
|
-
import { dirname as
|
|
2127
|
+
import { dirname as dirname4, join as join11, relative as relative4, resolve as resolve6, sep } from "node:path";
|
|
1859
2128
|
|
|
1860
2129
|
// packages/dev-server/src/http-utils.ts
|
|
1861
2130
|
import { readFile as readFile7 } from "node:fs/promises";
|
|
@@ -1901,6 +2170,12 @@ async function serveFile(res, filePath, contentType) {
|
|
|
1901
2170
|
res.end("Not found");
|
|
1902
2171
|
}
|
|
1903
2172
|
}
|
|
2173
|
+
var RESERVED_PREFIXES = ["/_pd/", "/api/", PD_ROUTE_PREFIX.preview, PD_ROUTE_PREFIX.source, PD_ROUTE_PREFIX.assets];
|
|
2174
|
+
function servesShell(pathname, accept) {
|
|
2175
|
+
if (RESERVED_PREFIXES.some((p) => pathname.startsWith(p))) return false;
|
|
2176
|
+
const last = pathname.slice(pathname.lastIndexOf("/") + 1);
|
|
2177
|
+
return !/\.[a-z0-9]+$/i.test(last) || (accept ?? "").includes("text/html");
|
|
2178
|
+
}
|
|
1904
2179
|
function buildShellHtml(runtime) {
|
|
1905
2180
|
return `<!DOCTYPE html>
|
|
1906
2181
|
<html lang="en">
|
|
@@ -1927,7 +2202,7 @@ function withBasePath(text, basePath) {
|
|
|
1927
2202
|
}
|
|
1928
2203
|
var isText = (contentType) => /\b(text\/html|text\/css|application\/json)\b/.test(contentType);
|
|
1929
2204
|
function diskPath(outDir, urlPath) {
|
|
1930
|
-
return
|
|
2205
|
+
return join11(outDir, ...urlPath.split("/").map((seg) => decodeURIComponent(seg)));
|
|
1931
2206
|
}
|
|
1932
2207
|
async function buildStaticSite(config, root, opts) {
|
|
1933
2208
|
const started = performance.now();
|
|
@@ -1936,13 +2211,13 @@ async function buildStaticSite(config, root, opts) {
|
|
|
1936
2211
|
const log = (line) => logLine(`${line} (${((performance.now() - started) / 1e3).toFixed(1)}s)`);
|
|
1937
2212
|
const basePath = normalizeBasePath(opts.basePath ?? config.build?.basePath);
|
|
1938
2213
|
const includeAudit = opts.audit ?? config.build?.audit ?? true;
|
|
1939
|
-
const outDir =
|
|
2214
|
+
const outDir = resolve6(opts.outDir);
|
|
1940
2215
|
const cfg = resolveServerConfig(config, root, {}, loadPatternPackages(config.packages, root));
|
|
1941
2216
|
const inside = (child, parent) => {
|
|
1942
|
-
const rel =
|
|
2217
|
+
const rel = relative4(parent, child);
|
|
1943
2218
|
return rel === "" || !rel.startsWith("..") && !rel.startsWith(sep) && !/^[a-zA-Z]:/.test(rel);
|
|
1944
2219
|
};
|
|
1945
|
-
const project =
|
|
2220
|
+
const project = resolve6(root);
|
|
1946
2221
|
if (inside(project, outDir)) throw new Error(`pd build: --out ${outDir} would contain the project itself \u2014 choose a folder inside or beside it, like ./dist`);
|
|
1947
2222
|
for (const source of [cfg.patternsDir, cfg.stylesDir, ...cfg.themeDirs, ...cfg.assetRoots]) {
|
|
1948
2223
|
if (inside(outDir, source) || inside(source, outDir)) {
|
|
@@ -1970,7 +2245,7 @@ async function buildStaticSite(config, root, opts) {
|
|
|
1970
2245
|
}
|
|
1971
2246
|
const type = res.headers.get("content-type") ?? "";
|
|
1972
2247
|
const target = diskPath(outDir, file);
|
|
1973
|
-
await mkdir3(
|
|
2248
|
+
await mkdir3(dirname4(target), { recursive: true });
|
|
1974
2249
|
if (isText(type)) {
|
|
1975
2250
|
const body = await res.text();
|
|
1976
2251
|
const out = withBasePath(body, basePath);
|
|
@@ -1990,7 +2265,7 @@ async function buildStaticSite(config, root, opts) {
|
|
|
1990
2265
|
if (done.has(file)) return;
|
|
1991
2266
|
done.add(file);
|
|
1992
2267
|
const target = diskPath(outDir, file);
|
|
1993
|
-
await mkdir3(
|
|
2268
|
+
await mkdir3(dirname4(target), { recursive: true });
|
|
1994
2269
|
const out = withBasePath(body, basePath);
|
|
1995
2270
|
await writeFile3(target, out);
|
|
1996
2271
|
report.bytes += Buffer.byteLength(out);
|
|
@@ -2056,8 +2331,8 @@ async function buildStaticSite(config, root, opts) {
|
|
|
2056
2331
|
for (const [i, rootDir] of cfg.assetRoots.entries()) {
|
|
2057
2332
|
const exists = await stat2(rootDir).then((s) => s.isDirectory(), () => false);
|
|
2058
2333
|
if (!exists) continue;
|
|
2059
|
-
await cp(rootDir,
|
|
2060
|
-
if (cfg.assetRoots.length === 1) await cp(rootDir,
|
|
2334
|
+
await cp(rootDir, join11(outDir, "assets", cfg.assetRootNames[i]), { recursive: true });
|
|
2335
|
+
if (cfg.assetRoots.length === 1) await cp(rootDir, join11(outDir, "assets"), { recursive: true, force: false });
|
|
2061
2336
|
}
|
|
2062
2337
|
const runtime = {
|
|
2063
2338
|
mode: "static",
|
|
@@ -2069,12 +2344,12 @@ async function buildStaticSite(config, root, opts) {
|
|
|
2069
2344
|
const shell = withBasePath(buildShellHtml(runtime), basePath);
|
|
2070
2345
|
for (const route of routes) {
|
|
2071
2346
|
const target = diskPath(outDir, staticPathFor(route));
|
|
2072
|
-
await mkdir3(
|
|
2347
|
+
await mkdir3(dirname4(target), { recursive: true });
|
|
2073
2348
|
await writeFile3(target, shell);
|
|
2074
2349
|
report.files++;
|
|
2075
2350
|
report.bytes += Buffer.byteLength(shell);
|
|
2076
2351
|
}
|
|
2077
|
-
await writeFile3(
|
|
2352
|
+
await writeFile3(join11(outDir, ".nojekyll"), "");
|
|
2078
2353
|
report.files++;
|
|
2079
2354
|
} finally {
|
|
2080
2355
|
await server.close();
|
|
@@ -2093,6 +2368,7 @@ function createServerState() {
|
|
|
2093
2368
|
scssGraphDirty: true,
|
|
2094
2369
|
perComponentCss: /* @__PURE__ */ new Map(),
|
|
2095
2370
|
baseCss: "",
|
|
2371
|
+
baseCssVersion: 0,
|
|
2096
2372
|
compositionRegistry: null,
|
|
2097
2373
|
discoveryResult: null,
|
|
2098
2374
|
auditSlices: null,
|
|
@@ -2102,27 +2378,46 @@ function createServerState() {
|
|
|
2102
2378
|
scriptCache: /* @__PURE__ */ new Map()
|
|
2103
2379
|
};
|
|
2104
2380
|
}
|
|
2381
|
+
function applyStyleData(state, data) {
|
|
2382
|
+
const firstBuild = state.classOwnerIndex === null;
|
|
2383
|
+
const before = state.perComponentCss;
|
|
2384
|
+
const changedComponents = [];
|
|
2385
|
+
if (!firstBuild) {
|
|
2386
|
+
for (const id of /* @__PURE__ */ new Set([...before.keys(), ...data.componentCssMap.keys()])) {
|
|
2387
|
+
if ((before.get(id) ?? "") !== (data.componentCssMap.get(id) ?? "")) changedComponents.push(id);
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
const baseChanged = !firstBuild && state.baseCss !== data.baseCss;
|
|
2391
|
+
if (baseChanged) state.baseCssVersion += 1;
|
|
2392
|
+
state.scssModuleGraph = data.scssModuleGraph;
|
|
2393
|
+
state.classOwnerIndex = data.classOwnerIndex;
|
|
2394
|
+
state.baseProvenance = data.baseProvenance;
|
|
2395
|
+
state.perComponentCss = data.componentCssMap;
|
|
2396
|
+
state.baseCss = data.baseCss;
|
|
2397
|
+
state.scssGraphDirty = false;
|
|
2398
|
+
return { changedComponents: changedComponents.sort(), baseChanged };
|
|
2399
|
+
}
|
|
2105
2400
|
|
|
2106
2401
|
// packages/dev-server/src/routes.ts
|
|
2107
|
-
import { basename as basename4, extname as extname2, join as
|
|
2402
|
+
import { basename as basename4, extname as extname2, join as join18 } from "node:path";
|
|
2108
2403
|
import { readFile as readFile15, stat as stat4 } from "node:fs/promises";
|
|
2109
|
-
import { existsSync as
|
|
2404
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
2110
2405
|
|
|
2111
2406
|
// packages/dev-server/src/component-tree.ts
|
|
2112
2407
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
2113
|
-
import { join as
|
|
2408
|
+
import { join as join14 } from "node:path";
|
|
2114
2409
|
|
|
2115
2410
|
// packages/dev-server/src/layout-mode.ts
|
|
2116
2411
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
2117
|
-
import { join as
|
|
2412
|
+
import { join as join13 } from "node:path";
|
|
2118
2413
|
|
|
2119
2414
|
// packages/dev-server/src/pattern-dirs.ts
|
|
2120
2415
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
2121
|
-
import { join as
|
|
2416
|
+
import { join as join12 } from "node:path";
|
|
2122
2417
|
async function readFolderFile(dirs, rel) {
|
|
2123
2418
|
for (const dir of typeof dirs === "string" ? [dirs] : dirs) {
|
|
2124
2419
|
try {
|
|
2125
|
-
return await readFile8(
|
|
2420
|
+
return await readFile8(join12(dir, rel), "utf-8");
|
|
2126
2421
|
} catch {
|
|
2127
2422
|
}
|
|
2128
2423
|
}
|
|
@@ -2135,7 +2430,7 @@ async function resolveLayoutMode(componentId, patternsDir, discoveryMap) {
|
|
|
2135
2430
|
const parts = componentId.split("/");
|
|
2136
2431
|
const category = parts[0];
|
|
2137
2432
|
let layout = FULL_CATEGORIES.has(category) ? "full" : "centered";
|
|
2138
|
-
const categoryIndex = await readFolderFile(patternsDir,
|
|
2433
|
+
const categoryIndex = await readFolderFile(patternsDir, join13(category, "index.md"));
|
|
2139
2434
|
if (categoryIndex !== null) {
|
|
2140
2435
|
const m = categoryIndex.match(/^preview:\s*(centered|full)$/m);
|
|
2141
2436
|
if (m) layout = m[1];
|
|
@@ -2287,7 +2582,7 @@ async function buildTreeNodeFromMap(nodePath, idsUnderPath, patternsDir, overvie
|
|
|
2287
2582
|
if (o && "weight" in o) weight = o.weight;
|
|
2288
2583
|
}
|
|
2289
2584
|
}
|
|
2290
|
-
const indexMd = await readFolderFile(patternsDir,
|
|
2585
|
+
const indexMd = await readFolderFile(patternsDir, join14(nodePath, "index.md"));
|
|
2291
2586
|
const folderOrder = orderOf(indexMd);
|
|
2292
2587
|
const built = [];
|
|
2293
2588
|
for (const childPath of directChildPaths(nodePath, idsUnderPath)) {
|
|
@@ -2497,197 +2792,7 @@ import postcss4 from "postcss";
|
|
|
2497
2792
|
import * as sass3 from "sass";
|
|
2498
2793
|
import { SourceMapConsumer as SourceMapConsumer2 } from "source-map";
|
|
2499
2794
|
import { parse as parseHtml2 } from "node-html-parser";
|
|
2500
|
-
|
|
2501
|
-
// packages/dev-server/src/style-index.ts
|
|
2502
|
-
import { dirname as dirname4, relative as relative3, resolve as resolve6 } from "node:path";
|
|
2503
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2504
|
-
import postcss3 from "postcss";
|
|
2505
|
-
import { SourceMapConsumer } from "source-map";
|
|
2506
|
-
import * as sass2 from "sass";
|
|
2507
|
-
|
|
2508
|
-
// packages/dev-server/src/timing.ts
|
|
2509
|
-
var verboseTiming = !!process.env["PD_VERBOSE_TIMING"];
|
|
2510
|
-
|
|
2511
|
-
// packages/dev-server/src/style-index.ts
|
|
2512
|
-
function resolveScssToComponent(absPath, stylesDir, componentIds) {
|
|
2513
|
-
const rel = relative3(stylesDir, absPath);
|
|
2514
|
-
let normalized = rel.replace(/\.scss$/, "").split("/").map((seg) => seg.replace(/^\d+[-_]/, "").replace(/^_/, "")).join("/");
|
|
2515
|
-
if (normalized.endsWith("/index")) normalized = normalized.slice(0, -6);
|
|
2516
|
-
const id = collapseComponentId(normalized);
|
|
2517
|
-
return componentIds.has(id) ? id : null;
|
|
2518
|
-
}
|
|
2519
|
-
function resolveSharedModulePath(absPath, stylesDir, keepBarrel = false) {
|
|
2520
|
-
const rel = relative3(stylesDir, absPath);
|
|
2521
|
-
if (rel.startsWith("..")) return null;
|
|
2522
|
-
const parts = rel.replace(/\.scss$/, "").split("/");
|
|
2523
|
-
const normalized = parts.map(
|
|
2524
|
-
(seg, i) => i === parts.length - 1 ? seg.replace(/^_/, "") : seg
|
|
2525
|
-
);
|
|
2526
|
-
if (!keepBarrel && normalized[normalized.length - 1] === "index") return null;
|
|
2527
|
-
return normalized.join("/");
|
|
2528
|
-
}
|
|
2529
|
-
function extractClassesFromCompiled(consumer, root, entryFile, stylesDir, componentIds) {
|
|
2530
|
-
const classMap = /* @__PURE__ */ new Map();
|
|
2531
|
-
const baseModules = /* @__PURE__ */ new Map();
|
|
2532
|
-
root.walkRules((rule) => {
|
|
2533
|
-
const classMatches = rule.selector.match(/\.([\w-]+)/g);
|
|
2534
|
-
if (!classMatches) return;
|
|
2535
|
-
const pos = rule.source?.start;
|
|
2536
|
-
if (!pos) return;
|
|
2537
|
-
const original = consumer.originalPositionFor({ line: pos.line, column: pos.column - 1 });
|
|
2538
|
-
if (!original.source) return;
|
|
2539
|
-
const originPath = original.source.startsWith("file://") ? fileURLToPath2(original.source) : resolve6(dirname4(entryFile), original.source);
|
|
2540
|
-
const resolved = resolveScssToComponent(originPath, stylesDir, componentIds);
|
|
2541
|
-
const owner = resolved ?? "__base__";
|
|
2542
|
-
const modulePath = resolved === null ? resolveSharedModulePath(originPath, stylesDir, true) : null;
|
|
2543
|
-
for (const cls of classMatches) {
|
|
2544
|
-
const name = cls.slice(1);
|
|
2545
|
-
if (!classMap.has(name)) classMap.set(name, owner);
|
|
2546
|
-
if (modulePath && classMap.get(name) === "__base__" && !baseModules.has(name)) {
|
|
2547
|
-
baseModules.set(name, modulePath);
|
|
2548
|
-
}
|
|
2549
|
-
}
|
|
2550
|
-
});
|
|
2551
|
-
return { classMap, baseModules };
|
|
2552
|
-
}
|
|
2553
|
-
async function attributeClassesFromFile(entryFile, stylesDir, componentIds) {
|
|
2554
|
-
try {
|
|
2555
|
-
const compiled = sass2.compile(entryFile, { sourceMap: true });
|
|
2556
|
-
if (!compiled.sourceMap) return null;
|
|
2557
|
-
const consumer = new SourceMapConsumer(compiled.sourceMap);
|
|
2558
|
-
const root = postcss3.parse(compiled.css);
|
|
2559
|
-
const { classMap } = extractClassesFromCompiled(consumer, root, entryFile, stylesDir, componentIds);
|
|
2560
|
-
return classMap;
|
|
2561
|
-
} catch {
|
|
2562
|
-
return null;
|
|
2563
|
-
}
|
|
2564
|
-
}
|
|
2565
|
-
async function patchClassOwnerIndex(entryFile, changedCompId, stylesDir, componentIds, index) {
|
|
2566
|
-
for (const [cls, owner] of index) {
|
|
2567
|
-
if (owner === changedCompId) index.delete(cls);
|
|
2568
|
-
}
|
|
2569
|
-
const fresh = await attributeClassesFromFile(entryFile, stylesDir, componentIds);
|
|
2570
|
-
if (!fresh) return false;
|
|
2571
|
-
for (const [name, owner] of fresh) {
|
|
2572
|
-
if (owner === changedCompId && !index.has(name)) index.set(name, owner);
|
|
2573
|
-
}
|
|
2574
|
-
return true;
|
|
2575
|
-
}
|
|
2576
|
-
async function buildAllStyleData(roots, componentIds, entryOverride) {
|
|
2577
|
-
if (verboseTiming) console.time("[dev-server:timing] full classOwnerIndex build");
|
|
2578
|
-
const styleUsesModules = /* @__PURE__ */ new Map();
|
|
2579
|
-
const classIndex = /* @__PURE__ */ new Map();
|
|
2580
|
-
const baseProvenance = /* @__PURE__ */ new Map();
|
|
2581
|
-
const componentCssBuckets = /* @__PURE__ */ new Map();
|
|
2582
|
-
const baseCssByRoot = [];
|
|
2583
|
-
for (const id of componentIds) styleUsesModules.set(id, []);
|
|
2584
|
-
const rootList = typeof roots === "string" ? [{ dir: roots, ...entryOverride ? { entries: entryOverride } : {} }] : roots;
|
|
2585
|
-
for (const styleRoot of rootList) {
|
|
2586
|
-
const stylesDir = styleRoot.dir;
|
|
2587
|
-
const baseCssBucket = /* @__PURE__ */ new Set();
|
|
2588
|
-
baseCssByRoot.push([]);
|
|
2589
|
-
let entryFiles = [];
|
|
2590
|
-
if (styleRoot.entries && styleRoot.entries.length > 0) {
|
|
2591
|
-
entryFiles = [...styleRoot.entries];
|
|
2592
|
-
} else {
|
|
2593
|
-
try {
|
|
2594
|
-
entryFiles = await collectScssFiles(stylesDir);
|
|
2595
|
-
} catch {
|
|
2596
|
-
entryFiles = [];
|
|
2597
|
-
}
|
|
2598
|
-
}
|
|
2599
|
-
entryFiles.sort();
|
|
2600
|
-
for (const entryFile of entryFiles) {
|
|
2601
|
-
let originOwnerFor2 = function(node) {
|
|
2602
|
-
const pos = node.source?.start;
|
|
2603
|
-
if (!pos) return null;
|
|
2604
|
-
const original = consumer.originalPositionFor({
|
|
2605
|
-
line: pos.line,
|
|
2606
|
-
column: pos.column - 1
|
|
2607
|
-
});
|
|
2608
|
-
if (!original.source) return null;
|
|
2609
|
-
const originPath = original.source.startsWith("file://") ? fileURLToPath2(original.source) : resolve6(dirname4(entryFile), original.source);
|
|
2610
|
-
const originComp = resolveScssToComponent(originPath, stylesDir, componentIds);
|
|
2611
|
-
return originComp ?? "__base__";
|
|
2612
|
-
};
|
|
2613
|
-
var originOwnerFor = originOwnerFor2;
|
|
2614
|
-
const compOwner = resolveScssToComponent(entryFile, stylesDir, componentIds);
|
|
2615
|
-
let compiled;
|
|
2616
|
-
try {
|
|
2617
|
-
compiled = sass2.compile(entryFile, { sourceMap: true });
|
|
2618
|
-
} catch {
|
|
2619
|
-
continue;
|
|
2620
|
-
}
|
|
2621
|
-
if (compOwner) {
|
|
2622
|
-
const deps = compiled.loadedUrls.filter((u) => u.protocol === "file:").map((u) => fileURLToPath2(u)).filter((p) => resolve6(p) !== resolve6(entryFile));
|
|
2623
|
-
for (const dep of deps) {
|
|
2624
|
-
const depComp = resolveScssToComponent(dep, stylesDir, componentIds);
|
|
2625
|
-
if (depComp !== null && depComp !== compOwner) {
|
|
2626
|
-
const rawRel = relative3(stylesDir, dep).replace(/\.scss$/, "");
|
|
2627
|
-
const modPath = rawRel.endsWith("/_index") ? rawRel.slice(0, -7) : rawRel;
|
|
2628
|
-
if (modPath) {
|
|
2629
|
-
const mArr = styleUsesModules.get(compOwner) ?? [];
|
|
2630
|
-
if (!mArr.includes(modPath)) {
|
|
2631
|
-
mArr.push(modPath);
|
|
2632
|
-
styleUsesModules.set(compOwner, mArr);
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
} else if (depComp === null) {
|
|
2636
|
-
const modPath = resolveSharedModulePath(dep, stylesDir);
|
|
2637
|
-
if (modPath) {
|
|
2638
|
-
const mArr = styleUsesModules.get(compOwner) ?? [];
|
|
2639
|
-
if (!mArr.includes(modPath)) {
|
|
2640
|
-
mArr.push(modPath);
|
|
2641
|
-
styleUsesModules.set(compOwner, mArr);
|
|
2642
|
-
}
|
|
2643
|
-
}
|
|
2644
|
-
}
|
|
2645
|
-
}
|
|
2646
|
-
}
|
|
2647
|
-
const rawMap = compiled.sourceMap;
|
|
2648
|
-
if (!rawMap) continue;
|
|
2649
|
-
const consumer = new SourceMapConsumer(rawMap);
|
|
2650
|
-
const root = postcss3.parse(compiled.css);
|
|
2651
|
-
root.each((node) => {
|
|
2652
|
-
const owner = originOwnerFor2(node);
|
|
2653
|
-
if (owner === null) return;
|
|
2654
|
-
const nodeText = node.toString();
|
|
2655
|
-
if (owner === "__base__") {
|
|
2656
|
-
baseCssBucket.add(nodeText);
|
|
2657
|
-
} else {
|
|
2658
|
-
const bucket = componentCssBuckets.get(owner) ?? /* @__PURE__ */ new Set();
|
|
2659
|
-
bucket.add(nodeText);
|
|
2660
|
-
componentCssBuckets.set(owner, bucket);
|
|
2661
|
-
}
|
|
2662
|
-
});
|
|
2663
|
-
const { classMap: perFileClasses, baseModules: perFileBaseModules } = extractClassesFromCompiled(consumer, root, entryFile, stylesDir, componentIds);
|
|
2664
|
-
for (const [name, owner] of perFileClasses) {
|
|
2665
|
-
if (!classIndex.has(name)) classIndex.set(name, owner);
|
|
2666
|
-
}
|
|
2667
|
-
for (const [name, mod] of perFileBaseModules) {
|
|
2668
|
-
if (!baseProvenance.has(name)) baseProvenance.set(name, mod);
|
|
2669
|
-
}
|
|
2670
|
-
}
|
|
2671
|
-
baseCssByRoot[baseCssByRoot.length - 1] = [...baseCssBucket];
|
|
2672
|
-
}
|
|
2673
|
-
for (const [k, v] of styleUsesModules) styleUsesModules.set(k, [...new Set(v)].sort());
|
|
2674
|
-
const componentCssMap = /* @__PURE__ */ new Map();
|
|
2675
|
-
for (const [id, bucket] of componentCssBuckets) {
|
|
2676
|
-
componentCssMap.set(id, [...bucket].join("\n"));
|
|
2677
|
-
}
|
|
2678
|
-
if (verboseTiming) console.timeEnd("[dev-server:timing] full classOwnerIndex build");
|
|
2679
|
-
return {
|
|
2680
|
-
scssModuleGraph: { styleUsesModules },
|
|
2681
|
-
classOwnerIndex: classIndex,
|
|
2682
|
-
baseProvenance,
|
|
2683
|
-
componentCssMap,
|
|
2684
|
-
// Packages' base rules first (later roots), the project's last — its own base wins the cascade.
|
|
2685
|
-
baseCss: [...baseCssByRoot].reverse().flat().join("\n")
|
|
2686
|
-
};
|
|
2687
|
-
}
|
|
2688
|
-
|
|
2689
|
-
// packages/dev-server/src/css-closure.ts
|
|
2690
|
-
async function recomputeComponentCss(entryFile, compId, stylesDir, componentIds) {
|
|
2795
|
+
async function recomputeComponentCss(entryFile, compId, stylesDir, componentIds, inBase = () => false) {
|
|
2691
2796
|
try {
|
|
2692
2797
|
const compiled = sass3.compile(entryFile, { sourceMap: true });
|
|
2693
2798
|
const rawMap = compiled.sourceMap;
|
|
@@ -2701,10 +2806,11 @@ async function recomputeComponentCss(entryFile, compId, stylesDir, componentIds)
|
|
|
2701
2806
|
const original = consumer.originalPositionFor({ line: pos.line, column: pos.column - 1 });
|
|
2702
2807
|
if (!original.source) return;
|
|
2703
2808
|
const originPath = original.source.startsWith("file://") ? fileURLToPath3(original.source) : resolve7(dirname5(entryFile), original.source);
|
|
2809
|
+
if (inBase(originPath)) return;
|
|
2704
2810
|
const originComp = resolveScssToComponent(originPath, stylesDir, componentIds);
|
|
2705
|
-
if (originComp === compId) bucket.add(node
|
|
2811
|
+
if (originComp === compId) bucket.add(cssNodeText(node));
|
|
2706
2812
|
});
|
|
2707
|
-
return
|
|
2813
|
+
return joinCssChunks(bucket);
|
|
2708
2814
|
} catch {
|
|
2709
2815
|
return null;
|
|
2710
2816
|
}
|
|
@@ -2777,7 +2883,7 @@ function linkableClosure(closureIds, perComponentCss) {
|
|
|
2777
2883
|
|
|
2778
2884
|
// packages/dev-server/src/requires.ts
|
|
2779
2885
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
2780
|
-
import { join as
|
|
2886
|
+
import { join as join15, basename as basename3 } from "node:path";
|
|
2781
2887
|
function normalizeScriptId(raw) {
|
|
2782
2888
|
let s = raw.trim().replace(/^\[(.+)\]$/, "$1").trim();
|
|
2783
2889
|
s = basename3(s);
|
|
@@ -2832,7 +2938,7 @@ async function loadSvgAssets(ids, assetsDir) {
|
|
|
2832
2938
|
for (const id of ids) {
|
|
2833
2939
|
for (const candidate of [`${id}.svg`, `images/${id}.svg`]) {
|
|
2834
2940
|
try {
|
|
2835
|
-
const raw = await readFile13(
|
|
2941
|
+
const raw = await readFile13(join15(assetsDir, candidate), "utf-8");
|
|
2836
2942
|
contents.push(hideSvg(raw));
|
|
2837
2943
|
break;
|
|
2838
2944
|
} catch {
|
|
@@ -2966,13 +3072,13 @@ async function buildComponentDetail(componentId, patternsDir, renderFn, tracker,
|
|
|
2966
3072
|
}
|
|
2967
3073
|
|
|
2968
3074
|
// packages/dev-server/src/overview.ts
|
|
2969
|
-
import { join as
|
|
3075
|
+
import { join as join16 } from "node:path";
|
|
2970
3076
|
async function buildFolderOverview(folderPath, patternsDir, renderFn, discoveryMap) {
|
|
2971
3077
|
const category = folderPath.split("/")[0];
|
|
2972
3078
|
let folderLayout = FULL_CATEGORIES.has(category) ? "full" : "centered";
|
|
2973
3079
|
let doc;
|
|
2974
3080
|
try {
|
|
2975
|
-
const rawIndex = await readFolderFile(patternsDir,
|
|
3081
|
+
const rawIndex = await readFolderFile(patternsDir, join16(folderPath, "index.md"));
|
|
2976
3082
|
if (rawIndex === null) throw new Error("no index.md");
|
|
2977
3083
|
const m = rawIndex.match(/^preview:\s*(centered|full)$/m);
|
|
2978
3084
|
if (m) folderLayout = m[1];
|
|
@@ -3010,13 +3116,13 @@ async function collectPublicLeaves(folderPath, patternsDir, discoveryMap) {
|
|
|
3010
3116
|
|
|
3011
3117
|
// packages/dev-server/src/preview-document.ts
|
|
3012
3118
|
import { stat as stat3 } from "node:fs/promises";
|
|
3013
|
-
import { join as
|
|
3119
|
+
import { join as join17 } from "node:path";
|
|
3014
3120
|
async function buildScriptVersions(ids, dirs) {
|
|
3015
3121
|
const map = /* @__PURE__ */ new Map();
|
|
3016
3122
|
await Promise.all(ids.map(async (id) => {
|
|
3017
3123
|
for (const dir of Array.isArray(dirs) ? dirs : [dirs]) {
|
|
3018
3124
|
try {
|
|
3019
|
-
const { mtimeMs } = await stat3(
|
|
3125
|
+
const { mtimeMs } = await stat3(join17(dir, `${id}.ts`));
|
|
3020
3126
|
map.set(id, mtimeMs);
|
|
3021
3127
|
return;
|
|
3022
3128
|
} catch {
|
|
@@ -3025,25 +3131,59 @@ async function buildScriptVersions(ids, dirs) {
|
|
|
3025
3131
|
}));
|
|
3026
3132
|
return map;
|
|
3027
3133
|
}
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3134
|
+
var CONTENT_WIDTH_SCRIPT = `
|
|
3135
|
+
function pdContentWidth(){
|
|
3136
|
+
function px(v){ return parseFloat(v) || 0; }
|
|
3137
|
+
function need(el){
|
|
3138
|
+
var right = 0, cr = el.getBoundingClientRect().right, es = getComputedStyle(el);
|
|
3139
|
+
var contentRight = cr - px(es.paddingRight) - px(es.borderRightWidth);
|
|
3140
|
+
for (var c = el.firstChild; c; c = c.nextSibling) {
|
|
3141
|
+
if (c.nodeType === 3) {
|
|
3142
|
+
if (!/\\S/.test(c.data)) continue;
|
|
3143
|
+
var rg = document.createRange(); rg.selectNodeContents(c);
|
|
3144
|
+
var rs = rg.getClientRects();
|
|
3145
|
+
for (var i = 0; i < rs.length; i++) right = Math.max(right, rs[i].right);
|
|
3146
|
+
continue;
|
|
3147
|
+
}
|
|
3148
|
+
if (c.nodeType !== 1) continue;
|
|
3149
|
+
var tag = c.tagName.toUpperCase();
|
|
3150
|
+
if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'TEMPLATE' || tag === 'NOSCRIPT') continue;
|
|
3151
|
+
var cs = getComputedStyle(c);
|
|
3152
|
+
if (cs.display === 'none') continue;
|
|
3153
|
+
var r = c.getBoundingClientRect();
|
|
3154
|
+
var box = /^(IMG|SVG|VIDEO|CANVAS|IFRAME|INPUT|SELECT|TEXTAREA|OBJECT|EMBED)$/.test(tag);
|
|
3155
|
+
if (cs.display === 'contents' || (!box && cs.display === 'inline') || (r.width === 0 && r.height === 0)) {
|
|
3156
|
+
right = Math.max(right, need(c));
|
|
3157
|
+
} else if (!box && !/grid|table/.test(cs.display) && r.right + px(cs.marginRight) >= contentRight - 0.5) {
|
|
3158
|
+
var inner = Math.max(need(c), r.left + px(cs.borderLeftWidth) + px(cs.paddingLeft));
|
|
3159
|
+
right = Math.max(right, inner + px(cs.paddingRight) + px(cs.borderRightWidth) + px(cs.marginRight));
|
|
3160
|
+
} else {
|
|
3161
|
+
right = Math.max(right, r.right + px(cs.marginRight));
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
return right;
|
|
3165
|
+
}
|
|
3166
|
+
var b = document.body, bs = getComputedStyle(b);
|
|
3167
|
+
return Math.ceil(need(b) + px(bs.paddingRight) + px(bs.borderRightWidth) + px(bs.marginRight));
|
|
3168
|
+
}
|
|
3169
|
+
`;
|
|
3170
|
+
function buildPreviewDocument(html, closureIds, layout, themeNames, defaultTheme, requireScripts, svgContents, assetsCss, componentId, baseHref, scriptVersions, linkBase = false, themeSwitch = "attribute", baseCssVersion = 0) {
|
|
3171
|
+
const componentAttr = componentId ? ` data-component-id="${escapeAttr(componentId)}"` : "";
|
|
3172
|
+
const bodyAttr = `${defaultTheme ? ` data-theme="${escapeAttr(defaultTheme)}"` : ""}${componentAttr}`;
|
|
3173
|
+
const widthPost = layout === "centered" ? `window.parent.postMessage({type:'pd-width',width:pdContentWidth(),at:window.innerWidth,scroll:document.documentElement.scrollWidth},'*');` : "";
|
|
3174
|
+
const resizeScript = layout === "centered" || layout === "full" ? `<script>(function(){${layout === "centered" ? CONTENT_WIDTH_SCRIPT : ""}var _t;new ResizeObserver(function(){clearTimeout(_t);_t=setTimeout(function(){window.parent.postMessage({type:'pd-height',height:document.documentElement.scrollHeight},'*');${widthPost}},50);}).observe(document.body);})();</script>` : "";
|
|
3034
3175
|
const baseTag = baseHref ? `<base href="${escapeAttr(baseHref)}">` : "";
|
|
3035
|
-
const
|
|
3036
|
-
const
|
|
3037
|
-
const
|
|
3038
|
-
const
|
|
3176
|
+
const baseHrefV = baseCssVersion > 0 ? `${PD_ROUTES.baseCss}?v=${baseCssVersion}` : PD_ROUTES.baseCss;
|
|
3177
|
+
const baseLink = linkBase ? `<link rel="stylesheet" href="${escapeAttr(baseHrefV)}">` : "";
|
|
3178
|
+
const assetCssLinks = (assetsCss ?? []).map((p) => `<link rel="stylesheet" href="${escapeAttr(p.includes("://") ? p : `/assets/${p}`)}">`).join("\n");
|
|
3179
|
+
const themeLinks = themeSwitch === "stylesheet" ? defaultTheme ? `<link rel="stylesheet" href="${escapeAttr(themeCssUrl(defaultTheme))}" data-pd-theme="${escapeAttr(defaultTheme)}">` : "" : themeNames.map((n) => `<link rel="stylesheet" href="${escapeAttr(themeCssUrl(n))}">`).join("\n");
|
|
3180
|
+
const closureLinks = closureIds.map((id) => `<link rel="stylesheet" href="${escapeAttr(componentCssUrl(id))}" data-pd-comp="${escapeAttr(id)}">`).join("\n");
|
|
3039
3181
|
const symbolSets = (svgContents ?? []).join("\n");
|
|
3040
3182
|
const scriptTags = (requireScripts ?? []).map((src) => {
|
|
3041
|
-
if (src.includes("://")) return `<script type="module" src="${src}"></script>`;
|
|
3183
|
+
if (src.includes("://")) return `<script type="module" src="${escapeAttr(src)}"></script>`;
|
|
3042
3184
|
const v = scriptVersions?.get(src);
|
|
3043
|
-
return `<script type="module" src="${scriptUrl(src, v ?? void 0)}"></script>`;
|
|
3185
|
+
return `<script type="module" src="${escapeAttr(scriptUrl(src, v ?? void 0))}"></script>`;
|
|
3044
3186
|
}).join("\n");
|
|
3045
|
-
const componentAttr = componentId ? ` data-component-id="${escapeAttr(componentId)}"` : "";
|
|
3046
|
-
const layoutStyle = layout === "full" ? ' style="width:100%;max-width:none"' : ' style="max-width:max-content;margin-inline:auto"';
|
|
3047
3187
|
return `<!DOCTYPE html>
|
|
3048
3188
|
<html lang="en">
|
|
3049
3189
|
<head>
|
|
@@ -3056,23 +3196,28 @@ ${closureLinks}
|
|
|
3056
3196
|
</head>
|
|
3057
3197
|
<body${bodyAttr}>
|
|
3058
3198
|
${symbolSets}
|
|
3059
|
-
|
|
3199
|
+
${html}
|
|
3060
3200
|
${resizeScript}
|
|
3061
3201
|
${scriptTags}
|
|
3062
3202
|
</body>
|
|
3063
3203
|
</html>`;
|
|
3064
3204
|
}
|
|
3065
|
-
function normalizeAssetRefs(
|
|
3066
|
-
if (rootNames.length === 0) return
|
|
3067
|
-
const alt =
|
|
3068
|
-
|
|
3205
|
+
function normalizeAssetRefs(text, rootNames) {
|
|
3206
|
+
if (rootNames.length === 0) return text;
|
|
3207
|
+
const alt = (names) => names.map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
3208
|
+
let out = text.replace(new RegExp(`(?:\\.\\.?/)+(${alt(rootNames)})/`, "g"), "/assets/$1/");
|
|
3209
|
+
const absNames = rootNames.filter((n) => n !== "assets");
|
|
3210
|
+
if (absNames.length > 0) {
|
|
3211
|
+
out = out.replace(new RegExp(`(^|["'(=,\\s])/(${alt(absNames)})/`, "g"), "$1/assets/$2/");
|
|
3212
|
+
}
|
|
3213
|
+
return out;
|
|
3069
3214
|
}
|
|
3070
3215
|
|
|
3071
3216
|
// packages/dev-server/src/assets.ts
|
|
3072
|
-
import { existsSync as
|
|
3217
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
3073
3218
|
import { createRequire as createRequire2 } from "node:module";
|
|
3074
3219
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
3075
|
-
function resolveDevUiDist(moduleUrl, exists =
|
|
3220
|
+
function resolveDevUiDist(moduleUrl, exists = existsSync3) {
|
|
3076
3221
|
const packaged = fileURLToPath4(new URL("./dev-ui/", moduleUrl));
|
|
3077
3222
|
if (exists(`${packaged}main.js`)) return packaged;
|
|
3078
3223
|
return fileURLToPath4(new URL("../../../apps/dev-ui/dist/", moduleUrl));
|
|
@@ -3108,10 +3253,10 @@ async function handleRequest(req, res, ctx) {
|
|
|
3108
3253
|
const url = new URL(req.url ?? "/", "http://x");
|
|
3109
3254
|
const pathname = url.pathname;
|
|
3110
3255
|
if (pathname === PD_ROUTES.mainJs) {
|
|
3111
|
-
return await serveFile(res,
|
|
3256
|
+
return await serveFile(res, join18(devUiDist2, "main.js"), "application/javascript");
|
|
3112
3257
|
}
|
|
3113
3258
|
if (pathname === PD_ROUTES.mainCss) {
|
|
3114
|
-
return await serveFile(res,
|
|
3259
|
+
return await serveFile(res, join18(devUiDist2, "main.css"), "text/css");
|
|
3115
3260
|
}
|
|
3116
3261
|
if (pathname === PD_ROUTES.axeCore) {
|
|
3117
3262
|
return await serveFile(res, resolveAxeCore(import.meta.url), "application/javascript");
|
|
@@ -3128,14 +3273,14 @@ async function handleRequest(req, res, ctx) {
|
|
|
3128
3273
|
}
|
|
3129
3274
|
if (pathname === PD_ROUTES.baseCss) {
|
|
3130
3275
|
res.writeHead(200, { "Content-Type": "text/css" });
|
|
3131
|
-
res.end(state.baseCss);
|
|
3276
|
+
res.end(normalizeAssetRefs(state.baseCss, assetRootNames));
|
|
3132
3277
|
return;
|
|
3133
3278
|
}
|
|
3134
3279
|
if (pathname.startsWith(PD_ROUTE_PREFIX.componentCss) && pathname.endsWith(".css")) {
|
|
3135
3280
|
const componentId = pathname.slice(PD_ROUTE_PREFIX.componentCss.length, -".css".length);
|
|
3136
3281
|
const css = state.perComponentCss.get(componentId) ?? "";
|
|
3137
3282
|
res.writeHead(200, { "Content-Type": "text/css" });
|
|
3138
|
-
res.end(css);
|
|
3283
|
+
res.end(normalizeAssetRefs(css, assetRootNames));
|
|
3139
3284
|
return;
|
|
3140
3285
|
}
|
|
3141
3286
|
if (pathname.startsWith(PD_ROUTE_PREFIX.themeCss) && pathname.endsWith(".css")) {
|
|
@@ -3149,7 +3294,7 @@ async function handleRequest(req, res, ctx) {
|
|
|
3149
3294
|
);
|
|
3150
3295
|
if (state.themeCss.has(themeName)) {
|
|
3151
3296
|
res.writeHead(200, { "Content-Type": "text/css" });
|
|
3152
|
-
res.end(state.themeCss.get(themeName));
|
|
3297
|
+
res.end(normalizeAssetRefs(state.themeCss.get(themeName), assetRootNames));
|
|
3153
3298
|
} else {
|
|
3154
3299
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
3155
3300
|
res.end("Theme not found");
|
|
@@ -3165,7 +3310,7 @@ async function handleRequest(req, res, ctx) {
|
|
|
3165
3310
|
const scriptId = normalizeScriptId(decodeURIComponent(basename4(pathname, ".js")));
|
|
3166
3311
|
try {
|
|
3167
3312
|
if (!state.scriptCache.has(scriptId)) {
|
|
3168
|
-
const entryPoint = scriptsDirs.map((d) =>
|
|
3313
|
+
const entryPoint = scriptsDirs.map((d) => join18(d, `${scriptId}.ts`)).find((f) => existsSync4(f)) ?? join18(scriptsDirs[0], `${scriptId}.ts`);
|
|
3169
3314
|
const js = await compileScriptFile(entryPoint);
|
|
3170
3315
|
state.scriptCache.set(scriptId, js);
|
|
3171
3316
|
}
|
|
@@ -3181,7 +3326,7 @@ async function handleRequest(req, res, ctx) {
|
|
|
3181
3326
|
const tail = decodeURIComponent(pathname.slice(PD_ROUTE_PREFIX.assets.length));
|
|
3182
3327
|
const firstSeg = tail.split("/")[0];
|
|
3183
3328
|
const namedIdx = assetRootNames.indexOf(firstSeg);
|
|
3184
|
-
const assetPath = namedIdx >= 0 ?
|
|
3329
|
+
const assetPath = namedIdx >= 0 ? join18(assetRoots[namedIdx], tail.slice(firstSeg.length + 1)) : join18(assetRoots[0], tail);
|
|
3185
3330
|
try {
|
|
3186
3331
|
await stat4(assetPath);
|
|
3187
3332
|
return await serveFile(res, assetPath, mimeType(extname2(assetPath)));
|
|
@@ -3235,15 +3380,14 @@ async function handleRequest(req, res, ctx) {
|
|
|
3235
3380
|
try {
|
|
3236
3381
|
if (!state.discoveryResult) state.discoveryResult = await buildDiscoveryMap(patternRoots);
|
|
3237
3382
|
const { handleMap } = state.discoveryResult;
|
|
3383
|
+
if (!state.discoveryResult.discoveryMap.has(componentId) && !state.discoveryResult.quarantineMap.has(componentId)) {
|
|
3384
|
+
json(res, 404, { error: `No component "${componentId}"` });
|
|
3385
|
+
return;
|
|
3386
|
+
}
|
|
3238
3387
|
if (state.scssGraphDirty) {
|
|
3239
3388
|
const ids = baseComponentIds(state.discoveryResult);
|
|
3240
3389
|
const styleData = await buildAllStyleData(styleRoots, ids);
|
|
3241
|
-
state
|
|
3242
|
-
state.classOwnerIndex = styleData.classOwnerIndex;
|
|
3243
|
-
state.baseProvenance = styleData.baseProvenance;
|
|
3244
|
-
state.perComponentCss = styleData.componentCssMap;
|
|
3245
|
-
state.baseCss = styleData.baseCss;
|
|
3246
|
-
state.scssGraphDirty = false;
|
|
3390
|
+
applyStyleData(state, styleData);
|
|
3247
3391
|
}
|
|
3248
3392
|
if (!state.compositionRegistry) {
|
|
3249
3393
|
state.compositionRegistry = await buildCompositionRegistry(patternDirs, handleMap, state.discoveryResult.discoveryMap);
|
|
@@ -3288,7 +3432,8 @@ async function handleRequest(req, res, ctx) {
|
|
|
3288
3432
|
void 0,
|
|
3289
3433
|
scriptVersions,
|
|
3290
3434
|
documentBaseEnabled,
|
|
3291
|
-
themeSwitch
|
|
3435
|
+
themeSwitch,
|
|
3436
|
+
state.baseCssVersion
|
|
3292
3437
|
);
|
|
3293
3438
|
json(res, 200, { ...detail, document: previewDoc, diagnostics });
|
|
3294
3439
|
} catch (e) {
|
|
@@ -3369,7 +3514,8 @@ async function handleRequest(req, res, ctx) {
|
|
|
3369
3514
|
void 0,
|
|
3370
3515
|
scriptVersions,
|
|
3371
3516
|
documentBaseEnabled,
|
|
3372
|
-
themeSwitch
|
|
3517
|
+
themeSwitch,
|
|
3518
|
+
state.baseCssVersion
|
|
3373
3519
|
);
|
|
3374
3520
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
3375
3521
|
res.end(doc);
|
|
@@ -3411,6 +3557,11 @@ async function handleRequest(req, res, ctx) {
|
|
|
3411
3557
|
}
|
|
3412
3558
|
return;
|
|
3413
3559
|
}
|
|
3560
|
+
if (!servesShell(pathname, req.headers.accept)) {
|
|
3561
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
3562
|
+
res.end(`Not found: ${pathname}`);
|
|
3563
|
+
return;
|
|
3564
|
+
}
|
|
3414
3565
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
3415
3566
|
res.end(buildShellHtml({ mode: "live", wsPort }));
|
|
3416
3567
|
} catch (e) {
|
|
@@ -3443,13 +3594,14 @@ function describeBindError(e, port2, role, ports) {
|
|
|
3443
3594
|
}
|
|
3444
3595
|
|
|
3445
3596
|
// packages/dev-server/src/watch.ts
|
|
3446
|
-
import { basename as basename5, extname as extname3, relative as
|
|
3597
|
+
import { basename as basename5, extname as extname3, relative as relative5, resolve as resolve8 } from "node:path";
|
|
3447
3598
|
import chokidar from "chokidar";
|
|
3448
3599
|
function startWatching(ctx) {
|
|
3449
3600
|
const { cfg, state, render, tracker, hotreloader } = ctx;
|
|
3450
|
-
const { patternsDir, stylesDir, patternRoots, patternDirs, swatchSlots, cwd } = cfg;
|
|
3601
|
+
const { patternsDir, stylesDir, styleRoots, patternRoots, patternDirs, swatchSlots, cwd } = cfg;
|
|
3451
3602
|
const themesDir = cfg.config.themes ? cfg.themesDir : null;
|
|
3452
3603
|
const scriptsDir = cfg.config.scriptsDir ? cfg.scriptsDirs[0] ?? null : null;
|
|
3604
|
+
const inProjectBase = documentBaseMatcher(stylesDir, cfg.config.documentBase);
|
|
3453
3605
|
const watchDirs = [patternsDir, stylesDir, ...themesDir ? [themesDir] : [], ...scriptsDir ? [scriptsDir] : []];
|
|
3454
3606
|
const watcher = chokidar.watch(watchDirs, { ignoreInitial: true, persistent: true });
|
|
3455
3607
|
async function handleComponentScss(filePath) {
|
|
@@ -3474,11 +3626,15 @@ function startWatching(ctx) {
|
|
|
3474
3626
|
const allIds = baseComponentIds(state.discoveryResult);
|
|
3475
3627
|
const timestamp = Date.now();
|
|
3476
3628
|
const effectiveFiles = recompiledFiles.length > 0 ? recompiledFiles : [resolve8(filePath)];
|
|
3629
|
+
if (inProjectBase(resolve8(filePath)) || effectiveFiles.some((f) => inProjectBase(resolve8(f)) || !resolveScssToComponent(resolve8(f), stylesDir, allIds))) {
|
|
3630
|
+
await restyleAll(allIds, timestamp, effectiveFiles, compileErrors.length > 0);
|
|
3631
|
+
return;
|
|
3632
|
+
}
|
|
3477
3633
|
const pending = [];
|
|
3478
3634
|
for (const recompiledFile of effectiveFiles) {
|
|
3479
3635
|
const compId = resolveScssToComponent(resolve8(recompiledFile), stylesDir, allIds);
|
|
3480
3636
|
if (compId) {
|
|
3481
|
-
const newCss = await recomputeComponentCss(recompiledFile, compId, stylesDir, allIds);
|
|
3637
|
+
const newCss = await recomputeComponentCss(recompiledFile, compId, stylesDir, allIds, inProjectBase);
|
|
3482
3638
|
if (newCss !== null) {
|
|
3483
3639
|
state.perComponentCss.set(compId, newCss);
|
|
3484
3640
|
pending.push({
|
|
@@ -3505,22 +3661,43 @@ function startWatching(ctx) {
|
|
|
3505
3661
|
}
|
|
3506
3662
|
if (verboseTiming) console.timeEnd("[dev-server:timing] classOwnerIndex patch");
|
|
3507
3663
|
}
|
|
3508
|
-
|
|
3509
|
-
if (state.lastComponentId && state.discoveryResult && state.compositionRegistry) {
|
|
3510
|
-
const staticClosure = await computeCssClosure(
|
|
3511
|
-
state.lastComponentId,
|
|
3512
|
-
state.compositionRegistry,
|
|
3513
|
-
patternDirs,
|
|
3514
|
-
state.classOwnerIndex ?? /* @__PURE__ */ new Map(),
|
|
3515
|
-
state.discoveryResult.discoveryMap
|
|
3516
|
-
);
|
|
3517
|
-
const renderedHtml = await render({ component: state.lastComponentId }).then((r) => r.html, () => "");
|
|
3518
|
-
const closureIds = withRenderedClassOwners(staticClosure, renderedHtml, state.classOwnerIndex ?? /* @__PURE__ */ new Map());
|
|
3519
|
-
orderedHrefs = linkableClosure(closureIds, state.perComponentCss).map((id) => componentCssUrl(id, timestamp));
|
|
3520
|
-
}
|
|
3664
|
+
const orderedHrefs = await closureHrefs(timestamp);
|
|
3521
3665
|
for (const m of pending) hotreloader.push({ ...m, orderedHrefs });
|
|
3522
3666
|
console.log(`[dev-server] CSS updated (recompiled: ${effectiveFiles.map((f) => basename5(f)).join(", ")})`);
|
|
3523
3667
|
}
|
|
3668
|
+
async function restyleAll(allIds, timestamp, files, hadCompileErrors) {
|
|
3669
|
+
const names = files.map((f) => basename5(f)).join(", ");
|
|
3670
|
+
if (hadCompileErrors) {
|
|
3671
|
+
console.warn(`[dev-server] CSS not updated \u2014 compile errors (recompiled: ${names})`);
|
|
3672
|
+
return;
|
|
3673
|
+
}
|
|
3674
|
+
const changes = applyStyleData(state, await buildAllStyleData(styleRoots, allIds));
|
|
3675
|
+
const orderedHrefs = await closureHrefs(timestamp);
|
|
3676
|
+
for (const componentId of changes.changedComponents) {
|
|
3677
|
+
hotreloader.push({ type: "css-link-swap", componentId, href: componentCssUrl(componentId, timestamp), orderedHrefs });
|
|
3678
|
+
}
|
|
3679
|
+
if (changes.baseChanged && state.lastComponentId) {
|
|
3680
|
+
hotreloader.push({ type: "content-swap", componentId: state.lastComponentId });
|
|
3681
|
+
}
|
|
3682
|
+
const what = [
|
|
3683
|
+
`${changes.changedComponents.length} component(s)`,
|
|
3684
|
+
...changes.baseChanged ? ["base layer"] : []
|
|
3685
|
+
].join(" + ");
|
|
3686
|
+
console.log(`[dev-server] CSS updated: ${what} (recompiled: ${names})`);
|
|
3687
|
+
}
|
|
3688
|
+
async function closureHrefs(timestamp) {
|
|
3689
|
+
if (!state.lastComponentId || !state.discoveryResult || !state.compositionRegistry) return void 0;
|
|
3690
|
+
const staticClosure = await computeCssClosure(
|
|
3691
|
+
state.lastComponentId,
|
|
3692
|
+
state.compositionRegistry,
|
|
3693
|
+
patternDirs,
|
|
3694
|
+
state.classOwnerIndex ?? /* @__PURE__ */ new Map(),
|
|
3695
|
+
state.discoveryResult.discoveryMap
|
|
3696
|
+
);
|
|
3697
|
+
const renderedHtml = await render({ component: state.lastComponentId }).then((r) => r.html, () => "");
|
|
3698
|
+
const closureIds = withRenderedClassOwners(staticClosure, renderedHtml, state.classOwnerIndex ?? /* @__PURE__ */ new Map());
|
|
3699
|
+
return linkableClosure(closureIds, state.perComponentCss).map((id) => componentCssUrl(id, timestamp));
|
|
3700
|
+
}
|
|
3524
3701
|
async function handleMarkdown(filePath, event) {
|
|
3525
3702
|
const abs = resolve8(filePath);
|
|
3526
3703
|
const ownerIn = (dr) => {
|
|
@@ -3529,7 +3706,7 @@ function startWatching(ctx) {
|
|
|
3529
3706
|
}
|
|
3530
3707
|
return void 0;
|
|
3531
3708
|
};
|
|
3532
|
-
const rel =
|
|
3709
|
+
const rel = relative5(patternsDir, filePath);
|
|
3533
3710
|
if (event === "change") state.discoveryResult ??= await buildDiscoveryMap(patternRoots);
|
|
3534
3711
|
let owner = ownerIn(state.discoveryResult);
|
|
3535
3712
|
if (event === "change") {
|
|
@@ -3611,21 +3788,21 @@ function startWatching(ctx) {
|
|
|
3611
3788
|
if (ext === ".scss" && !(themesDir && filePath.startsWith(themesDir))) {
|
|
3612
3789
|
await handleComponentScss(filePath);
|
|
3613
3790
|
} else if (ext === ".hbs" && filePath.startsWith(patternsDir)) {
|
|
3614
|
-
await rebuildAndSignalTree(`Pattern added: ${
|
|
3791
|
+
await rebuildAndSignalTree(`Pattern added: ${relative5(patternsDir, filePath)}`);
|
|
3615
3792
|
} else if (ext === ".md" && filePath.startsWith(patternsDir)) {
|
|
3616
3793
|
await handleMarkdown(filePath, "add");
|
|
3617
3794
|
}
|
|
3618
3795
|
});
|
|
3619
3796
|
watcher.on("unlink", async (filePath) => {
|
|
3620
3797
|
if (extname3(filePath) === ".hbs" && filePath.startsWith(patternsDir)) {
|
|
3621
|
-
await rebuildAndSignalTree(`Pattern removed: ${
|
|
3798
|
+
await rebuildAndSignalTree(`Pattern removed: ${relative5(patternsDir, filePath)}`);
|
|
3622
3799
|
} else if (extname3(filePath) === ".md" && filePath.startsWith(patternsDir)) {
|
|
3623
3800
|
await handleMarkdown(filePath, "unlink");
|
|
3624
3801
|
}
|
|
3625
3802
|
});
|
|
3626
3803
|
watcher.on("unlinkDir", async (dirPath) => {
|
|
3627
3804
|
if (dirPath.startsWith(patternsDir) && dirPath !== patternsDir) {
|
|
3628
|
-
await rebuildAndSignalTree(`Pattern dir removed: ${
|
|
3805
|
+
await rebuildAndSignalTree(`Pattern dir removed: ${relative5(patternsDir, dirPath)}`);
|
|
3629
3806
|
}
|
|
3630
3807
|
});
|
|
3631
3808
|
return watcher;
|
|
@@ -3633,7 +3810,7 @@ function startWatching(ctx) {
|
|
|
3633
3810
|
|
|
3634
3811
|
// packages/dev-server/src/export-io.ts
|
|
3635
3812
|
import { readFile as readFile16, writeFile as writeFile4, mkdir as mkdir4 } from "node:fs/promises";
|
|
3636
|
-
import { dirname as dirname6, join as
|
|
3813
|
+
import { dirname as dirname6, join as join19, relative as relative6, resolve as resolve9 } from "node:path";
|
|
3637
3814
|
|
|
3638
3815
|
// packages/dev-server/src/export.ts
|
|
3639
3816
|
var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -3783,7 +3960,7 @@ async function planExport(config, cwd) {
|
|
|
3783
3960
|
const hbs = await readFile16(entry.hbsPath, "utf-8");
|
|
3784
3961
|
inputs.push({ id, hbs });
|
|
3785
3962
|
hbsById.set(id, hbs);
|
|
3786
|
-
relPathById.set(id,
|
|
3963
|
+
relPathById.set(id, relative6(patternsDir, entry.hbsPath).replace(/\.hbs$/, ""));
|
|
3787
3964
|
if (entry.jsonPath) {
|
|
3788
3965
|
try {
|
|
3789
3966
|
dataJsonById.set(id, await readFile16(entry.jsonPath, "utf-8"));
|
|
@@ -3826,20 +4003,20 @@ async function writeExport(families, sink) {
|
|
|
3826
4003
|
const files = [];
|
|
3827
4004
|
for (const family of families) {
|
|
3828
4005
|
const baseRel = `${family.baseId}.hbs`;
|
|
3829
|
-
const basePath =
|
|
4006
|
+
const basePath = join19(sink, baseRel);
|
|
3830
4007
|
await mkdir4(dirname6(basePath), { recursive: true });
|
|
3831
4008
|
await writeFile4(basePath, family.baseHbs, "utf-8");
|
|
3832
4009
|
files.push(baseRel);
|
|
3833
4010
|
if (family.baseData) {
|
|
3834
4011
|
const dataRel = `${family.baseId}.json`;
|
|
3835
|
-
await writeFile4(
|
|
4012
|
+
await writeFile4(join19(sink, dataRel), JSON.stringify(family.baseData, null, 2) + "\n", "utf-8");
|
|
3836
4013
|
files.push(dataRel);
|
|
3837
4014
|
}
|
|
3838
4015
|
for (const v of family.variants) {
|
|
3839
4016
|
if (v.token === "") continue;
|
|
3840
4017
|
const overlayRel = `${family.baseId}~${v.token}.json`;
|
|
3841
4018
|
const overlay = { modifier: v.modifier, ...v.data ?? {} };
|
|
3842
|
-
await writeFile4(
|
|
4019
|
+
await writeFile4(join19(sink, overlayRel), JSON.stringify(overlay, null, 2) + "\n", "utf-8");
|
|
3843
4020
|
files.push(overlayRel);
|
|
3844
4021
|
}
|
|
3845
4022
|
}
|
|
@@ -3870,8 +4047,8 @@ async function writeThreadedIncluders(threaded, patternsDir, sink) {
|
|
|
3870
4047
|
const res = rewriteIncludes(src, map);
|
|
3871
4048
|
if (res.rewrites === 0) continue;
|
|
3872
4049
|
rewrites += res.rewrites;
|
|
3873
|
-
const rel =
|
|
3874
|
-
const outPath =
|
|
4050
|
+
const rel = relative6(patternsDir, hbsPath);
|
|
4051
|
+
const outPath = join19(sink, rel);
|
|
3875
4052
|
await mkdir4(dirname6(outPath), { recursive: true });
|
|
3876
4053
|
await writeFile4(outPath, res.hbs, "utf-8");
|
|
3877
4054
|
files.push(rel);
|
|
@@ -3881,6 +4058,36 @@ async function writeThreadedIncluders(threaded, patternsDir, sink) {
|
|
|
3881
4058
|
}
|
|
3882
4059
|
|
|
3883
4060
|
// packages/dev-server/src/audit-format.ts
|
|
4061
|
+
function scopeMarkdown(scope) {
|
|
4062
|
+
const item = (i) => `- **${i.name}:** ${i.detail}`;
|
|
4063
|
+
return [
|
|
4064
|
+
"## Scope",
|
|
4065
|
+
"",
|
|
4066
|
+
"Covered: static checks on a single render, without browser layout.",
|
|
4067
|
+
"",
|
|
4068
|
+
...scope.covered.map(item),
|
|
4069
|
+
"",
|
|
4070
|
+
"Not covered:",
|
|
4071
|
+
"",
|
|
4072
|
+
...scope.notCovered.map(item),
|
|
4073
|
+
"",
|
|
4074
|
+
scope.note
|
|
4075
|
+
];
|
|
4076
|
+
}
|
|
4077
|
+
function scopeText(scope) {
|
|
4078
|
+
const item = (i) => ` - ${i.name}: ${i.detail}`;
|
|
4079
|
+
return [
|
|
4080
|
+
"Scope",
|
|
4081
|
+
"",
|
|
4082
|
+
"Covered: static checks on a single render, without browser layout.",
|
|
4083
|
+
...scope.covered.map(item),
|
|
4084
|
+
"",
|
|
4085
|
+
"Not covered:",
|
|
4086
|
+
...scope.notCovered.map(item),
|
|
4087
|
+
"",
|
|
4088
|
+
scope.note
|
|
4089
|
+
];
|
|
4090
|
+
}
|
|
3884
4091
|
function renderAuditMarkdown(r) {
|
|
3885
4092
|
const lines = [];
|
|
3886
4093
|
lines.push(`# Diagnostics audit${r.subjectSha ? ` \u2014 ${r.subjectSha}` : ""}`);
|
|
@@ -3907,7 +4114,7 @@ function renderAuditMarkdown(r) {
|
|
|
3907
4114
|
}
|
|
3908
4115
|
lines.push("");
|
|
3909
4116
|
}
|
|
3910
|
-
lines.push(
|
|
4117
|
+
lines.push(...scopeMarkdown(r.scope));
|
|
3911
4118
|
lines.push("");
|
|
3912
4119
|
return lines.join("\n");
|
|
3913
4120
|
}
|
|
@@ -3935,7 +4142,7 @@ function renderAuditText(r) {
|
|
|
3935
4142
|
}
|
|
3936
4143
|
}
|
|
3937
4144
|
lines.push("");
|
|
3938
|
-
lines.push(r.scope);
|
|
4145
|
+
lines.push(...scopeText(r.scope));
|
|
3939
4146
|
lines.push("");
|
|
3940
4147
|
return lines.join("\n");
|
|
3941
4148
|
}
|
|
@@ -4000,7 +4207,7 @@ async function startDevServer(config, cwd, opts = {}) {
|
|
|
4000
4207
|
if (cfg.themeDirs.length) {
|
|
4001
4208
|
for (const { name, dir } of await discoverThemeFiles(cfg.themeDirs)) {
|
|
4002
4209
|
try {
|
|
4003
|
-
const css = await compileSassFile(
|
|
4210
|
+
const css = await compileSassFile(join20(dir, `${name}.theme.scss`));
|
|
4004
4211
|
state.themeCss.set(name, css);
|
|
4005
4212
|
const slots = config.themes && dir === cfg.themeDirs[0] ? swatchSlots : cfg.packageSwatchSlots;
|
|
4006
4213
|
state.themeTokens.set(name, await extractThemeTokens(name, css, slots, dir));
|
|
@@ -4022,12 +4229,8 @@ async function startDevServer(config, cwd, opts = {}) {
|
|
|
4022
4229
|
await surfaceFlattening(dr.discoveryMap, state.compositionRegistry, cwd);
|
|
4023
4230
|
const allComponentIds = baseComponentIds(dr);
|
|
4024
4231
|
const styleData = await buildAllStyleData(styleRoots, allComponentIds);
|
|
4025
|
-
state
|
|
4026
|
-
|
|
4027
|
-
state.baseProvenance = styleData.baseProvenance;
|
|
4028
|
-
state.perComponentCss = styleData.componentCssMap;
|
|
4029
|
-
state.baseCss = styleData.baseCss;
|
|
4030
|
-
state.scssGraphDirty = false;
|
|
4232
|
+
applyStyleData(state, styleData);
|
|
4233
|
+
surfaceDocumentBase(styleRoots, styleData.baseOutsideList, cwd);
|
|
4031
4234
|
}
|
|
4032
4235
|
const boot = {
|
|
4033
4236
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4068,9 +4271,9 @@ async function startDevServer(config, cwd, opts = {}) {
|
|
|
4068
4271
|
|
|
4069
4272
|
// packages/core/src/index.ts
|
|
4070
4273
|
import { build as esbuild } from "esbuild";
|
|
4071
|
-
import { existsSync as
|
|
4274
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
4072
4275
|
import { readFile as readFile17, writeFile as writeFile5, unlink, mkdir as mkdir5 } from "node:fs/promises";
|
|
4073
|
-
import { join as
|
|
4276
|
+
import { join as join21, dirname as dirname7, resolve as resolve10, relative as relative7, isAbsolute } from "node:path";
|
|
4074
4277
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
4075
4278
|
import { createHash as createHash3 } from "node:crypto";
|
|
4076
4279
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
@@ -4098,7 +4301,7 @@ async function runAudit(root, configPath, format = "md") {
|
|
|
4098
4301
|
const { graph, reaudited, reused, artifactPath } = await runDiagnosticsAudit(config, root);
|
|
4099
4302
|
const report = formatAudit(graph, format);
|
|
4100
4303
|
const ext = format === "json" ? "json" : format === "text" ? "txt" : "md";
|
|
4101
|
-
const reportPath =
|
|
4304
|
+
const reportPath = join21(dirname7(artifactPath), `diagnostics-report.${ext}`);
|
|
4102
4305
|
await writeFile5(reportPath, report, "utf-8");
|
|
4103
4306
|
console.log(report);
|
|
4104
4307
|
const affected = new Set(graph.occurrences.map((o) => o.component)).size;
|
|
@@ -4110,10 +4313,10 @@ async function runAudit(root, configPath, format = "md") {
|
|
|
4110
4313
|
}
|
|
4111
4314
|
function defaultExportDir(root) {
|
|
4112
4315
|
const key = createHash3("sha1").update(resolve10(root)).digest("hex").slice(0, 16);
|
|
4113
|
-
return
|
|
4316
|
+
return join21(tmpdir2(), "pattern-dump", key, "export");
|
|
4114
4317
|
}
|
|
4115
4318
|
function assertOutsideRoot(outDir, root) {
|
|
4116
|
-
const rel =
|
|
4319
|
+
const rel = relative7(resolve10(root), resolve10(outDir));
|
|
4117
4320
|
const inside = rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
4118
4321
|
if (inside) {
|
|
4119
4322
|
throw new Error(
|
|
@@ -4184,23 +4387,24 @@ async function runExport(root, configPath, outDir) {
|
|
|
4184
4387
|
}
|
|
4185
4388
|
async function loadConfig(root, configPath) {
|
|
4186
4389
|
if (configPath) {
|
|
4187
|
-
if (!
|
|
4188
|
-
throw new
|
|
4390
|
+
if (!existsSync5(configPath)) {
|
|
4391
|
+
throw new ConfigError(`--config file not found: ${configPath}`);
|
|
4189
4392
|
}
|
|
4190
|
-
return
|
|
4393
|
+
return validateConfig(await readConfigFile(configPath), configPath);
|
|
4191
4394
|
}
|
|
4192
|
-
const
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
if (existsSync4(jsonPath)) {
|
|
4196
|
-
return JSON.parse(await readFile17(jsonPath, "utf-8"));
|
|
4395
|
+
for (const name of ["pattern-dump.config.ts", "pattern-dump.config.json"]) {
|
|
4396
|
+
const path = join21(root, name);
|
|
4397
|
+
if (existsSync5(path)) return validateConfig(await readConfigFile(path), name);
|
|
4197
4398
|
}
|
|
4198
|
-
throw new
|
|
4399
|
+
throw new ConfigError(
|
|
4199
4400
|
`No pattern-dump.config.ts or pattern-dump.config.json found in ${root} (and no --config given)`
|
|
4200
4401
|
);
|
|
4201
4402
|
}
|
|
4403
|
+
async function readConfigFile(path) {
|
|
4404
|
+
return path.endsWith(".json") ? JSON.parse(await readFile17(path, "utf-8")) : loadTsConfig(path);
|
|
4405
|
+
}
|
|
4202
4406
|
async function loadTsConfig(tsPath) {
|
|
4203
|
-
const tmp =
|
|
4407
|
+
const tmp = join21(tmpdir2(), `pd-config-${Date.now()}.mjs`);
|
|
4204
4408
|
try {
|
|
4205
4409
|
await esbuild({
|
|
4206
4410
|
entryPoints: [tsPath],
|
|
@@ -4405,14 +4609,14 @@ var USAGE = `Usage: pd <command> [options]
|
|
|
4405
4609
|
help [command] Show this help`;
|
|
4406
4610
|
|
|
4407
4611
|
// packages/cli/src/init/run.ts
|
|
4408
|
-
import { existsSync as
|
|
4612
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
4409
4613
|
import { readFile as readFile19 } from "node:fs/promises";
|
|
4410
|
-
import { basename as basename6, dirname as dirname9, join as
|
|
4614
|
+
import { basename as basename6, dirname as dirname9, join as join23, relative as relative9, resolve as resolve12 } from "node:path";
|
|
4411
4615
|
|
|
4412
4616
|
// packages/cli/package.json
|
|
4413
4617
|
var package_default = {
|
|
4414
4618
|
name: "@pattern-dump/cli",
|
|
4415
|
-
version: "0.1.0-alpha.
|
|
4619
|
+
version: "0.1.0-alpha.1",
|
|
4416
4620
|
private: true,
|
|
4417
4621
|
description: "The pd command and the published pattern-dump package.",
|
|
4418
4622
|
type: "module",
|
|
@@ -4448,14 +4652,14 @@ var PD_VERSION = package_default.version;
|
|
|
4448
4652
|
|
|
4449
4653
|
// packages/cli/src/init/files.ts
|
|
4450
4654
|
import { mkdir as mkdir6, readdir as readdir5, readFile as readFile18, writeFile as writeFile6 } from "node:fs/promises";
|
|
4451
|
-
import { dirname as dirname8, join as
|
|
4655
|
+
import { dirname as dirname8, join as join22, relative as relative8, sep as sep2 } from "node:path";
|
|
4452
4656
|
async function readTemplateDir(dir) {
|
|
4453
4657
|
const out = [];
|
|
4454
4658
|
async function walk(current) {
|
|
4455
4659
|
for (const entry of await readdir5(current, { withFileTypes: true })) {
|
|
4456
|
-
const full =
|
|
4660
|
+
const full = join22(current, entry.name);
|
|
4457
4661
|
if (entry.isDirectory()) await walk(full);
|
|
4458
|
-
else out.push({ path:
|
|
4662
|
+
else out.push({ path: relative8(dir, full).split(sep2).join("/"), content: await readFile18(full, "utf8") });
|
|
4459
4663
|
}
|
|
4460
4664
|
}
|
|
4461
4665
|
await walk(dir);
|
|
@@ -4463,20 +4667,20 @@ async function readTemplateDir(dir) {
|
|
|
4463
4667
|
}
|
|
4464
4668
|
async function readTemplates(templatesDir) {
|
|
4465
4669
|
const [base2, examples] = await Promise.all([
|
|
4466
|
-
readTemplateDir(
|
|
4467
|
-
readTemplateDir(
|
|
4670
|
+
readTemplateDir(join22(templatesDir, "base")),
|
|
4671
|
+
readTemplateDir(join22(templatesDir, "examples"))
|
|
4468
4672
|
]);
|
|
4469
4673
|
return { base: base2, examples };
|
|
4470
4674
|
}
|
|
4471
4675
|
async function applyPlan(target, plan) {
|
|
4472
4676
|
await mkdir6(target, { recursive: true });
|
|
4473
4677
|
for (const file of plan.writes) {
|
|
4474
|
-
const abs =
|
|
4678
|
+
const abs = join22(target, file.path);
|
|
4475
4679
|
await mkdir6(dirname8(abs), { recursive: true });
|
|
4476
4680
|
await writeFile6(abs, file.content, { flag: "wx" });
|
|
4477
4681
|
}
|
|
4478
|
-
await writeFile6(
|
|
4479
|
-
for (const dir of ["assets", "src/patterns"]) await mkdir6(
|
|
4682
|
+
await writeFile6(join22(target, "package.json"), plan.packageJson.content);
|
|
4683
|
+
for (const dir of ["assets", "src/patterns"]) await mkdir6(join22(target, dir), { recursive: true });
|
|
4480
4684
|
}
|
|
4481
4685
|
|
|
4482
4686
|
// packages/cli/src/init/plan.ts
|
|
@@ -4555,12 +4759,12 @@ function mergePackageJson(current, answers, packageName) {
|
|
|
4555
4759
|
function dependencySpec(pdSource, cwd) {
|
|
4556
4760
|
if (pdSource === void 0) return `^${PD_VERSION}`;
|
|
4557
4761
|
const asPath = resolve12(cwd, pdSource);
|
|
4558
|
-
if (
|
|
4762
|
+
if (existsSync6(asPath)) return `file:${asPath}`;
|
|
4559
4763
|
return pdSource.startsWith(`${PD_PACKAGE_NAME}@`) ? pdSource.slice(PD_PACKAGE_NAME.length + 1) : pdSource;
|
|
4560
4764
|
}
|
|
4561
4765
|
async function insideGitRepo(target, io) {
|
|
4562
4766
|
let dir = target;
|
|
4563
|
-
while (!
|
|
4767
|
+
while (!existsSync6(dir) && dirname9(dir) !== dir) dir = dirname9(dir);
|
|
4564
4768
|
return await io.run("git", ["rev-parse", "--is-inside-work-tree"], dir) === 0;
|
|
4565
4769
|
}
|
|
4566
4770
|
async function runInit(cmd, io) {
|
|
@@ -4583,9 +4787,9 @@ async function runInit(cmd, io) {
|
|
|
4583
4787
|
} else {
|
|
4584
4788
|
dependency = dependencySpec(void 0, io.cwd);
|
|
4585
4789
|
}
|
|
4586
|
-
const pkgPath =
|
|
4790
|
+
const pkgPath = join23(target, "package.json");
|
|
4587
4791
|
let packageJson = null;
|
|
4588
|
-
if (
|
|
4792
|
+
if (existsSync6(pkgPath)) {
|
|
4589
4793
|
try {
|
|
4590
4794
|
packageJson = JSON.parse(await readFile19(pkgPath, "utf8"));
|
|
4591
4795
|
} catch (e) {
|
|
@@ -4603,14 +4807,14 @@ async function runInit(cmd, io) {
|
|
|
4603
4807
|
const install = cmd.install ?? (prompt ? await io.confirm("Run npm install", true) : true);
|
|
4604
4808
|
const answers = { name, brand, examples, dependency };
|
|
4605
4809
|
const templates = await readTemplates(io.templatesDir);
|
|
4606
|
-
const result = planInit(templates, answers, { packageJson, exists: (p) =>
|
|
4810
|
+
const result = planInit(templates, answers, { packageJson, exists: (p) => existsSync6(join23(target, p)) }, PD_PACKAGE_NAME);
|
|
4607
4811
|
if (!result.ok) {
|
|
4608
4812
|
io.log(`\u2717 ${result.reason}`);
|
|
4609
4813
|
return 1;
|
|
4610
4814
|
}
|
|
4611
4815
|
const { plan } = result;
|
|
4612
4816
|
await applyPlan(target, plan);
|
|
4613
|
-
const where =
|
|
4817
|
+
const where = relative9(io.cwd, target) || ".";
|
|
4614
4818
|
io.log(`\u2713 ${plan.writes.length} files written to ${where}`);
|
|
4615
4819
|
io.log(`\u2713 package.json ${plan.packageJson.created ? "created" : "updated"} \u2014 ${PD_PACKAGE_NAME} ${answers.dependency}`);
|
|
4616
4820
|
for (const note of plan.packageJson.notes) io.log(` \xB7 ${note}`);
|
|
@@ -4643,8 +4847,8 @@ async function runInit(cmd, io) {
|
|
|
4643
4847
|
}
|
|
4644
4848
|
|
|
4645
4849
|
// packages/cli/src/init/local-build.ts
|
|
4646
|
-
import { existsSync as
|
|
4647
|
-
import { join as
|
|
4850
|
+
import { existsSync as existsSync7, statSync } from "node:fs";
|
|
4851
|
+
import { join as join24 } from "node:path";
|
|
4648
4852
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
4649
4853
|
var BUILT = [
|
|
4650
4854
|
"packages/cli/dist/index.js",
|
|
@@ -4662,15 +4866,18 @@ var mtime = (path) => {
|
|
|
4662
4866
|
};
|
|
4663
4867
|
function detectLocalBuild(moduleUrl, packageName, version) {
|
|
4664
4868
|
const repo = fileURLToPath6(new URL("../../../", moduleUrl)).replace(/[\\/]$/, "");
|
|
4665
|
-
if (!
|
|
4666
|
-
const tarball =
|
|
4667
|
-
if (!
|
|
4668
|
-
const newestBuild = Math.max(...BUILT.map((p) => mtime(
|
|
4869
|
+
if (!existsSync7(join24(repo, "packages/cli/src/distribution.ts"))) return null;
|
|
4870
|
+
const tarball = join24(repo, ".pd-pack", `${packageName}-${version}.tgz`);
|
|
4871
|
+
if (!existsSync7(tarball)) return { repo, tarball: null, stale: false };
|
|
4872
|
+
const newestBuild = Math.max(...BUILT.map((p) => mtime(join24(repo, p))));
|
|
4669
4873
|
return { repo, tarball, stale: newestBuild > mtime(tarball) };
|
|
4670
4874
|
}
|
|
4671
4875
|
|
|
4672
4876
|
// packages/cli/src/index.ts
|
|
4673
4877
|
var command = parseCommand(process.argv.slice(2), process.cwd());
|
|
4878
|
+
function describeError(e) {
|
|
4879
|
+
return e instanceof Error && e.name === "ConfigError" ? e.message : e;
|
|
4880
|
+
}
|
|
4674
4881
|
switch (command.kind) {
|
|
4675
4882
|
case "init": {
|
|
4676
4883
|
const rl = process.stdin.isTTY ? createInterface({ input: process.stdin, output: process.stdout }) : null;
|
|
@@ -4713,14 +4920,14 @@ switch (command.kind) {
|
|
|
4713
4920
|
if (typeof e === "object" && e !== null && e.code === "EADDRINUSE" && e instanceof Error) {
|
|
4714
4921
|
console.error(`[pd] ${e.message}`);
|
|
4715
4922
|
} else {
|
|
4716
|
-
console.error("[pd] Error:", e);
|
|
4923
|
+
console.error("[pd] Error:", describeError(e));
|
|
4717
4924
|
}
|
|
4718
4925
|
process.exit(1);
|
|
4719
4926
|
});
|
|
4720
4927
|
break;
|
|
4721
4928
|
case "audit":
|
|
4722
4929
|
runAudit(command.root, command.configPath, command.format).then(() => process.stdout.write("", () => process.exit(0))).catch((e) => {
|
|
4723
|
-
console.error("[pd] Error:", e);
|
|
4930
|
+
console.error("[pd] Error:", describeError(e));
|
|
4724
4931
|
process.exit(1);
|
|
4725
4932
|
});
|
|
4726
4933
|
break;
|
|
@@ -4739,7 +4946,7 @@ switch (command.kind) {
|
|
|
4739
4946
|
runExport(command.root, command.configPath, command.out).then(() => {
|
|
4740
4947
|
process.exitCode = 0;
|
|
4741
4948
|
}).catch((e) => {
|
|
4742
|
-
console.error("[pd] Error:", e);
|
|
4949
|
+
console.error("[pd] Error:", describeError(e));
|
|
4743
4950
|
process.exitCode = 1;
|
|
4744
4951
|
});
|
|
4745
4952
|
break;
|