storyblok 4.14.3 → 4.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -5299,6 +5299,121 @@ const storyblokSchemas = /* @__PURE__ */ new Map([
5299
5299
  ["richtext", getRichtextJSONSchema]
5300
5300
  ]);
5301
5301
 
5302
+ function generateStoryblokImports(storyblokPropertyTypes, STORY_TYPE) {
5303
+ const imports = [];
5304
+ const needsISbStoryData = storyblokPropertyTypes.has(STORY_TYPE);
5305
+ if (needsISbStoryData) {
5306
+ imports.push(`import type { ${STORY_TYPE} } from '@storyblok/js';`);
5307
+ storyblokPropertyTypes.delete(STORY_TYPE);
5308
+ }
5309
+ if (storyblokPropertyTypes.size > 0) {
5310
+ const typeImports = Array.from(storyblokPropertyTypes).map((type) => {
5311
+ const pascalType = toPascalCase(type);
5312
+ return `Storyblok${pascalType}`;
5313
+ });
5314
+ imports.push(`import type { ${typeImports.join(", ")} } from '../storyblok.d.ts';`);
5315
+ }
5316
+ return imports;
5317
+ }
5318
+ function escapeRegExp(value) {
5319
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5320
+ }
5321
+ function hasIdentifier(content, identifier) {
5322
+ const regex = new RegExp(`\\b${escapeRegExp(identifier)}\\b`, "g");
5323
+ return regex.test(content);
5324
+ }
5325
+ function detectUsedStoryblokTypes(content, storyblokPropertyTypes, STORY_TYPE) {
5326
+ const usedTypes = [];
5327
+ if (content.includes(STORY_TYPE)) {
5328
+ usedTypes.push(STORY_TYPE);
5329
+ }
5330
+ Array.from(storyblokPropertyTypes).forEach((type) => {
5331
+ const pascalType = toPascalCase(type);
5332
+ if (content.includes(`Storyblok${pascalType}`)) {
5333
+ usedTypes.push(type);
5334
+ }
5335
+ });
5336
+ return usedTypes;
5337
+ }
5338
+ function detectUsedDatasourceTypes(content, datasourceResults) {
5339
+ return datasourceResults.filter((ds) => hasIdentifier(content, ds.title)).map((ds) => ds.title);
5340
+ }
5341
+ function detectReferencedComponents(content, currentTitle, componentResults) {
5342
+ return componentResults.filter((c) => c.title !== currentTitle && hasIdentifier(content, c.title)).map((c) => c.title);
5343
+ }
5344
+ function generateComponentImports(componentContent, componentTitle, storyblokPropertyTypes, datasourceResults, componentResults, STORY_TYPE) {
5345
+ const imports = [];
5346
+ const usedStoryblokTypes = detectUsedStoryblokTypes(
5347
+ componentContent,
5348
+ storyblokPropertyTypes,
5349
+ STORY_TYPE
5350
+ );
5351
+ if (usedStoryblokTypes.length > 0) {
5352
+ const hasISbStoryData = usedStoryblokTypes.includes(STORY_TYPE);
5353
+ const otherTypes = usedStoryblokTypes.filter((t) => t !== STORY_TYPE);
5354
+ if (hasISbStoryData) {
5355
+ imports.push(`import type { ${STORY_TYPE} } from '@storyblok/js';`);
5356
+ }
5357
+ if (otherTypes.length > 0) {
5358
+ const typeImports = otherTypes.map((type) => {
5359
+ const pascalType = toPascalCase(type);
5360
+ return `Storyblok${pascalType}`;
5361
+ });
5362
+ imports.push(`import type { ${typeImports.join(", ")} } from '../storyblok.d.ts';`);
5363
+ }
5364
+ }
5365
+ const usedDatasourceTypes = detectUsedDatasourceTypes(componentContent, datasourceResults);
5366
+ if (usedDatasourceTypes.length > 0) {
5367
+ imports.push(`import type { ${usedDatasourceTypes.join(", ")} } from './datasource-types.d.ts';`);
5368
+ }
5369
+ const referencedComponents = detectReferencedComponents(
5370
+ componentContent,
5371
+ componentTitle,
5372
+ componentResults
5373
+ );
5374
+ if (referencedComponents.length > 0) {
5375
+ const componentImportsStr = referencedComponents.map((name) => `import type { ${name} } from './${name}.d.ts';`).join("\n");
5376
+ imports.push(componentImportsStr);
5377
+ }
5378
+ return imports;
5379
+ }
5380
+ function createDatasourcesFile(datasourceResults, typeDefs) {
5381
+ if (datasourceResults.length === 0) {
5382
+ return null;
5383
+ }
5384
+ const content = [
5385
+ ...typeDefs,
5386
+ ...datasourceResults.map((r) => r.content)
5387
+ ].join("\n");
5388
+ return { name: "datasource-types", content };
5389
+ }
5390
+ function createContentTypesFile(contentTypeBloks, typeDefs) {
5391
+ if (contentTypeBloks.size === 0) {
5392
+ return null;
5393
+ }
5394
+ const contentTypeNames = Array.from(contentTypeBloks);
5395
+ const imports = contentTypeNames.map((name) => `import type { ${name} } from './${name}.d.ts';`).join("\n");
5396
+ const typeUnion = contentTypeNames.length > 0 ? contentTypeNames.join("\n | ") : "never";
5397
+ const typeDefinition = `export type ContentType =
5398
+ | ${typeUnion};`;
5399
+ const content = [
5400
+ ...typeDefs,
5401
+ imports,
5402
+ typeDefinition
5403
+ ].join("\n");
5404
+ return { name: "content-types", content };
5405
+ }
5406
+ function createComponentFile(componentResult, typeDefs, componentImports) {
5407
+ return {
5408
+ name: componentResult.title,
5409
+ content: [
5410
+ ...typeDefs,
5411
+ ...componentImports,
5412
+ componentResult.content
5413
+ ].join("\n")
5414
+ };
5415
+ }
5416
+
5302
5417
  const STORY_TYPE = "ISbStoryData";
