@vertesia/build-tools 1.0.0-dev.20260225.024852Z → 1.0.0-dev.20260227.104700Z

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.
Files changed (42) hide show
  1. package/lib/build-tools.js +223 -1
  2. package/lib/build-tools.js.map +1 -1
  3. package/lib/cjs/index.js +4 -1
  4. package/lib/cjs/index.js.map +1 -1
  5. package/lib/cjs/presets/index.js +6 -1
  6. package/lib/cjs/presets/index.js.map +1 -1
  7. package/lib/cjs/presets/template-collection.js +80 -0
  8. package/lib/cjs/presets/template-collection.js.map +1 -0
  9. package/lib/cjs/presets/template.js +105 -0
  10. package/lib/cjs/presets/template.js.map +1 -0
  11. package/lib/cjs/utils/template-asset-discovery.js +63 -0
  12. package/lib/cjs/utils/template-asset-discovery.js.map +1 -0
  13. package/lib/esm/index.js +1 -1
  14. package/lib/esm/index.js.map +1 -1
  15. package/lib/esm/presets/index.js +2 -0
  16. package/lib/esm/presets/index.js.map +1 -1
  17. package/lib/esm/presets/template-collection.js +74 -0
  18. package/lib/esm/presets/template-collection.js.map +1 -0
  19. package/lib/esm/presets/template.js +99 -0
  20. package/lib/esm/presets/template.js.map +1 -0
  21. package/lib/esm/utils/template-asset-discovery.js +57 -0
  22. package/lib/esm/utils/template-asset-discovery.js.map +1 -0
  23. package/lib/types/index.d.ts +1 -1
  24. package/lib/types/index.d.ts.map +1 -1
  25. package/lib/types/presets/index.d.ts +2 -0
  26. package/lib/types/presets/index.d.ts.map +1 -1
  27. package/lib/types/presets/skill.d.ts +12 -12
  28. package/lib/types/presets/template-collection.d.ts +25 -0
  29. package/lib/types/presets/template-collection.d.ts.map +1 -0
  30. package/lib/types/presets/template.d.ts +58 -0
  31. package/lib/types/presets/template.d.ts.map +1 -0
  32. package/lib/types/types.d.ts +1 -1
  33. package/lib/types/types.d.ts.map +1 -1
  34. package/lib/types/utils/template-asset-discovery.d.ts +23 -0
  35. package/lib/types/utils/template-asset-discovery.d.ts.map +1 -0
  36. package/package.json +5 -5
  37. package/src/index.ts +4 -0
  38. package/src/presets/index.ts +2 -0
  39. package/src/presets/template-collection.ts +83 -0
  40. package/src/presets/template.ts +116 -0
  41. package/src/types.ts +1 -1
  42. package/src/utils/template-asset-discovery.ts +77 -0
@@ -717,6 +717,228 @@ const skillCollectionTransformer = {
717
717
  }
718
718
  };
719
719
 
