scribe-cms 0.0.17 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/cli/index.cjs +4021 -555
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +4019 -553
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +1514 -229
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1499 -228
- package/dist/index.js.map +1 -1
- package/dist/runtime.cjs +447 -33
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +446 -32
- package/dist/runtime.js.map +1 -1
- package/dist/src/config/resolve-config.d.ts.map +1 -1
- package/dist/src/core/builtin-fields.d.ts +10 -1
- package/dist/src/core/builtin-fields.d.ts.map +1 -1
- package/dist/src/core/field.d.ts +70 -0
- package/dist/src/core/field.d.ts.map +1 -1
- package/dist/src/core/introspect-schema.d.ts +31 -1
- package/dist/src/core/introspect-schema.d.ts.map +1 -1
- package/dist/src/core/managed-roots.d.ts +15 -0
- package/dist/src/core/managed-roots.d.ts.map +1 -0
- package/dist/src/core/types.d.ts +56 -4
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/create-project.d.ts +17 -1
- package/dist/src/create-project.d.ts.map +1 -1
- package/dist/src/create-scribe.d.ts.map +1 -1
- package/dist/src/delete/execute.d.ts +21 -0
- package/dist/src/delete/execute.d.ts.map +1 -0
- package/dist/src/delete/plan.d.ts +85 -0
- package/dist/src/delete/plan.d.ts.map +1 -0
- package/dist/src/delete/render-text.d.ts +7 -0
- package/dist/src/delete/render-text.d.ts.map +1 -0
- package/dist/src/hash/page-hash.d.ts +9 -0
- package/dist/src/hash/page-hash.d.ts.map +1 -1
- package/dist/src/index.d.ts +12 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/inline/resolve-tokens.d.ts +39 -0
- package/dist/src/inline/resolve-tokens.d.ts.map +1 -0
- package/dist/src/inline/tokens.d.ts +95 -0
- package/dist/src/inline/tokens.d.ts.map +1 -0
- package/dist/src/loader/create-loader.d.ts +7 -1
- package/dist/src/loader/create-loader.d.ts.map +1 -1
- package/dist/src/loader/resolve-assets.d.ts +16 -0
- package/dist/src/loader/resolve-assets.d.ts.map +1 -0
- package/dist/src/runtime.d.ts +1 -1
- package/dist/src/runtime.d.ts.map +1 -1
- package/dist/src/storage/translations.d.ts +8 -0
- package/dist/src/storage/translations.d.ts.map +1 -1
- package/dist/src/translate/resolve-translate-config.d.ts.map +1 -1
- package/dist/src/translate/response-schema.d.ts +7 -1
- package/dist/src/translate/response-schema.d.ts.map +1 -1
- package/dist/src/translate/translate-core.d.ts +6 -0
- package/dist/src/translate/translate-core.d.ts.map +1 -1
- package/dist/src/translate/validate-translation.d.ts.map +1 -1
- package/dist/src/translate/worklist.d.ts +8 -1
- package/dist/src/translate/worklist.d.ts.map +1 -1
- package/dist/src/validate/validate-assets.d.ts +26 -2
- package/dist/src/validate/validate-assets.d.ts.map +1 -1
- package/dist/src/validate/validate-inline-tokens.d.ts +10 -0
- package/dist/src/validate/validate-inline-tokens.d.ts.map +1 -0
- package/dist/src/validate/validate-project.d.ts.map +1 -1
- package/dist/src/validate/validate-relations.d.ts +2 -1
- package/dist/src/validate/validate-relations.d.ts.map +1 -1
- package/dist/studio/asset-serve.d.ts +39 -0
- package/dist/studio/asset-serve.d.ts.map +1 -0
- package/dist/studio/asset-views.d.ts +4 -0
- package/dist/studio/asset-views.d.ts.map +1 -0
- package/dist/studio/content-views.d.ts +85 -0
- package/dist/studio/content-views.d.ts.map +1 -0
- package/dist/studio/introspect-fields.d.ts +111 -0
- package/dist/studio/introspect-fields.d.ts.map +1 -0
- package/dist/studio/mdx-preview.d.ts +3 -0
- package/dist/studio/mdx-preview.d.ts.map +1 -0
- package/dist/studio/preview-tokens.d.ts +16 -0
- package/dist/studio/preview-tokens.d.ts.map +1 -0
- package/dist/studio/search.d.ts +34 -0
- package/dist/studio/search.d.ts.map +1 -0
- package/dist/studio/server.cjs +4699 -304
- package/dist/studio/server.cjs.map +1 -1
- package/dist/studio/server.d.ts.map +1 -1
- package/dist/studio/server.js +4697 -302
- package/dist/studio/server.js.map +1 -1
- package/dist/studio/shared.d.ts +20 -0
- package/dist/studio/shared.d.ts.map +1 -0
- package/dist/studio/studio-cache.d.ts +93 -0
- package/dist/studio/studio-cache.d.ts.map +1 -0
- package/package.json +2 -2
package/dist/runtime.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var path = require('path');
|
|
4
4
|
var zod = require('zod');
|
|
5
|
-
var
|
|
5
|
+
var fs3 = require('fs');
|
|
6
6
|
var matter = require('gray-matter');
|
|
7
7
|
var Database = require('better-sqlite3');
|
|
8
8
|
var remarkMdx = require('remark-mdx');
|
|
@@ -12,7 +12,7 @@ var unified = require('unified');
|
|
|
12
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
13
|
|
|
14
14
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
15
|
-
var
|
|
15
|
+
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
16
16
|
var matter__default = /*#__PURE__*/_interopDefault(matter);
|
|
17
17
|
var Database__default = /*#__PURE__*/_interopDefault(Database);
|
|
18
18
|
var remarkMdx__default = /*#__PURE__*/_interopDefault(remarkMdx);
|
|
@@ -142,7 +142,15 @@ function resolveConfig(input, baseDir) {
|
|
|
142
142
|
const projectRoot = path__default.default.resolve(process.cwd(), raw.rootDir);
|
|
143
143
|
const contentRoot = path__default.default.resolve(projectRoot, raw.contentDir ?? "content");
|
|
144
144
|
const storePath = path__default.default.resolve(projectRoot, raw.store ?? ".scribe/store.sqlite");
|
|
145
|
-
|
|
145
|
+
let assetsPath;
|
|
146
|
+
let assets;
|
|
147
|
+
if (raw.assets !== void 0 || raw.assetsDir !== void 0) {
|
|
148
|
+
const dir = raw.assets?.dir ?? raw.assetsDir ?? "public";
|
|
149
|
+
const publicPath = raw.assets?.publicPath ?? "/";
|
|
150
|
+
const managedDirs = raw.assets?.managedDirs ?? [];
|
|
151
|
+
assetsPath = path__default.default.resolve(projectRoot, dir);
|
|
152
|
+
assets = { assetsPath, publicPath, managedDirs };
|
|
153
|
+
}
|
|
146
154
|
const seenIds = /* @__PURE__ */ new Set();
|
|
147
155
|
const types = raw.types.map((type) => {
|
|
148
156
|
if (seenIds.has(type.id)) {
|
|
@@ -157,13 +165,17 @@ function resolveConfig(input, baseDir) {
|
|
|
157
165
|
contentDir: type.contentDir ?? type.id,
|
|
158
166
|
label: type.label ?? type.id.charAt(0).toUpperCase() + type.id.slice(1),
|
|
159
167
|
slugStrategy: type.slugStrategy ?? "fixed",
|
|
160
|
-
indexFallback: type.indexFallback ?? (type.path ? "en" : "none")
|
|
168
|
+
indexFallback: type.indexFallback ?? (type.path ? "en" : "none"),
|
|
169
|
+
// `body` defaults to true (fully backwards compatible). A resolved type
|
|
170
|
+
// always carries an explicit boolean so consumers read one source of truth.
|
|
171
|
+
body: type.body ?? true
|
|
161
172
|
};
|
|
162
173
|
});
|
|
163
174
|
const config = {
|
|
164
175
|
rootDir: contentRoot,
|
|
165
176
|
storePath,
|
|
166
177
|
assetsPath,
|
|
178
|
+
assets,
|
|
167
179
|
locales: [...raw.locales],
|
|
168
180
|
defaultLocale,
|
|
169
181
|
localeRouting,
|
|
@@ -199,6 +211,7 @@ function deriveLocaleFallbacks(locales, defaultLocale) {
|
|
|
199
211
|
}
|
|
200
212
|
var FIELD_KIND = /* @__PURE__ */ Symbol.for("@genlook/scribe/fieldKind");
|
|
201
213
|
var RELATION_META = /* @__PURE__ */ Symbol.for("@genlook/scribe/relationMeta");
|
|
214
|
+
var ASSET_META = /* @__PURE__ */ Symbol.for("@genlook/scribe/assetMeta");
|
|
202
215
|
function getFieldKind(schema) {
|
|
203
216
|
const tagged = schema;
|
|
204
217
|
return tagged[FIELD_KIND] ?? "structural";
|
|
@@ -226,6 +239,29 @@ function getRelationTarget(schema) {
|
|
|
226
239
|
}
|
|
227
240
|
return null;
|
|
228
241
|
}
|
|
242
|
+
function getAssetMeta(schema) {
|
|
243
|
+
let current = schema;
|
|
244
|
+
for (let i = 0; i < 8; i++) {
|
|
245
|
+
const tagged = current;
|
|
246
|
+
if (tagged[ASSET_META]) {
|
|
247
|
+
return tagged[ASSET_META];
|
|
248
|
+
}
|
|
249
|
+
if (typeof tagged.unwrap === "function") {
|
|
250
|
+
current = tagged.unwrap();
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (typeof tagged.removeDefault === "function") {
|
|
254
|
+
current = tagged.removeDefault();
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (tagged._def?.innerType) {
|
|
258
|
+
current = tagged._def.innerType;
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
229
265
|
function peelOptionalWrappers(schema) {
|
|
230
266
|
let current = schema;
|
|
231
267
|
for (let i = 0; i < 8; i++) {
|
|
@@ -259,7 +295,23 @@ function introspectSchema(schema, prefix = []) {
|
|
|
259
295
|
kind: "relation",
|
|
260
296
|
relationTarget: relation.typeId,
|
|
261
297
|
relationMultiple: relation.multiple,
|
|
262
|
-
relationOptional: relation.optional
|
|
298
|
+
relationOptional: relation.optional,
|
|
299
|
+
relationOnTargetDelete: relation.onTargetDelete
|
|
300
|
+
}
|
|
301
|
+
];
|
|
302
|
+
}
|
|
303
|
+
const asset = getAssetMeta(schema);
|
|
304
|
+
if (asset && prefix.length > 0) {
|
|
305
|
+
return [
|
|
306
|
+
{
|
|
307
|
+
path: prefix,
|
|
308
|
+
kind: "asset",
|
|
309
|
+
assetDir: asset.dir,
|
|
310
|
+
assetTemplate: asset.template,
|
|
311
|
+
assetFormats: asset.formats,
|
|
312
|
+
assetMaxKB: asset.maxKB,
|
|
313
|
+
assetOptional: asset.optional,
|
|
314
|
+
assetOnDelete: asset.onDelete
|
|
263
315
|
}
|
|
264
316
|
];
|
|
265
317
|
}
|
|
@@ -337,7 +389,7 @@ function pickTranslatable(data, schema) {
|
|
|
337
389
|
return extractByPaths(data, translatablePaths);
|
|
338
390
|
}
|
|
339
391
|
function pickStructural(data, schema) {
|
|
340
|
-
const structuralPaths = introspectSchema(schema).filter((f) => f.kind === "structural" || f.kind === "relation").map((f) => f.path);
|
|
392
|
+
const structuralPaths = introspectSchema(schema).filter((f) => f.kind === "structural" || f.kind === "relation" || f.kind === "asset").map((f) => f.path);
|
|
341
393
|
return extractByPaths(data, structuralPaths);
|
|
342
394
|
}
|
|
343
395
|
function mergeStructuralOntoLocale(localeData, enData, schema) {
|
|
@@ -352,7 +404,7 @@ function deepMerge(base, overlay) {
|
|
|
352
404
|
out[key] = deepMerge(out[key], value);
|
|
353
405
|
} else if (Array.isArray(value) && Array.isArray(out[key])) {
|
|
354
406
|
out[key] = mergeArrayOverlay(out[key], value);
|
|
355
|
-
} else {
|
|
407
|
+
} else if (value !== void 0) {
|
|
356
408
|
out[key] = value;
|
|
357
409
|
}
|
|
358
410
|
}
|
|
@@ -370,6 +422,9 @@ function mergeArrayOverlay(base, overlay) {
|
|
|
370
422
|
function listRelationFields(schema) {
|
|
371
423
|
return introspectSchema(schema).filter((f) => f.kind === "relation");
|
|
372
424
|
}
|
|
425
|
+
function listAssetFields(schema) {
|
|
426
|
+
return introspectSchema(schema).filter((f) => f.kind === "asset");
|
|
427
|
+
}
|
|
373
428
|
var SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
374
429
|
var slugPatternSchema = zod.z.string().regex(SLUG_PATTERN, "slug must be lowercase-kebab-case");
|
|
375
430
|
var isoDateSchema = zod.z.string().regex(/^\d{4}-\d{2}-\d{2}(T[\d:.Z+-]*)?$/, "Use ISO date YYYY-MM-DD or full ISO 8601");
|
|
@@ -450,12 +505,18 @@ function extractBuiltinEnFields(data, _pathTemplate, _enSlug, _defaultLocale) {
|
|
|
450
505
|
delete rest[internalKey];
|
|
451
506
|
}
|
|
452
507
|
}
|
|
508
|
+
let vars;
|
|
509
|
+
if (rest.vars !== void 0) {
|
|
510
|
+
vars = rest.vars;
|
|
511
|
+
delete rest.vars;
|
|
512
|
+
}
|
|
453
513
|
return {
|
|
454
514
|
builtin: {
|
|
455
515
|
publishedAt,
|
|
456
516
|
updatedAt,
|
|
457
517
|
noindex,
|
|
458
|
-
canonicalPathOverride
|
|
518
|
+
canonicalPathOverride,
|
|
519
|
+
vars
|
|
459
520
|
},
|
|
460
521
|
rest,
|
|
461
522
|
issues
|
|
@@ -485,6 +546,7 @@ function mergeBuiltinsIntoFrontmatter(frontmatter, doc, type, defaultLocale, loc
|
|
|
485
546
|
if (doc.updatedAt !== void 0) out.updatedAt = doc.updatedAt;
|
|
486
547
|
out.noindex = doc.noindex;
|
|
487
548
|
out.canonicalPath = resolveCanonicalPathname(type, doc, defaultLocale, localeRouting);
|
|
549
|
+
if (doc.vars !== void 0) out.vars = doc.vars;
|
|
488
550
|
return out;
|
|
489
551
|
}
|
|
490
552
|
function seoFieldsFromEn(enDoc) {
|
|
@@ -574,7 +636,7 @@ function resolveStorePath(config) {
|
|
|
574
636
|
function openStore(config, mode = "readwrite") {
|
|
575
637
|
const storePath = resolveStorePath(config);
|
|
576
638
|
if (mode === "readwrite") {
|
|
577
|
-
|
|
639
|
+
fs3__default.default.mkdirSync(path__default.default.dirname(storePath), { recursive: true });
|
|
578
640
|
}
|
|
579
641
|
const db = new Database__default.default(storePath, { readonly: mode === "readonly" });
|
|
580
642
|
if (mode === "readwrite") {
|
|
@@ -613,6 +675,9 @@ function migrate(db) {
|
|
|
613
675
|
function listTranslationsForType(db, contentType) {
|
|
614
676
|
return db.prepare(`SELECT * FROM translations WHERE content_type = ? ORDER BY en_slug, locale`).all(contentType);
|
|
615
677
|
}
|
|
678
|
+
function bulkLoadTranslations(db) {
|
|
679
|
+
return db.prepare(`SELECT * FROM translations ORDER BY content_type, en_slug, locale`).all();
|
|
680
|
+
}
|
|
616
681
|
|
|
617
682
|
// src/translate/normalize-mdx-body.ts
|
|
618
683
|
function needsMdxEscapeNormalization(body) {
|
|
@@ -729,6 +794,284 @@ function prepareTranslatedMdxBody(body) {
|
|
|
729
794
|
};
|
|
730
795
|
}
|
|
731
796
|
|
|
797
|
+
// src/loader/resolve-assets.ts
|
|
798
|
+
function joinPublicPath(publicPath, webPath) {
|
|
799
|
+
if (publicPath === "" || publicPath === "/") return webPath;
|
|
800
|
+
const suffix = webPath.startsWith("/") ? webPath : `/${webPath}`;
|
|
801
|
+
return publicPath.replace(/\/+$/, "") + suffix;
|
|
802
|
+
}
|
|
803
|
+
function materializeTemplate(template, enSlug) {
|
|
804
|
+
return template.split("{slug}").join(enSlug);
|
|
805
|
+
}
|
|
806
|
+
function setAssetAtPath(container, path6, transform) {
|
|
807
|
+
const [head, ...rest] = path6;
|
|
808
|
+
if (head === void 0) return;
|
|
809
|
+
if (rest.length === 0) {
|
|
810
|
+
const current = container[head];
|
|
811
|
+
const next = transform(typeof current === "string" ? current : void 0);
|
|
812
|
+
if (next !== void 0) container[head] = next;
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (rest[0] === "*") {
|
|
816
|
+
const arr = container[head];
|
|
817
|
+
if (!Array.isArray(arr)) return;
|
|
818
|
+
for (const item of arr) {
|
|
819
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
820
|
+
setAssetAtPath(item, rest.slice(1), transform);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
const child = container[head];
|
|
826
|
+
if (child && typeof child === "object" && !Array.isArray(child)) {
|
|
827
|
+
setAssetAtPath(child, rest, transform);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
function resolveDocumentAssets(doc, assetFields, assets) {
|
|
831
|
+
const frontmatter = doc.frontmatter;
|
|
832
|
+
for (const f of assetFields) {
|
|
833
|
+
setAssetAtPath(frontmatter, f.path, (current) => {
|
|
834
|
+
let value = current;
|
|
835
|
+
if (value === void 0) {
|
|
836
|
+
if (!f.assetTemplate) return void 0;
|
|
837
|
+
value = materializeTemplate(f.assetTemplate, doc.enSlug);
|
|
838
|
+
}
|
|
839
|
+
return joinPublicPath(assets.publicPath, value);
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// src/inline/tokens.ts
|
|
845
|
+
var TOKEN_OPEN = "${{";
|
|
846
|
+
var ESCAPE_SEQUENCE = "$\\{{";
|
|
847
|
+
var ESCAPE_RESULT = "${{";
|
|
848
|
+
function placeholderMarker(n) {
|
|
849
|
+
return `%%${n}%%`;
|
|
850
|
+
}
|
|
851
|
+
function findTokenEnd(body, start) {
|
|
852
|
+
let i = start;
|
|
853
|
+
let inString = false;
|
|
854
|
+
while (i < body.length) {
|
|
855
|
+
const ch = body[i];
|
|
856
|
+
if (ch === "\n") return -1;
|
|
857
|
+
if (inString) {
|
|
858
|
+
if (ch === "\\") {
|
|
859
|
+
i += 2;
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
if (ch === '"') inString = false;
|
|
863
|
+
i += 1;
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
if (ch === '"') {
|
|
867
|
+
inString = true;
|
|
868
|
+
i += 1;
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
if (ch === "}" && body[i + 1] === "}") return i;
|
|
872
|
+
i += 1;
|
|
873
|
+
}
|
|
874
|
+
return -1;
|
|
875
|
+
}
|
|
876
|
+
function parseTokenInner(inner) {
|
|
877
|
+
const colon = inner.indexOf(":");
|
|
878
|
+
if (colon === -1) {
|
|
879
|
+
return { ok: false, reason: `missing kind separator ":"` };
|
|
880
|
+
}
|
|
881
|
+
const kind = inner.slice(0, colon);
|
|
882
|
+
const rest = inner.slice(colon + 1);
|
|
883
|
+
switch (kind) {
|
|
884
|
+
case "static": {
|
|
885
|
+
let text;
|
|
886
|
+
try {
|
|
887
|
+
text = JSON.parse(rest);
|
|
888
|
+
} catch {
|
|
889
|
+
return { ok: false, reason: `static value is not a valid JSON string` };
|
|
890
|
+
}
|
|
891
|
+
if (typeof text !== "string") {
|
|
892
|
+
return { ok: false, reason: `static value must be a JSON string` };
|
|
893
|
+
}
|
|
894
|
+
return { ok: true, token: { kind: "static", text } };
|
|
895
|
+
}
|
|
896
|
+
case "relation": {
|
|
897
|
+
const parts = rest.split(":");
|
|
898
|
+
if (parts.length !== 3) {
|
|
899
|
+
return {
|
|
900
|
+
ok: false,
|
|
901
|
+
reason: `relation must be relation:<typeId>:<enSlug>:href or relation:<typeId>:<enSlug>:slug`
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
const [targetTypeId, enSlug, mode] = parts;
|
|
905
|
+
if (!targetTypeId || !enSlug) {
|
|
906
|
+
return { ok: false, reason: `relation is missing a typeId or enSlug` };
|
|
907
|
+
}
|
|
908
|
+
if (mode !== "href" && mode !== "slug") {
|
|
909
|
+
return { ok: false, reason: `relation mode must be "href" or "slug" (got "${mode}")` };
|
|
910
|
+
}
|
|
911
|
+
return {
|
|
912
|
+
ok: true,
|
|
913
|
+
token: {
|
|
914
|
+
kind: "relation",
|
|
915
|
+
targetTypeId,
|
|
916
|
+
enSlug,
|
|
917
|
+
mode
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
case "asset": {
|
|
922
|
+
if (!rest.startsWith("/")) {
|
|
923
|
+
return { ok: false, reason: `asset path must start with "/" (got "${rest}")` };
|
|
924
|
+
}
|
|
925
|
+
return { ok: true, token: { kind: "asset", webPath: rest } };
|
|
926
|
+
}
|
|
927
|
+
case "var": {
|
|
928
|
+
if (!rest) return { ok: false, reason: `var is missing a key` };
|
|
929
|
+
return { ok: true, token: { kind: "var", key: rest } };
|
|
930
|
+
}
|
|
931
|
+
default:
|
|
932
|
+
return { ok: false, reason: `unknown token kind "${kind}"` };
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
function extractInlineTokens(body) {
|
|
936
|
+
const tokens = [];
|
|
937
|
+
const malformed = [];
|
|
938
|
+
let out = "";
|
|
939
|
+
let i = 0;
|
|
940
|
+
while (i < body.length) {
|
|
941
|
+
const open = body.indexOf(TOKEN_OPEN, i);
|
|
942
|
+
if (open === -1) {
|
|
943
|
+
out += body.slice(i);
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
out += body.slice(i, open);
|
|
947
|
+
const contentStart = open + TOKEN_OPEN.length;
|
|
948
|
+
const end = findTokenEnd(body, contentStart);
|
|
949
|
+
if (end === -1) {
|
|
950
|
+
malformed.push({
|
|
951
|
+
raw: body.slice(open),
|
|
952
|
+
reason: `unterminated token (missing "}}")`,
|
|
953
|
+
index: open
|
|
954
|
+
});
|
|
955
|
+
out += body.slice(open);
|
|
956
|
+
break;
|
|
957
|
+
}
|
|
958
|
+
const raw = body.slice(open, end + 2);
|
|
959
|
+
const inner = body.slice(contentStart, end);
|
|
960
|
+
const parsed = parseTokenInner(inner);
|
|
961
|
+
if (parsed.ok) {
|
|
962
|
+
tokens.push({ ...parsed.token, raw });
|
|
963
|
+
out += placeholderMarker(tokens.length);
|
|
964
|
+
} else {
|
|
965
|
+
malformed.push({ raw, reason: parsed.reason, index: open });
|
|
966
|
+
out += raw;
|
|
967
|
+
}
|
|
968
|
+
i = end + 2;
|
|
969
|
+
}
|
|
970
|
+
return { placeholderBody: out, tokens, malformed };
|
|
971
|
+
}
|
|
972
|
+
function fillPlaceholders(body, resolvedValues) {
|
|
973
|
+
return body.replace(/%%(\d+)%%/g, (match, n) => {
|
|
974
|
+
const idx = Number(n) - 1;
|
|
975
|
+
return idx >= 0 && idx < resolvedValues.length ? resolvedValues[idx] : match;
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
function unescapeInlineTokens(body) {
|
|
979
|
+
return body.split(ESCAPE_SEQUENCE).join(ESCAPE_RESULT);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// src/inline/resolve-tokens.ts
|
|
983
|
+
function slugKey(typeId, enSlug, locale) {
|
|
984
|
+
return `${typeId}\0${enSlug}\0${locale}`;
|
|
985
|
+
}
|
|
986
|
+
function isStringRecord(value) {
|
|
987
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
988
|
+
return Object.values(value).every((v) => typeof v === "string");
|
|
989
|
+
}
|
|
990
|
+
function createInlineResolver(config, options = {}) {
|
|
991
|
+
const linkStyle = options.linkStyle ?? "app";
|
|
992
|
+
const exportExtension = options.exportExtension ?? ".md";
|
|
993
|
+
const urlBuilder = createUrlBuilder(config);
|
|
994
|
+
const typeById = new Map(config.types.map((t) => [t.id, t]));
|
|
995
|
+
const storePath = resolveStorePath(config);
|
|
996
|
+
let slugCache = null;
|
|
997
|
+
const localizedSlugIndex = () => {
|
|
998
|
+
let mtime = 0;
|
|
999
|
+
try {
|
|
1000
|
+
mtime = fs3__default.default.statSync(storePath).mtimeMs;
|
|
1001
|
+
} catch {
|
|
1002
|
+
mtime = 0;
|
|
1003
|
+
}
|
|
1004
|
+
if (slugCache && slugCache.mtime === mtime) return slugCache.index;
|
|
1005
|
+
const index = /* @__PURE__ */ new Map();
|
|
1006
|
+
try {
|
|
1007
|
+
const db = openStore(config, "readonly");
|
|
1008
|
+
try {
|
|
1009
|
+
for (const row of bulkLoadTranslations(db)) {
|
|
1010
|
+
index.set(slugKey(row.content_type, row.en_slug, row.locale), row.slug);
|
|
1011
|
+
}
|
|
1012
|
+
} finally {
|
|
1013
|
+
db.close();
|
|
1014
|
+
}
|
|
1015
|
+
} catch {
|
|
1016
|
+
}
|
|
1017
|
+
slugCache = { mtime, index };
|
|
1018
|
+
return index;
|
|
1019
|
+
};
|
|
1020
|
+
const localizedSlug = (typeId, enSlug, locale) => {
|
|
1021
|
+
if (locale === config.defaultLocale) return enSlug;
|
|
1022
|
+
const index = localizedSlugIndex();
|
|
1023
|
+
const direct = index.get(slugKey(typeId, enSlug, locale));
|
|
1024
|
+
if (direct) return direct;
|
|
1025
|
+
for (const fb of config.localeFallbacks?.[locale] ?? []) {
|
|
1026
|
+
const hit = index.get(slugKey(typeId, enSlug, fb));
|
|
1027
|
+
if (hit) return hit;
|
|
1028
|
+
}
|
|
1029
|
+
return enSlug;
|
|
1030
|
+
};
|
|
1031
|
+
return {
|
|
1032
|
+
resolve(token, enFrontmatter, locale) {
|
|
1033
|
+
switch (token.kind) {
|
|
1034
|
+
case "static":
|
|
1035
|
+
return token.text;
|
|
1036
|
+
case "var": {
|
|
1037
|
+
const vars = enFrontmatter.vars;
|
|
1038
|
+
if (isStringRecord(vars) && typeof vars[token.key] === "string") {
|
|
1039
|
+
return vars[token.key];
|
|
1040
|
+
}
|
|
1041
|
+
return "";
|
|
1042
|
+
}
|
|
1043
|
+
case "asset": {
|
|
1044
|
+
const assets = config.assets;
|
|
1045
|
+
return assets ? joinPublicPath(assets.publicPath, token.webPath) : token.webPath;
|
|
1046
|
+
}
|
|
1047
|
+
case "relation": {
|
|
1048
|
+
const type = typeById.get(token.targetTypeId);
|
|
1049
|
+
if (!type) return "";
|
|
1050
|
+
if (token.mode === "slug") return token.enSlug;
|
|
1051
|
+
if (!isRoutableType(type)) return "";
|
|
1052
|
+
const slug = localizedSlug(token.targetTypeId, token.enSlug, locale);
|
|
1053
|
+
if (linkStyle === "export") {
|
|
1054
|
+
return urlBuilder.resolvePath(type.path, `${slug}${exportExtension}`, locale);
|
|
1055
|
+
}
|
|
1056
|
+
return type.path.replace("{slug}", slug);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
function substituteEnInlineBody(rawBody, enFrontmatter, defaultLocale, resolver) {
|
|
1063
|
+
const { placeholderBody, tokens } = extractInlineTokens(rawBody);
|
|
1064
|
+
if (tokens.length === 0) return unescapeInlineTokens(placeholderBody);
|
|
1065
|
+
const values = tokens.map((t) => resolver.resolve(t, enFrontmatter, defaultLocale));
|
|
1066
|
+
return unescapeInlineTokens(fillPlaceholders(placeholderBody, values));
|
|
1067
|
+
}
|
|
1068
|
+
function fillTranslatedInlineBody(translatedBody, enRawBody, enFrontmatter, locale, resolver) {
|
|
1069
|
+
const { tokens } = extractInlineTokens(enRawBody);
|
|
1070
|
+
if (tokens.length === 0) return unescapeInlineTokens(translatedBody);
|
|
1071
|
+
const values = tokens.map((t) => resolver.resolve(t, enFrontmatter, locale));
|
|
1072
|
+
return unescapeInlineTokens(fillPlaceholders(translatedBody, values));
|
|
1073
|
+
}
|
|
1074
|
+
|
|
732
1075
|
// src/loader/normalize-en.ts
|
|
733
1076
|
function normalizeEnFrontmatter(data) {
|
|
734
1077
|
const out = { ...data };
|
|
@@ -754,14 +1097,14 @@ function isPostFile(name) {
|
|
|
754
1097
|
return isPublishableContentFile(name);
|
|
755
1098
|
}
|
|
756
1099
|
function listEnFiles(contentDir) {
|
|
757
|
-
if (!
|
|
758
|
-
return
|
|
1100
|
+
if (!fs3__default.default.existsSync(contentDir)) return [];
|
|
1101
|
+
return fs3__default.default.readdirSync(contentDir).filter(isPostFile).map((f) => path__default.default.join(contentDir, f));
|
|
759
1102
|
}
|
|
760
1103
|
function slugFromPath(filePath) {
|
|
761
1104
|
return path__default.default.basename(filePath).replace(/\.(md|mdx)$/, "");
|
|
762
1105
|
}
|
|
763
1106
|
function parseEnMdx(filePath, config, type) {
|
|
764
|
-
const raw =
|
|
1107
|
+
const raw = fs3__default.default.readFileSync(filePath, "utf8");
|
|
765
1108
|
const parsed = matter__default.default(raw);
|
|
766
1109
|
const slug = slugFromPath(filePath);
|
|
767
1110
|
const normalized = normalizeEnFrontmatter(parsed.data);
|
|
@@ -800,6 +1143,7 @@ function parseEnMdx(filePath, config, type) {
|
|
|
800
1143
|
updatedAt: builtin.updatedAt,
|
|
801
1144
|
noindex: builtin.noindex,
|
|
802
1145
|
canonicalPathOverride: builtin.canonicalPathOverride,
|
|
1146
|
+
vars: builtin.vars,
|
|
803
1147
|
slug,
|
|
804
1148
|
locale: config.defaultLocale
|
|
805
1149
|
},
|
|
@@ -824,12 +1168,15 @@ function parseEnMdx(filePath, config, type) {
|
|
|
824
1168
|
noindex: builtin.noindex,
|
|
825
1169
|
canonicalPathOverride: builtin.canonicalPathOverride,
|
|
826
1170
|
frontmatter,
|
|
827
|
-
|
|
1171
|
+
// Bodyless types (`body: false`) never carry a body: the loader skips the
|
|
1172
|
+
// MDX body entirely so runtimes, exports, and hashing all see an empty body.
|
|
1173
|
+
// A stray body is reported separately by `scribe validate`.
|
|
1174
|
+
content: type.body === false ? "" : parsed.content,
|
|
828
1175
|
filePath
|
|
829
1176
|
};
|
|
830
1177
|
return { document, issues };
|
|
831
1178
|
}
|
|
832
|
-
function buildDocumentFromTranslation(row, enDoc, type, config) {
|
|
1179
|
+
function buildDocumentFromTranslation(row, enDoc, type, config, inlineResolver) {
|
|
833
1180
|
const localeFm = JSON.parse(row.frontmatter_json);
|
|
834
1181
|
const merged = mergeStructuralOntoLocale(localeFm, enDoc.frontmatter, type.schema);
|
|
835
1182
|
const seo = seoFieldsFromEn(enDoc);
|
|
@@ -840,6 +1187,14 @@ function buildDocumentFromTranslation(row, enDoc, type, config) {
|
|
|
840
1187
|
config.defaultLocale,
|
|
841
1188
|
config.localeRouting
|
|
842
1189
|
);
|
|
1190
|
+
const preparedBody = prepareTranslatedMdxBody(row.body).body;
|
|
1191
|
+
const content = inlineResolver ? fillTranslatedInlineBody(
|
|
1192
|
+
preparedBody,
|
|
1193
|
+
enDoc.content,
|
|
1194
|
+
enDoc.frontmatter,
|
|
1195
|
+
row.locale,
|
|
1196
|
+
inlineResolver
|
|
1197
|
+
) : preparedBody;
|
|
843
1198
|
return {
|
|
844
1199
|
slug: row.slug,
|
|
845
1200
|
enSlug: row.en_slug,
|
|
@@ -849,14 +1204,16 @@ function buildDocumentFromTranslation(row, enDoc, type, config) {
|
|
|
849
1204
|
noindex: seo.noindex,
|
|
850
1205
|
canonicalPathOverride: seo.canonicalPathOverride,
|
|
851
1206
|
frontmatter,
|
|
852
|
-
content
|
|
1207
|
+
content
|
|
853
1208
|
};
|
|
854
1209
|
}
|
|
855
1210
|
var DEV_REVALIDATE_MS = 1500;
|
|
856
|
-
|
|
1211
|
+
var contentVersion = 0;
|
|
1212
|
+
function createContentLoader(config, type, options = {}) {
|
|
857
1213
|
let cached = null;
|
|
858
1214
|
let signature = "";
|
|
859
1215
|
let lastCheck = 0;
|
|
1216
|
+
let builtVersion = contentVersion;
|
|
860
1217
|
const contentDir = path__default.default.join(
|
|
861
1218
|
/* turbopackIgnore: true */
|
|
862
1219
|
config.rootDir,
|
|
@@ -864,18 +1221,34 @@ function createContentLoader(config, type) {
|
|
|
864
1221
|
);
|
|
865
1222
|
const storePath = resolveStorePath(config);
|
|
866
1223
|
const isProd = process.env.NODE_ENV === "production";
|
|
1224
|
+
const assets = config.assets;
|
|
1225
|
+
const assetFields = assets && options.resolveAssets ? listAssetFields(type.schema) : [];
|
|
1226
|
+
const resolveAssets = (doc) => {
|
|
1227
|
+
if (assetFields.length > 0 && assets) resolveDocumentAssets(doc, assetFields, assets);
|
|
1228
|
+
};
|
|
1229
|
+
const inlineResolver = options.inlineResolver;
|
|
1230
|
+
const resolveEnInline = (doc) => {
|
|
1231
|
+
if (inlineResolver) {
|
|
1232
|
+
doc.content = substituteEnInlineBody(
|
|
1233
|
+
doc.content,
|
|
1234
|
+
doc.frontmatter,
|
|
1235
|
+
config.defaultLocale,
|
|
1236
|
+
inlineResolver
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
867
1240
|
function computeSignature() {
|
|
868
1241
|
const files = listEnFiles(contentDir);
|
|
869
1242
|
let newest = 0;
|
|
870
1243
|
for (const f of files) {
|
|
871
1244
|
try {
|
|
872
|
-
newest = Math.max(newest,
|
|
1245
|
+
newest = Math.max(newest, fs3__default.default.statSync(f).mtimeMs);
|
|
873
1246
|
} catch {
|
|
874
1247
|
}
|
|
875
1248
|
}
|
|
876
1249
|
let store = 0;
|
|
877
1250
|
try {
|
|
878
|
-
store =
|
|
1251
|
+
store = fs3__default.default.statSync(storePath).mtimeMs;
|
|
879
1252
|
} catch {
|
|
880
1253
|
}
|
|
881
1254
|
return `${files.length}:${newest}:${store}`;
|
|
@@ -921,16 +1294,28 @@ function createContentLoader(config, type) {
|
|
|
921
1294
|
for (const row of rowsByLocale.get(locale) ?? []) {
|
|
922
1295
|
const enDoc = englishBySlug.get(row.en_slug);
|
|
923
1296
|
if (!enDoc) continue;
|
|
924
|
-
const doc = buildDocumentFromTranslation(row, enDoc, type, config);
|
|
1297
|
+
const doc = buildDocumentFromTranslation(row, enDoc, type, config, inlineResolver);
|
|
1298
|
+
resolveAssets(doc);
|
|
925
1299
|
bySlug.set(doc.slug, doc);
|
|
926
1300
|
byEnSlug.set(row.en_slug, doc);
|
|
927
1301
|
}
|
|
928
1302
|
out.set(locale, { bySlug, byEnSlug });
|
|
929
1303
|
}
|
|
1304
|
+
for (const doc of englishBySlug.values()) {
|
|
1305
|
+
resolveAssets(doc);
|
|
1306
|
+
resolveEnInline(doc);
|
|
1307
|
+
}
|
|
930
1308
|
return out;
|
|
931
1309
|
}
|
|
932
1310
|
return () => {
|
|
933
1311
|
if (cached) {
|
|
1312
|
+
if (contentVersion !== builtVersion) {
|
|
1313
|
+
cached = build();
|
|
1314
|
+
builtVersion = contentVersion;
|
|
1315
|
+
lastCheck = Date.now();
|
|
1316
|
+
signature = isProd ? "" : computeSignature();
|
|
1317
|
+
return cached;
|
|
1318
|
+
}
|
|
934
1319
|
if (isProd) return cached;
|
|
935
1320
|
const now = Date.now();
|
|
936
1321
|
if (now - lastCheck < DEV_REVALIDATE_MS) return cached;
|
|
@@ -943,6 +1328,7 @@ function createContentLoader(config, type) {
|
|
|
943
1328
|
}
|
|
944
1329
|
lastCheck = Date.now();
|
|
945
1330
|
cached = build();
|
|
1331
|
+
builtVersion = contentVersion;
|
|
946
1332
|
signature = isProd ? "" : computeSignature();
|
|
947
1333
|
return cached;
|
|
948
1334
|
};
|
|
@@ -1032,8 +1418,11 @@ function comparatorFor(orderBy) {
|
|
|
1032
1418
|
return (a, b) => a.slug.localeCompare(b.slug);
|
|
1033
1419
|
}
|
|
1034
1420
|
}
|
|
1035
|
-
function buildRuntime(config, type, getRuntime) {
|
|
1036
|
-
const load = createContentLoader(config, type
|
|
1421
|
+
function buildRuntime(config, type, getRuntime, options = {}) {
|
|
1422
|
+
const load = createContentLoader(config, type, {
|
|
1423
|
+
resolveAssets: options.resolveAssets,
|
|
1424
|
+
inlineResolver: options.inlineResolver
|
|
1425
|
+
});
|
|
1037
1426
|
const relationFields = new Map(
|
|
1038
1427
|
listRelationFields(type.schema).filter((f) => f.path.length === 1).map((f) => [f.path[0], f])
|
|
1039
1428
|
);
|
|
@@ -1050,12 +1439,12 @@ function buildRuntime(config, type, getRuntime) {
|
|
|
1050
1439
|
id: type.id,
|
|
1051
1440
|
config: type,
|
|
1052
1441
|
load,
|
|
1053
|
-
list(locale = config.defaultLocale,
|
|
1442
|
+
list(locale = config.defaultLocale, options2 = {}) {
|
|
1054
1443
|
const idx = load().get(locale);
|
|
1055
1444
|
if (!idx) return [];
|
|
1056
1445
|
const docs = Array.from(idx.bySlug.values());
|
|
1057
|
-
docs.sort(comparatorFor(
|
|
1058
|
-
return
|
|
1446
|
+
docs.sort(comparatorFor(options2.orderBy ?? type.orderBy ?? "slug"));
|
|
1447
|
+
return options2.limit !== void 0 ? docs.slice(0, options2.limit) : docs;
|
|
1059
1448
|
},
|
|
1060
1449
|
get(slug, locale = config.defaultLocale) {
|
|
1061
1450
|
return load().get(locale)?.bySlug.get(slug) ?? null;
|
|
@@ -1068,7 +1457,7 @@ function buildRuntime(config, type, getRuntime) {
|
|
|
1068
1457
|
load(),
|
|
1069
1458
|
type,
|
|
1070
1459
|
config.localeRouting,
|
|
1071
|
-
config.localeFallbacks[locale] ?? []
|
|
1460
|
+
config.localeFallbacks?.[locale] ?? []
|
|
1072
1461
|
);
|
|
1073
1462
|
if (result.document && type.path) {
|
|
1074
1463
|
return {
|
|
@@ -1082,15 +1471,15 @@ function buildRuntime(config, type, getRuntime) {
|
|
|
1082
1471
|
}
|
|
1083
1472
|
return result;
|
|
1084
1473
|
},
|
|
1085
|
-
staticParams(
|
|
1474
|
+
staticParams(options2 = {}) {
|
|
1086
1475
|
assertRoutable("staticParams");
|
|
1087
1476
|
const all = load();
|
|
1088
1477
|
const enIdx = all.get(config.defaultLocale);
|
|
1089
1478
|
if (!enIdx) return [];
|
|
1090
1479
|
const params = [];
|
|
1091
|
-
for (const locale of
|
|
1480
|
+
for (const locale of options2.locales ?? config.locales) {
|
|
1092
1481
|
const localeIdx = all.get(locale);
|
|
1093
|
-
const fallbacks = config.localeFallbacks[locale] ?? [];
|
|
1482
|
+
const fallbacks = config.localeFallbacks?.[locale] ?? [];
|
|
1094
1483
|
for (const doc of enIdx.bySlug.values()) {
|
|
1095
1484
|
let slug;
|
|
1096
1485
|
if (locale === config.defaultLocale) {
|
|
@@ -1169,7 +1558,7 @@ function buildRuntime(config, type, getRuntime) {
|
|
|
1169
1558
|
};
|
|
1170
1559
|
return runtime;
|
|
1171
1560
|
}
|
|
1172
|
-
function createProject(config) {
|
|
1561
|
+
function createProject(config, options = {}) {
|
|
1173
1562
|
const runtimes = /* @__PURE__ */ new Map();
|
|
1174
1563
|
const getRuntime = (id) => {
|
|
1175
1564
|
const runtime = runtimes.get(id);
|
|
@@ -1178,8 +1567,18 @@ function createProject(config) {
|
|
|
1178
1567
|
}
|
|
1179
1568
|
return runtime;
|
|
1180
1569
|
};
|
|
1570
|
+
const inlineResolver = options.resolveInlineTokens ? createInlineResolver(config, {
|
|
1571
|
+
linkStyle: options.inlineLinkStyle,
|
|
1572
|
+
exportExtension: options.exportLinkExtension
|
|
1573
|
+
}) : void 0;
|
|
1181
1574
|
for (const type of config.types) {
|
|
1182
|
-
runtimes.set(
|
|
1575
|
+
runtimes.set(
|
|
1576
|
+
type.id,
|
|
1577
|
+
buildRuntime(config, type, getRuntime, {
|
|
1578
|
+
resolveAssets: options.resolveAssets,
|
|
1579
|
+
inlineResolver
|
|
1580
|
+
})
|
|
1581
|
+
);
|
|
1183
1582
|
}
|
|
1184
1583
|
return {
|
|
1185
1584
|
config,
|
|
@@ -1272,10 +1671,10 @@ function redirectsFilePath(config, type) {
|
|
|
1272
1671
|
}
|
|
1273
1672
|
function loadTypeRedirectsFile(config, type) {
|
|
1274
1673
|
const filePath = redirectsFilePath(config, type);
|
|
1275
|
-
if (!
|
|
1674
|
+
if (!fs3__default.default.existsSync(filePath)) return null;
|
|
1276
1675
|
let raw;
|
|
1277
1676
|
try {
|
|
1278
|
-
raw = JSON.parse(
|
|
1677
|
+
raw = JSON.parse(fs3__default.default.readFileSync(filePath, "utf8"));
|
|
1279
1678
|
} catch (error) {
|
|
1280
1679
|
throw new Error(
|
|
1281
1680
|
`${type.id}: failed to parse ${TYPE_REDIRECTS_FILENAME}: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -1431,7 +1830,11 @@ async function generateSitemap(project, options) {
|
|
|
1431
1830
|
// src/create-scribe.ts
|
|
1432
1831
|
function createScribe(input) {
|
|
1433
1832
|
const config = resolveConfig(input);
|
|
1434
|
-
const project = createProject(config
|
|
1833
|
+
const project = createProject(config, {
|
|
1834
|
+
resolveAssets: true,
|
|
1835
|
+
resolveInlineTokens: true,
|
|
1836
|
+
inlineLinkStyle: "app"
|
|
1837
|
+
});
|
|
1435
1838
|
const scribe = {
|
|
1436
1839
|
config,
|
|
1437
1840
|
project,
|
|
@@ -1440,6 +1843,17 @@ function createScribe(input) {
|
|
|
1440
1843
|
listRoutableTypes: project.listRoutableTypes,
|
|
1441
1844
|
sitemap(options) {
|
|
1442
1845
|
return generateSitemap(project, options);
|
|
1846
|
+
},
|
|
1847
|
+
assets: {
|
|
1848
|
+
url(ref, opts) {
|
|
1849
|
+
if (opts && Object.keys(opts).length > 0) {
|
|
1850
|
+
throw new Error(
|
|
1851
|
+
`scribe.assets.url: options are reserved for a future pipeline; got ${Object.keys(opts).join(", ")}`
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
const assetsConfig = config.assets;
|
|
1855
|
+
return assetsConfig ? joinPublicPath(assetsConfig.publicPath, ref) : ref;
|
|
1856
|
+
}
|
|
1443
1857
|
}
|
|
1444
1858
|
};
|
|
1445
1859
|
for (const type of config.types) {
|