5303
5418
  const DEFAULT_COMPONENT_FILENAME = "storyblok-components";
5304
5419
  const DEFAULT_TYPEDEFS_HEADER = [
@@ -5390,6 +5505,9 @@ const getComponentType = (componentName, options) => {
5390
5505
  return isFirstCharacterNumber ? `_${componentType}` : componentType;
5391
5506
  };
5392
5507
  const getComponentPropertiesTypeAnnotations = async (component, options, spaceData, customFieldsParser) => {
5508
+ if (!component.schema || typeof component.schema !== "object") {
5509
+ return {};
5510
+ }
5393
5511
  return Object.entries(component.schema).reduce(async (accPromise, [key, value]) => {
5394
5512
  const acc = await accPromise;
5395
5513
  if (key.startsWith("tab-")) {
@@ -5551,6 +5669,9 @@ const generateTypes = async (spaceData, options = {
5551
5669
  const datasourcesSchema = spaceData.datasources.map(async (datasource) => {
5552
5670
  const allComponentTypes = resolvedComponentsSchema.map((schema) => schema.title);
5553
5671
  const enumValues = datasource.entries?.filter((d) => d.value).map((d) => d.value);
5672
+ if (!datasource.slug) {
5673
+ return null;
5674
+ }
5554
5675
  const type = getDatasourceTypeTitle(datasource.slug);
5555
5676
  if (allComponentTypes.includes(type)) {
5556
5677
  console.warn(`Warning: Datasource type "${type}" conflicts with existing component type`);
@@ -5563,7 +5684,10 @@ const generateTypes = async (spaceData, options = {
5563
5684
  };
5564
5685
  return datasourceSchema;
5565
5686
  });
5566
- const resolvedDatasourcesSchema = await Promise.all(datasourcesSchema);
5687
+ const resolvedDatasourcesSchemaWithNulls = await Promise.all(datasourcesSchema);
5688
+ const resolvedDatasourcesSchema = resolvedDatasourcesSchemaWithNulls.filter((s) => s !== null);
5689
+ const componentTitles = new Set(resolvedComponentsSchema.map((s) => s.title).filter(Boolean));
5690
+ const datasourceTitles = new Set(resolvedDatasourcesSchema.map((s) => s.title).filter(Boolean));
5567
5691
  const contentTypeSchema = {
5568
5692
  $id: `#/ContentType`,
5569
5693
  title: "ContentType",
@@ -5576,26 +5700,48 @@ const generateTypes = async (spaceData, options = {
5576
5700
  contentTypeSchema
5577
5701
  ];
5578
5702
  const result = await Promise.all(schemas.map(async (schema) => {
5579
- return await compile(schema, schema.title || schema.$id.replace("#/", ""), {
5580
- additionalProperties: !options.strict,
5581
- bannerComment: "",
5582
- ...compilerOptions
5583
- });
5703
+ const title = schema.title || schema.$id.replace("#/", "");
5704
+ return {
5705
+ title,
5706
+ content: await compile(schema, title, {
5707
+ additionalProperties: !options.strict,
5708
+ bannerComment: "",
5709
+ ...compilerOptions
5710
+ }),
5711
+ isComponent: componentTitles.has(title),
5712
+ isDatasource: datasourceTitles.has(title)
5713
+ };
5584
5714
  }));
5585
- const imports = [];
5586
- const needsISbStoryData = storyblokPropertyTypes.has(STORY_TYPE);
5587
- if (needsISbStoryData) {
5588
- imports.push(`import type { ${STORY_TYPE} } from '@storyblok/js';`);
5589
- storyblokPropertyTypes.delete(STORY_TYPE);
5590
- }
5591
- if (storyblokPropertyTypes.size > 0) {
5592
- const typeImports = Array.from(storyblokPropertyTypes).map((type) => {
5593
- const pascalType = toPascalCase(type);
5594
- return `Storyblok${pascalType}`;
5595
- });
5596
- imports.push(`import type { ${typeImports.join(", ")} } from '../storyblok.d.ts';`);
5715
+ const imports = generateStoryblokImports(storyblokPropertyTypes, STORY_TYPE);
5716
+ if (options.separateFiles) {
5717
+ const files = [];
5718
+ const datasourceResults = result.filter((r) => r.isDatasource);
5719
+ const componentResults = result.filter((r) => r.isComponent);
5720
+ const datasourcesFile = createDatasourcesFile(datasourceResults, typeDefs);
5721
+ if (datasourcesFile) {
5722
+ files.push(datasourcesFile);
5723
+ }
5724
+ const contentTypesFile = createContentTypesFile(
5725
+ contentTypeBloks,
5726
+ typeDefs
5727
+ );
5728
+ if (contentTypesFile) {
5729
+ files.push(contentTypesFile);
5730
+ }
5731
+ for (const componentResult of componentResults) {
5732
+ const componentImports = generateComponentImports(
5733
+ componentResult.content,
5734
+ componentResult.title,
5735
+ storyblokPropertyTypes,
5736
+ datasourceResults,
5737
+ componentResults,
5738
+ STORY_TYPE
5739
+ );
5740
+ files.push(createComponentFile(componentResult, typeDefs, componentImports));
5741
+ }
5742
+ return files;
5597
5743
  }
5598
- const finalTypeDef = [...typeDefs, ...imports, ...result];
5744
+ const finalTypeDef = [...typeDefs, ...imports, ...result.map((r) => r.content)];
5599
5745
  return [
5600
5746
  ...finalTypeDef
5601
5747
  ].join("\n");
@@ -5603,11 +5749,17 @@ const generateTypes = async (spaceData, options = {
5603
5749
  handleError(error);
5604
5750
  }
5605
5751
  };
5606
- const saveTypesToComponentsFile = async (space, typedefString, options) => {
5607
- const { filename = DEFAULT_COMPONENT_FILENAME, path } = options;
5752
+ const saveTypesToComponentsFile = async (space, typedefData, options) => {
5753
+ const { filename = DEFAULT_COMPONENT_FILENAME, path, separateFiles } = options;
5608
5754
  const resolvedPath = path ? resolve$1(process.cwd(), path, "types", space) : resolvePath(path, `types/${space}`);
5609
5755
  try {
5610
- await saveToFile(join(resolvedPath, `${filename}.d.ts`), typedefString);
5756
+ if (separateFiles && Array.isArray(typedefData)) {
5757
+ for (const { name, content } of typedefData) {
5758
+ await saveToFile(join(resolvedPath, `${name}.d.ts`), content);
5759
+ }
5760
+ } else if (typeof typedefData === "string") {
5761
+ await saveToFile(join(resolvedPath, `${filename}.d.ts`), typedefData);
5762
+ }
5611
5763
  } catch (error) {
5612
5764
  handleFileSystemError("write", error);
5613
5765
  }
@@ -5801,16 +5953,18 @@ generateCmd.action(async (options, command) => {
5801
5953
  try {
5802
5954
  spinner.start(`Generating types...`);
5803
5955
  const componentsData = await readComponentsFiles({
5804
- ...options,
5805
5956
  from: space,
5806
- path
5957
+ path,
5958
+ suffix: options.suffix,
5959
+ verbose
5807
5960
  });
5808
5961
  let dataSourceData;
5809
5962
  try {
5810
5963
  dataSourceData = await readDatasourcesFiles({
5811
- ...options,
5812
5964
  from: space,
5813
- path
5965
+ path,
5966
+ suffix: options.suffix,
5967
+ verbose
5814
5968
  });
5815
5969
  } catch (error) {
5816
5970
  if (error instanceof FileSystemError && error.errorId === "file_not_found") {
@@ -5826,17 +5980,21 @@ generateCmd.action(async (options, command) => {
5826
5980
  ...componentsData,
5827
5981
  ...dataSourceData
5828
5982
  };
5829
- const typedefString = await generateTypes(spaceDataWithComponentsAndDatasources, {
5983
+ const typedefData = await generateTypes(spaceDataWithComponentsAndDatasources, {
5830
5984
  ...options,
5831
5985
  path
5832
5986
  });
5833
- if (typedefString) {
5834
- await saveTypesToComponentsFile(space, typedefString, {
5987
+ if (typedefData) {
5988
+ await saveTypesToComponentsFile(space, typedefData, {
5835
5989
  filename: options.filename,
5836
- path
5990
+ path,
5991
+ separateFiles: options.separateFiles
5837
5992
  });
5838
5993
  }
5839
5994
  spinner.succeed();
5995
+ if (options.separateFiles && options.filename) {
5996
+ konsola.warn(`The --filename option is ignored when using --separate-files`);
5997
+ }
5840
5998
  konsola.ok(`Successfully generated types for space ${space}`, true);
5841
5999
  konsola.br();
5842
6000
  } catch (error) {