720
+ /**
721
+ * Utilities for discovering asset files in template directories
722
+ */
723
+ /**
724
+ * Files to exclude from template asset discovery
725
+ * (source files and the template definition itself)
726
+ */
727
+ const EXCLUDED_PATTERNS = [
728
+ /^TEMPLATE\.md$/,
729
+ /\.ts$/,
730
+ /\.js$/,
731
+ ];
732
+ function isExcluded(fileName) {
733
+ return EXCLUDED_PATTERNS.some(p => p.test(fileName));
734
+ }
735
+ /**
736
+ * Discover asset files in a template directory.
737
+ * All files except TEMPLATE.md, *.ts, and *.js are considered assets.
738
+ *
739
+ * @param templateFilePath - Absolute path to the TEMPLATE.md file
740
+ * @param templatePath - The template path segment (e.g., "examples/report")
741
+ * @returns Discovered assets and metadata
742
+ */
743
+ function discoverTemplateAssets(templateFilePath, templatePath) {
744
+ const templateDir = path.dirname(templateFilePath);
745
+ const fileNames = [];
746
+ const assetFiles = [];
747
+ let files;
748
+ try {
749
+ files = readdirSync(templateDir).filter(file => {
750
+ try {
751
+ return statSync(path.join(templateDir, file)).isFile();
752
+ }
753
+ catch {
754
+ return false;
755
+ }
756
+ });
757
+ }
758
+ catch {
759
+ files = [];
760
+ }
761
+ for (const file of files) {
762
+ if (isExcluded(file)) {
763
+ continue;
764
+ }
765
+ fileNames.push(file);
766
+ assetFiles.push({
767
+ sourcePath: path.join(templateDir, file),
768
+ destPath: path.join('templates', templatePath, file),
769
+ type: 'template',
770
+ });
771
+ }
772
+ return { fileNames, assetFiles };
773
+ }
774
+
775
+ /**
776
+ * Template transformer preset for markdown files with frontmatter
777
+ */
778
+ /**
779
+ * Zod schema for template frontmatter validation.
780
+ * Only includes fields authored by the user.
781
+ * The name and id are inferred from the directory structure.
782
+ */
783
+ const TemplateFrontmatterSchema = z.object({
784
+ title: z.string().optional(),
785
+ description: z.string().min(1, 'Template description is required'),
786
+ tags: z.array(z.string()).optional(),
787
+ type: z.enum(['presentation', 'document']),
788
+ }).strict();
789
+ /**
790
+ * MUST be kept in sync with @vertesia/tools-sdk RenderingTemplateDefinition
791
+ * Zod schema for template definition
792
+ */
793
+ const RenderingTemplateDefinitionSchema = z.object({
794
+ id: z.string().min(1, 'Template id is required'),
795
+ name: z.string().min(1, 'Template name is required'),
796
+ title: z.string().optional(),
797
+ description: z.string().min(1, 'Template description is required'),
798
+ instructions: z.string(),
799
+ tags: z.array(z.string()).optional(),
800
+ type: z.enum(['presentation', 'document']),
801
+ assets: z.array(z.string()),
802
+ }).passthrough();
803
+ /**
804
+ * Derive the template path segments from the file path.
805
+ *
806
+ * Example: .../templates/examples/report/TEMPLATE.md
807
+ * → category: "examples", name: "report", relative: "examples/report"
808
+ */
809
+ function deriveTemplatePathInfo(filePath) {
810
+ const templateDir = path.dirname(filePath);
811
+ const templateName = path.basename(templateDir);
812
+ const collectionDir = path.dirname(templateDir);
813
+ const category = path.basename(collectionDir);
814
+ return { category, templateName, relative: `${category}/${templateName}` };
815
+ }
816
+ /**
817
+ * Template transformer preset
818
+ * Transforms markdown files with ?template suffix OR TEMPLATE.md files into template definition objects
819
+ *
820
+ * Matches:
821
+ * - Files with ?template suffix: ./my-template.md?template
822
+ * - TEMPLATE.md files: ./my-template/TEMPLATE.md
823
+ *
824
+ * @example
825
+ * ```typescript
826
+ * import template1 from './my-template.md?template';
827
+ * import template2 from './my-template/TEMPLATE.md';
828
+ * // Both are RenderingTemplateDefinition objects
829
+ * ```
830
+ */
831
+ const templateTransformer = {
832
+ pattern: /(\.md\?template$|\/TEMPLATE\.md$)/,
833
+ schema: RenderingTemplateDefinitionSchema,
834
+ transform: (content, filePath) => {
835
+ const { frontmatter, content: markdown } = parseFrontmatter(content);
836
+ // Validate frontmatter
837
+ const frontmatterValidation = TemplateFrontmatterSchema.safeParse(frontmatter);
838
+ if (!frontmatterValidation.success) {
839
+ const errors = frontmatterValidation.error.errors
840
+ .map((err) => {
841
+ const pathStr = err.path.length > 0 ? err.path.join('.') : 'frontmatter';
842
+ return ` - ${pathStr}: ${err.message}`;
843
+ })
844
+ .join('\n');
845
+ throw new Error(`Invalid frontmatter in ${filePath}:\n${errors}`);
846
+ }
847
+ // Derive template path from directory structure
848
+ const { category, templateName, relative: templatePath } = deriveTemplatePathInfo(filePath);
849
+ // Discover asset files in the template directory
850
+ const assets = discoverTemplateAssets(filePath, templatePath);
851
+ // Build template definition
852
+ // Assets use absolute paths for direct server-side resolution
853
+ const templateData = {
854
+ id: `${category}:${templateName}`,
855
+ name: templateName,
856
+ title: frontmatter.title,
857
+ description: frontmatter.description,
858
+ instructions: markdown,
859
+ tags: frontmatter.tags,
860
+ type: frontmatter.type,
861
+ assets: assets.fileNames.map(f => `/templates/${templatePath}/${f}`),
862
+ };
863
+ return {
864
+ data: templateData,
865
+ assets: assets.assetFiles,
866
+ };
867
+ }
868
+ };
869
+
870
+ /**
871
+ * Template collection transformer for directory-based template imports
872
+ * Scans a directory for subdirectories containing TEMPLATE.md files
873
+ */
874
+ /**
875
+ * Template collection transformer preset
876
+ * Transforms directory imports with ?templates suffix into an array of template imports
877
+ *
878
+ * Matches:
879
+ * - ./all?templates (recommended - generates all.js in the directory)
880
+ * - Any path ending with a filename and ?templates
881
+ *
882
+ * NOTE: A filename before ?templates is REQUIRED to avoid naming conflicts.
883
+ * The filename becomes the output module name.
884
+ *
885
+ * @example
886
+ * ```typescript
887
+ * import templates from './all?templates';
888
+ * // Scans current directory for subdirectories with TEMPLATE.md
889
+ * // Generates all.js containing array of all templates
890
+ * ```
891
+ */
892
+ const templateCollectionTransformer = {
893
+ pattern: /\/[^/?]+\?templates$/,
894
+ virtual: true,
895
+ transform: (_content, filePath) => {
896
+ // Remove ?templates suffix and the filename to get directory path
897
+ const pathWithoutQuery = filePath.replace(/\?templates$/, '');
898
+ const dirPath = path.dirname(pathWithoutQuery);
899
+ if (!existsSync(dirPath)) {
900
+ throw new Error(`Directory not found: ${dirPath}`);
901
+ }
902
+ if (!statSync(dirPath).isDirectory()) {
903
+ throw new Error(`Not a directory: ${dirPath}`);
904
+ }
905
+ // Scan for subdirectories containing TEMPLATE.md
906
+ const entries = readdirSync(dirPath);
907
+ const imports = [];
908
+ const names = [];
909
+ for (const entry of entries) {
910
+ const entryPath = path.join(dirPath, entry);
911
+ try {
912
+ if (statSync(entryPath).isDirectory()) {
913
+ const templateFile = path.join(entryPath, 'TEMPLATE.md');
914
+ if (existsSync(templateFile)) {
915
+ const identifier = `Template_${entry.replace(/[^a-zA-Z0-9_]/g, '_')}`;
916
+ imports.push(`import ${identifier} from './${entry}/TEMPLATE.md';`);
917
+ names.push(identifier);
918
+ }
919
+ }
920
+ }
921
+ catch (_err) {
922
+ // Skip entries that can't be read
923
+ continue;
924
+ }
925
+ }
926
+ if (names.length === 0) {
927
+ console.warn(`No TEMPLATE.md files found in subdirectories of ${dirPath}`);
928
+ }
929
+ // Generate code that imports all templates and exports as array
930
+ const code = [
931
+ ...imports,
932
+ '',
933
+ `export default [${names.join(', ')}];`
934
+ ].join('\n');
935
+ return {
936
+ data: null,
937
+ code
938
+ };
939
+ }
940
+ };
941
+
720
942
  /**
721
943
  * Raw transformer preset for importing file content as strings
722
944
  */
@@ -1794,5 +2016,5 @@ const promptTransformer = {
1794
2016
  }
1795
2017
  };
1796
2018
 
1797
- export { PromptDefinitionSchema, PromptRole, SkillDefinitionSchema, SkillPropertiesSchema, TemplateType, parseFrontmatter, promptTransformer, rawTransformer, skillCollectionTransformer, skillTransformer, vertesiaImportPlugin };
2019
+ export { PromptDefinitionSchema, PromptRole, RenderingTemplateDefinitionSchema, SkillDefinitionSchema, SkillPropertiesSchema, TemplateType, parseFrontmatter, promptTransformer, rawTransformer, skillCollectionTransformer, skillTransformer, templateCollectionTransformer, templateTransformer, vertesiaImportPlugin };
1798
2020
  //# sourceMappingURL=build-tools.js.map