@fluffjs/cli 0.0.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/BabelHelpers.d.ts +26 -0
  2. package/BabelHelpers.js +65 -0
  3. package/Cli.d.ts +5 -10
  4. package/Cli.js +123 -52
  5. package/CodeGenerator.d.ts +53 -39
  6. package/CodeGenerator.js +330 -725
  7. package/ComponentCompiler.d.ts +14 -16
  8. package/ComponentCompiler.js +187 -256
  9. package/DomPreProcessor.d.ts +36 -0
  10. package/DomPreProcessor.js +645 -0
  11. package/ErrorHelpers.d.ts +5 -0
  12. package/ErrorHelpers.js +8 -0
  13. package/ExpressionTransformer.d.ts +38 -28
  14. package/ExpressionTransformer.js +558 -230
  15. package/Generator.d.ts +1 -5
  16. package/Generator.js +128 -67
  17. package/GetterDependencyExtractor.d.ts +4 -0
  18. package/GetterDependencyExtractor.js +73 -0
  19. package/IndexHtmlTransformer.d.ts +6 -7
  20. package/IndexHtmlTransformer.js +82 -88
  21. package/Parse5Helpers.d.ts +16 -0
  22. package/Parse5Helpers.js +81 -0
  23. package/TemplateParser.d.ts +39 -21
  24. package/TemplateParser.js +462 -268
  25. package/Typeguards.d.ts +24 -0
  26. package/Typeguards.js +30 -0
  27. package/babel-plugin-class-transform.d.ts +3 -18
  28. package/babel-plugin-class-transform.js +3 -11
  29. package/babel-plugin-component.d.ts +4 -13
  30. package/babel-plugin-component.js +7 -0
  31. package/babel-plugin-imports.d.ts +3 -11
  32. package/babel-plugin-imports.js +5 -31
  33. package/babel-plugin-reactive.d.ts +2 -19
  34. package/babel-plugin-reactive.js +21 -76
  35. package/bin.js +2 -2
  36. package/fluff-esbuild-plugin.d.ts +2 -5
  37. package/fluff-esbuild-plugin.js +4 -1
  38. package/index.d.ts +6 -2
  39. package/index.js +1 -1
  40. package/interfaces/BabelPluginClassTransformState.d.ts +5 -0
  41. package/interfaces/BabelPluginComponentState.d.ts +4 -0
  42. package/interfaces/BabelPluginComponentState.js +1 -0
  43. package/interfaces/BabelPluginImportsState.d.ts +5 -0
  44. package/interfaces/BabelPluginImportsState.js +1 -0
  45. package/interfaces/BabelPluginReactiveState.d.ts +13 -0
  46. package/interfaces/BabelPluginReactiveState.js +1 -0
  47. package/interfaces/BabelPluginReactiveWatchCallInfo.d.ts +7 -0
  48. package/interfaces/BabelPluginReactiveWatchCallInfo.js +1 -0
  49. package/interfaces/BabelPluginReactiveWatchInfo.d.ts +5 -0
  50. package/interfaces/BabelPluginReactiveWatchInfo.js +1 -0
  51. package/interfaces/BabelToken.d.ts +8 -0
  52. package/interfaces/BabelToken.js +1 -0
  53. package/interfaces/BindingInfo.d.ts +12 -0
  54. package/interfaces/BindingInfo.js +1 -0
  55. package/interfaces/BreakMarkerConfig.d.ts +4 -0
  56. package/interfaces/BreakMarkerConfig.js +1 -0
  57. package/interfaces/BreakNode.d.ts +4 -0
  58. package/interfaces/BreakNode.js +1 -0
  59. package/interfaces/BundleOptions.d.ts +8 -0
  60. package/interfaces/BundleOptions.js +1 -0
  61. package/interfaces/ClassTransformOptions.d.ts +10 -0
  62. package/interfaces/ClassTransformOptions.js +1 -0
  63. package/interfaces/CliOptions.d.ts +6 -0
  64. package/interfaces/CliOptions.js +1 -0
  65. package/interfaces/CommentNode.d.ts +5 -0
  66. package/interfaces/CommentNode.js +1 -0
  67. package/interfaces/CompileResult.d.ts +6 -0
  68. package/interfaces/CompileResult.js +1 -0
  69. package/interfaces/CompilerOptions.d.ts +6 -0
  70. package/interfaces/CompilerOptions.js +1 -0
  71. package/interfaces/ComponentInfo.d.ts +8 -0
  72. package/interfaces/ComponentInfo.js +1 -0
  73. package/interfaces/ComponentMetadata.d.ts +9 -0
  74. package/interfaces/ComponentMetadata.js +1 -0
  75. package/interfaces/ControlFlow.d.ts +19 -0
  76. package/interfaces/ControlFlow.js +1 -0
  77. package/interfaces/ControlFlowNode.d.ts +6 -0
  78. package/interfaces/ControlFlowNode.js +1 -0
  79. package/interfaces/ControlFlowParseResult.d.ts +10 -0
  80. package/interfaces/ControlFlowParseResult.js +1 -0
  81. package/interfaces/ElementNode.d.ts +11 -0
  82. package/interfaces/ElementNode.js +1 -0
  83. package/interfaces/FluffConfigInterface.d.ts +7 -0
  84. package/interfaces/FluffConfigInterface.js +1 -0
  85. package/interfaces/FluffPluginOptions.d.ts +9 -0
  86. package/interfaces/FluffPluginOptions.js +1 -0
  87. package/interfaces/FluffTarget.d.ts +15 -0
  88. package/interfaces/FluffTarget.js +1 -0
  89. package/interfaces/ForMarkerConfig.d.ts +9 -0
  90. package/interfaces/ForMarkerConfig.js +1 -0
  91. package/interfaces/ForNode.d.ts +13 -0
  92. package/interfaces/ForNode.js +1 -0
  93. package/interfaces/GeneratorOptions.d.ts +5 -0
  94. package/interfaces/GeneratorOptions.js +1 -0
  95. package/interfaces/HtmlTransformOptions.d.ts +9 -0
  96. package/interfaces/HtmlTransformOptions.js +1 -0
  97. package/interfaces/IfBranch.d.ts +8 -0
  98. package/interfaces/IfBranch.js +1 -0
  99. package/interfaces/IfMarkerConfig.d.ts +8 -0
  100. package/interfaces/IfMarkerConfig.js +1 -0
  101. package/interfaces/IfNode.d.ts +7 -0
  102. package/interfaces/IfNode.js +1 -0
  103. package/interfaces/ImportTransformOptions.d.ts +7 -0
  104. package/interfaces/ImportTransformOptions.js +1 -0
  105. package/interfaces/InterpolationNode.d.ts +12 -0
  106. package/interfaces/InterpolationNode.js +1 -0
  107. package/interfaces/ParsedTemplate.d.ts +6 -0
  108. package/interfaces/ParsedTemplate.js +1 -0
  109. package/interfaces/ParsedTemplateOld.d.ts +9 -0
  110. package/interfaces/ParsedTemplateOld.js +1 -0
  111. package/interfaces/PropertyChain.d.ts +2 -0
  112. package/interfaces/PropertyChain.js +1 -0
  113. package/interfaces/Scope.d.ts +5 -0
  114. package/interfaces/Scope.js +1 -0
  115. package/interfaces/ServeOptions.d.ts +5 -0
  116. package/interfaces/ServeOptions.js +1 -0
  117. package/interfaces/SwitchCase.d.ts +8 -0
  118. package/interfaces/SwitchCase.js +1 -0
  119. package/interfaces/SwitchMarkerConfig.d.ts +11 -0
  120. package/interfaces/SwitchMarkerConfig.js +1 -0
  121. package/interfaces/SwitchNode.d.ts +10 -0
  122. package/interfaces/SwitchNode.js +1 -0
  123. package/interfaces/TemplateBinding.d.ts +10 -0
  124. package/interfaces/TemplateBinding.js +1 -0
  125. package/interfaces/TemplateNode.d.ts +7 -0
  126. package/interfaces/TemplateNode.js +1 -0
  127. package/interfaces/TextMarkerConfig.d.ts +10 -0
  128. package/interfaces/TextMarkerConfig.js +1 -0
  129. package/interfaces/TextNode.d.ts +5 -0
  130. package/interfaces/TextNode.js +1 -0
  131. package/interfaces/TokenizeResult.d.ts +6 -0
  132. package/interfaces/TokenizeResult.js +1 -0
  133. package/interfaces/TransformOptions.d.ts +11 -0
  134. package/interfaces/TransformOptions.js +1 -0
  135. package/interfaces/index.d.ts +34 -0
  136. package/interfaces/index.js +1 -0
  137. package/package.json +9 -1
  138. package/types/FluffConfig.d.ts +5 -27
  139. package/ControlFlowParser.d.ts +0 -55
  140. package/ControlFlowParser.js +0 -279
  141. package/types.d.ts +0 -46
  142. /package/{types.js → interfaces/BabelPluginClassTransformState.js} +0 -0
@@ -1,28 +1,26 @@
1
1
  import type { ClassTransformOptions } from './babel-plugin-class-transform.js';
2
2
  import type { ComponentMetadata } from './babel-plugin-component.js';
3
- export interface CompileResult {
4
- code: string;
5
- map?: string;
6
- watchFiles?: string[];
7
- }
3
+ import type { CompileResult } from './interfaces/CompileResult.js';
4
+ import { TemplateParser } from './TemplateParser.js';
5
+ export type { CompileResult } from './interfaces/CompileResult.js';
8
6
  export declare class ComponentCompiler {
9
7
  private readonly componentSelectors;
10
- private readonly parser;
11
- private readonly generator;
12
- constructor();
13
- registerComponent(selector: string): void;
14
- discoverComponents(demoDir: string): Promise<void>;
15
- compileComponent(filePath: string): Promise<string>;
16
- compileComponentForBundle(filePath: string, minify?: boolean, sourcemap?: boolean): Promise<CompileResult>;
8
+ private getReactivePropsForFile;
9
+ protected createTemplateParser(_filePath: string): TemplateParser;
10
+ private runBabelTransform;
11
+ discoverComponents(dir: string): Promise<void>;
12
+ compileComponentForBundle(filePath: string, minify?: boolean, sourcemap?: boolean, skipDefine?: boolean, production?: boolean): Promise<CompileResult>;
17
13
  stripTypeScriptWithSourceMap(code: string, filePath: string, sourcemap?: boolean): Promise<CompileResult>;
18
- private createComponentSourceMap;
19
14
  transformImportsForBundle(code: string, filePath: string): Promise<string>;
20
- transformReactiveProperties(code: string, filePath?: string): Promise<string>;
15
+ transformReactiveProperties(code: string, filePath?: string, production?: boolean): Promise<string>;
21
16
  stripTypeScript(code: string, filePath?: string): Promise<string>;
22
17
  extractComponentMetadata(code: string, filePath: string): Promise<ComponentMetadata | null>;
23
- transformImportsAndDecorators(code: string, filePath: string): Promise<string>;
24
18
  transformClass(code: string, filePath: string, options: ClassTransformOptions): Promise<string>;
25
19
  transformLibraryImports(code: string, filePath: string): Promise<string>;
26
- copyLighterLib(srcDir: string, distDir: string): Promise<void>;
20
+ private createComponentSourceMap;
21
+ private addFluffImport;
22
+ private appendCode;
23
+ private addBindingsMap;
24
+ private addCustomElementsDefine;
27
25
  }
28
26
  //# sourceMappingURL=ComponentCompiler.d.ts.map
@@ -1,105 +1,86 @@
1
1
  import * as babel from '@babel/core';
2
+ import { parse } from '@babel/parser';
3
+ import * as t from '@babel/types';
2
4
  import * as esbuild from 'esbuild';
3
5
  import * as fs from 'fs';
4
6
  import { minify as minifyHtml } from 'html-minifier-terser';
5
7
  import * as path from 'path';
6
8
  import { SourceMapConsumer, SourceMapGenerator } from 'source-map';
7
- import classTransformPlugin, { injectMethodBodies } from './babel-plugin-class-transform.js';
9
+ import classTransformPlugin from './babel-plugin-class-transform.js';
8
10
  import componentPlugin, { componentMetadataMap } from './babel-plugin-component.js';
9
11
  import importsPlugin from './babel-plugin-imports.js';
10
12
  import reactivePlugin, { reactivePropertiesMap } from './babel-plugin-reactive.js';
13
+ import { generate } from './BabelHelpers.js';
11
14
  import { CodeGenerator } from './CodeGenerator.js';
15
+ import { ErrorHelpers } from './ErrorHelpers.js';
16
+ import { GetterDependencyExtractor } from './GetterDependencyExtractor.js';
12
17
  import { TemplateParser } from './TemplateParser.js';
13
18
  export class ComponentCompiler {
14
19
  componentSelectors = new Set();
15
- parser;
16
- generator;
17
- constructor() {
18
- this.parser = new TemplateParser();
19
- this.generator = new CodeGenerator();
20
- }
21
- registerComponent(selector) {
22
- this.componentSelectors.add(selector);
23
- }
24
- async discoverComponents(demoDir) {
25
- const files = fs.readdirSync(demoDir)
26
- .filter(f => f.endsWith('.component.ts'));
27
- for (const file of files) {
28
- const filePath = path.join(demoDir, file);
29
- const content = fs.readFileSync(filePath, 'utf-8');
30
- const metadata = await this.extractComponentMetadata(content, filePath);
31
- if (metadata?.selector) {
32
- this.componentSelectors.add(metadata.selector);
20
+ getReactivePropsForFile(filePath) {
21
+ const direct = reactivePropertiesMap.get(filePath);
22
+ if (direct) {
23
+ return direct;
24
+ }
25
+ for (const [key, value] of reactivePropertiesMap.entries()) {
26
+ if (key === filePath || key.endsWith(filePath) || filePath.endsWith(key)) {
27
+ return value;
33
28
  }
34
29
  }
30
+ return new Set();
35
31
  }
36
- async compileComponent(filePath) {
37
- let source = fs.readFileSync(filePath, 'utf-8');
38
- const componentDir = path.dirname(filePath);
39
- if (source.includes('@Reactive') || source.includes('@Input')) {
40
- source = await this.transformReactiveProperties(source, filePath);
41
- const reactiveProps = reactivePropertiesMap.get(filePath);
42
- if (reactiveProps) {
43
- this.generator.setReactiveProperties(reactiveProps);
44
- }
45
- }
46
- else {
47
- this.generator.setReactiveProperties(new Set());
48
- }
49
- const metadata = await this.extractComponentMetadata(source, filePath);
50
- if (!metadata) {
51
- return source;
52
- }
53
- const { selector, templateUrl, template, styleUrl, styles: inlineStyles, className } = metadata;
54
- let templateHtml = '';
55
- if (templateUrl) {
56
- const templatePath = path.resolve(componentDir, templateUrl);
57
- templateHtml = fs.readFileSync(templatePath, 'utf-8');
58
- }
59
- else if (template) {
60
- templateHtml = template;
32
+ createTemplateParser(_filePath) {
33
+ return new TemplateParser();
34
+ }
35
+ async runBabelTransform(code, filePath, options) {
36
+ try {
37
+ const presets = options.useTypeScriptPreset
38
+ ? [['@babel/preset-typescript', { isTSX: false, allExtensions: true }]]
39
+ : [];
40
+ const plugins = options.useDecoratorSyntax
41
+ ? [['@babel/plugin-syntax-decorators', { version: '2023-11' }], ...options.plugins]
42
+ : options.plugins;
43
+ const parserOpts = options.useDecoratorSyntax
44
+ ? { plugins: ['typescript', 'decorators'] }
45
+ : options.useTypeScriptPreset
46
+ ? { plugins: ['typescript'] }
47
+ : undefined;
48
+ const result = await babel.transformAsync(code, {
49
+ filename: filePath,
50
+ presets,
51
+ plugins,
52
+ parserOpts
53
+ });
54
+ return result?.code ?? code;
61
55
  }
62
- else {
63
- return source;
56
+ catch (e) {
57
+ console.error(`${options.errorContext} in ${filePath}:`, ErrorHelpers.getErrorMessage(e));
58
+ return code;
64
59
  }
65
- let styles = '';
66
- if (styleUrl) {
67
- const stylePath = path.resolve(componentDir, styleUrl);
68
- if (fs.existsSync(stylePath)) {
69
- styles = fs.readFileSync(stylePath, 'utf-8');
60
+ }
61
+ async discoverComponents(dir) {
62
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
63
+ for (const entry of entries) {
64
+ const fullPath = path.join(dir, entry.name);
65
+ if (entry.isDirectory()) {
66
+ await this.discoverComponents(fullPath);
67
+ }
68
+ else if (entry.name.endsWith('.component.ts')) {
69
+ const content = fs.readFileSync(fullPath, 'utf-8');
70
+ const metadata = await this.extractComponentMetadata(content, fullPath);
71
+ if (metadata?.selector) {
72
+ this.componentSelectors.add(metadata.selector);
73
+ }
70
74
  }
71
75
  }
72
- else if (inlineStyles) {
73
- styles = inlineStyles;
74
- }
75
- const { html, bindings, controlFlows, templateRefs } = this.parser.parse(templateHtml);
76
- this.generator.setTemplateRefs(templateRefs);
77
- const renderMethod = this.generator.generateRenderMethod(html, styles);
78
- const bindingsSetup = this.generator.generateBindingsSetup(bindings, controlFlows);
79
- let result = await this.transformImportsAndDecorators(source, filePath);
80
- result = await this.transformClass(result, filePath, {
81
- className, originalSuperClass: 'HTMLElement', newSuperClass: 'FluffElement', injectMethods: [
82
- { name: '__render', body: renderMethod }, { name: '__setupBindings', body: bindingsSetup }
83
- ]
84
- });
85
- result = 'import { FluffElement } from \'./fluff-lib/runtime/FluffElement.js\';\n' + result;
86
- result += `\ncustomElements.define('${selector}', ${className});\n`;
87
- result = await this.stripTypeScript(result, filePath);
88
- return result;
89
76
  }
90
- async compileComponentForBundle(filePath, minify, sourcemap) {
77
+ async compileComponentForBundle(filePath, minify, sourcemap, skipDefine, production) {
91
78
  let source = fs.readFileSync(filePath, 'utf-8');
92
79
  const componentDir = path.dirname(filePath);
93
- const generator = new CodeGenerator();
80
+ const parser = this.createTemplateParser(filePath);
81
+ reactivePropertiesMap.delete(filePath);
94
82
  if (source.includes('@Reactive') || source.includes('@Input')) {
95
- source = await this.transformReactiveProperties(source, filePath);
96
- const reactiveProps = reactivePropertiesMap.get(filePath);
97
- if (reactiveProps) {
98
- generator.setReactiveProperties(reactiveProps);
99
- }
100
- }
101
- else {
102
- generator.setReactiveProperties(new Set());
83
+ source = await this.transformReactiveProperties(source, filePath, production);
103
84
  }
104
85
  const metadata = await this.extractComponentMetadata(source, filePath);
105
86
  if (!metadata) {
@@ -126,32 +107,48 @@ export class ComponentCompiler {
126
107
  else if (inlineStyles) {
127
108
  styles = inlineStyles;
128
109
  }
110
+ if (minify && styles) {
111
+ const cssResult = await esbuild.transform(styles, {
112
+ loader: 'css', minify: true
113
+ });
114
+ styles = cssResult.code;
115
+ }
116
+ const reactiveProps = this.getReactivePropsForFile(filePath);
117
+ const getterDepMap = GetterDependencyExtractor.extractGetterDependencyMap(source, reactiveProps);
118
+ parser.setGetterDependencyMap(getterDepMap);
119
+ const parsed = await parser.parse(templateHtml);
120
+ parser.setGetterDependencyMap(new Map());
121
+ const gen = new CodeGenerator(this.componentSelectors, selector);
122
+ let generatedHtml = gen.generateHtml(parsed);
129
123
  if (minify) {
130
- templateHtml = await minifyHtml(templateHtml, {
124
+ generatedHtml = await minifyHtml(generatedHtml, {
131
125
  collapseWhitespace: true,
132
126
  removeComments: true,
133
127
  removeRedundantAttributes: true,
134
128
  removeEmptyAttributes: true
135
129
  });
136
- if (styles) {
137
- const cssResult = await esbuild.transform(styles, {
138
- loader: 'css', minify: true
139
- });
140
- styles = cssResult.code;
141
- }
142
130
  }
143
- const { html, bindings, controlFlows, templateRefs } = this.parser.parse(templateHtml);
144
- generator.setTemplateRefs(templateRefs);
145
- const renderMethod = generator.generateRenderMethod(html, styles);
146
- const bindingsSetup = generator.generateBindingsSetup(bindings, controlFlows);
131
+ const markerConfigJson = gen.getMarkerConfigJson();
132
+ const renderMethod = gen.generateRenderMethodFromHtml(generatedHtml, styles, markerConfigJson);
133
+ const bindingsSetup = gen.generateBindingsSetup();
147
134
  let result = await this.transformImportsForBundle(source, filePath);
148
135
  result = await this.transformClass(result, filePath, {
149
136
  className, originalSuperClass: 'HTMLElement', newSuperClass: 'FluffElement', injectMethods: [
150
137
  { name: '__render', body: renderMethod }, { name: '__setupBindings', body: bindingsSetup }
151
138
  ]
152
139
  });
153
- result = 'import { FluffElement } from \'@fluffjs/fluff\';\n' + result;
154
- result += `\ncustomElements.define('${selector}', ${className});\n`;
140
+ result = this.addFluffImport(result);
141
+ const exprAssignments = gen.generateExpressionAssignments();
142
+ if (exprAssignments) {
143
+ result = this.appendCode(result, exprAssignments);
144
+ }
145
+ const bindingsMap = gen.getBindingsMap();
146
+ if (Object.keys(bindingsMap).length > 0) {
147
+ result = this.addBindingsMap(result, className, bindingsMap);
148
+ }
149
+ if (!skipDefine) {
150
+ result = this.addCustomElementsDefine(result, selector, className);
151
+ }
155
152
  const tsResult = await this.stripTypeScriptWithSourceMap(result, filePath, sourcemap);
156
153
  const watchFiles = [];
157
154
  if (templatePath) {
@@ -178,78 +175,31 @@ export class ComponentCompiler {
178
175
  return { code: result.code, map: result.map };
179
176
  }
180
177
  catch (e) {
181
- const message = e instanceof Error ? e.message : String(e);
182
- console.error(`Error transforming ${filePath}:`, message);
178
+ console.error(`Error transforming ${filePath}:`, ErrorHelpers.getErrorMessage(e));
183
179
  return { code };
184
180
  }
185
181
  }
186
- async createComponentSourceMap(code, esbuildMap, componentPath, templatePath, stylePath) {
187
- const consumer = await new SourceMapConsumer(JSON.parse(esbuildMap));
188
- const generator = new SourceMapGenerator({
189
- file: path.basename(componentPath)
190
- .replace('.ts', '.js')
191
- });
192
- generator.setSourceContent(componentPath, fs.readFileSync(componentPath, 'utf-8'));
193
- generator.setSourceContent(templatePath, fs.readFileSync(templatePath, 'utf-8'));
194
- if (stylePath && fs.existsSync(stylePath)) {
195
- generator.setSourceContent(stylePath, fs.readFileSync(stylePath, 'utf-8'));
196
- }
197
- consumer.eachMapping(mapping => {
198
- if (mapping.source) {
199
- generator.addMapping({
200
- generated: { line: mapping.generatedLine, column: mapping.generatedColumn },
201
- original: { line: mapping.originalLine, column: mapping.originalColumn },
202
- source: mapping.source,
203
- name: mapping.name ?? undefined
204
- });
205
- }
206
- });
207
- consumer.destroy();
208
- return generator.toString();
209
- }
210
182
  async transformImportsForBundle(code, filePath) {
211
- try {
212
- const importOptions = {
213
- removeImportsFrom: ['lighter'],
214
- removeDecorators: ['Component', 'Input', 'Output'],
215
- pathReplacements: {},
216
- addJsExtension: false
217
- };
218
- const result = await babel.transformAsync(code, {
219
- filename: filePath, presets: [
220
- ['@babel/preset-typescript', { isTSX: false, allExtensions: true }]
221
- ], plugins: [
222
- ['@babel/plugin-syntax-decorators', { version: '2023-11' }], [importsPlugin, importOptions]
223
- ], parserOpts: {
224
- plugins: ['typescript', 'decorators']
225
- }
226
- });
227
- return result?.code ?? code;
228
- }
229
- catch (e) {
230
- const message = e instanceof Error ? e.message : String(e);
231
- console.error(`Failed to transform imports in ${filePath}:`, message);
232
- return code;
233
- }
183
+ const importOptions = {
184
+ removeImportsFrom: ['lighter'],
185
+ removeDecorators: ['Component', 'Input', 'Output'],
186
+ pathReplacements: {},
187
+ addJsExtension: false
188
+ };
189
+ return this.runBabelTransform(code, filePath, {
190
+ useTypeScriptPreset: true,
191
+ useDecoratorSyntax: true,
192
+ plugins: [[importsPlugin, importOptions]],
193
+ errorContext: 'Failed to transform imports'
194
+ });
234
195
  }
235
- async transformReactiveProperties(code, filePath = 'file.ts') {
236
- try {
237
- const result = await babel.transformAsync(code, {
238
- filename: filePath, presets: [
239
- ['@babel/preset-typescript', { isTSX: false, allExtensions: true }]
240
- ], plugins: [
241
- ['@babel/plugin-syntax-decorators', { version: '2023-11' }], reactivePlugin
242
- ], parserOpts: {
243
- plugins: ['typescript', 'decorators']
244
- }
245
- });
246
- return result?.code ?? code;
247
- }
248
- catch (e) {
249
- const message = e instanceof Error ? e.message : String(e);
250
- console.error(`Babel transform error in ${filePath}:`, message);
251
- return code;
252
- }
196
+ async transformReactiveProperties(code, filePath = 'file.ts', production) {
197
+ return this.runBabelTransform(code, filePath, {
198
+ useTypeScriptPreset: true,
199
+ useDecoratorSyntax: true,
200
+ plugins: [[reactivePlugin, { production: production ?? false }]],
201
+ errorContext: 'Babel transform error'
202
+ });
253
203
  }
254
204
  async stripTypeScript(code, filePath = 'file.ts') {
255
205
  try {
@@ -259,123 +209,104 @@ export class ComponentCompiler {
259
209
  return result.code;
260
210
  }
261
211
  catch (e) {
262
- const message = e instanceof Error ? e.message : String(e);
263
- console.error(`Error transforming ${filePath}:`, message);
212
+ console.error(`Error transforming ${filePath}:`, ErrorHelpers.getErrorMessage(e));
264
213
  return code;
265
214
  }
266
215
  }
267
216
  async extractComponentMetadata(code, filePath) {
268
217
  try {
269
218
  componentMetadataMap.delete(filePath);
270
- await babel.transformAsync(code, {
271
- filename: filePath, presets: [
272
- ['@babel/preset-typescript', { isTSX: false, allExtensions: true }]
273
- ], plugins: [
274
- ['@babel/plugin-syntax-decorators', { version: '2023-11' }], componentPlugin
275
- ], parserOpts: {
276
- plugins: ['typescript', 'decorators']
277
- }
219
+ await this.runBabelTransform(code, filePath, {
220
+ useTypeScriptPreset: true,
221
+ useDecoratorSyntax: true,
222
+ plugins: [componentPlugin],
223
+ errorContext: 'Failed to extract component metadata'
278
224
  });
279
225
  return componentMetadataMap.get(filePath) ?? null;
280
226
  }
281
227
  catch (e) {
282
- const message = e instanceof Error ? e.message : String(e);
283
- console.error(`Failed to extract component metadata from ${filePath}:`, message);
228
+ console.error(`Failed to extract component metadata from ${filePath}:`, ErrorHelpers.getErrorMessage(e));
284
229
  return null;
285
230
  }
286
231
  }
287
- async transformImportsAndDecorators(code, filePath) {
288
- try {
289
- const importOptions = {
290
- removeImportsFrom: ['lighter'], removeDecorators: ['Component', 'Input', 'Output'], pathReplacements: {
291
- '@/fluff-lib/': './fluff-lib/'
292
- }, addJsExtension: true
293
- };
294
- const result = await babel.transformAsync(code, {
295
- filename: filePath, presets: [
296
- ['@babel/preset-typescript', { isTSX: false, allExtensions: true }]
297
- ], plugins: [
298
- ['@babel/plugin-syntax-decorators', { version: '2023-11' }], [importsPlugin, importOptions]
299
- ], parserOpts: {
300
- plugins: ['typescript', 'decorators']
301
- }
302
- });
303
- return result?.code ?? code;
304
- }
305
- catch (e) {
306
- const message = e instanceof Error ? e.message : String(e);
307
- console.error(`Failed to transform imports in ${filePath}:`, message);
308
- return code;
309
- }
310
- }
311
232
  async transformClass(code, filePath, options) {
312
- try {
313
- const result = await babel.transformAsync(code, {
314
- filename: filePath, plugins: [
315
- [classTransformPlugin, options]
316
- ]
317
- });
318
- let transformed = result?.code ?? code;
319
- if (options.injectMethods) {
320
- transformed = injectMethodBodies(transformed, options.injectMethods);
321
- }
322
- return transformed;
323
- }
324
- catch (e) {
325
- const message = e instanceof Error ? e.message : String(e);
326
- console.error(`Failed to transform class in ${filePath}:`, message);
327
- return code;
328
- }
233
+ return this.runBabelTransform(code, filePath, {
234
+ useTypeScriptPreset: false,
235
+ useDecoratorSyntax: false,
236
+ plugins: [[classTransformPlugin, options]],
237
+ errorContext: 'Failed to transform class'
238
+ });
329
239
  }
330
240
  async transformLibraryImports(code, filePath) {
331
- try {
332
- const importOptions = {
333
- pathReplacements: {
334
- '@/fluff-lib/': '../'
335
- }, addJsExtension: true
336
- };
337
- const result = await babel.transformAsync(code, {
338
- filename: filePath, presets: [
339
- ['@babel/preset-typescript', { isTSX: false, allExtensions: true }]
340
- ], plugins: [
341
- [importsPlugin, importOptions]
342
- ], parserOpts: {
343
- plugins: ['typescript']
344
- }
345
- });
346
- return result?.code ?? code;
347
- }
348
- catch (e) {
349
- const message = e instanceof Error ? e.message : String(e);
350
- console.error(`Failed to transform library imports in ${filePath}:`, message);
351
- return code;
352
- }
241
+ const importOptions = {
242
+ pathReplacements: {
243
+ '@/fluff-lib/': '../'
244
+ }, addJsExtension: true
245
+ };
246
+ return this.runBabelTransform(code, filePath, {
247
+ useTypeScriptPreset: true,
248
+ useDecoratorSyntax: false,
249
+ plugins: [[importsPlugin, importOptions]],
250
+ errorContext: 'Failed to transform library imports'
251
+ });
353
252
  }
354
- async copyLighterLib(srcDir, distDir) {
355
- const lighterLibSrc = path.join(srcDir, 'fluff-lib');
356
- const lighterLibDist = path.join(distDir, 'fluff-lib');
357
- if (!fs.existsSync(lighterLibDist)) {
358
- fs.mkdirSync(lighterLibDist, { recursive: true });
253
+ async createComponentSourceMap(code, esbuildMap, componentPath, templatePath, stylePath) {
254
+ const consumer = await new SourceMapConsumer(JSON.parse(esbuildMap));
255
+ const generator = new SourceMapGenerator({
256
+ file: path.basename(componentPath)
257
+ .replace('.ts', '.js')
258
+ });
259
+ generator.setSourceContent(componentPath, fs.readFileSync(componentPath, 'utf-8'));
260
+ generator.setSourceContent(templatePath, fs.readFileSync(templatePath, 'utf-8'));
261
+ if (stylePath && fs.existsSync(stylePath)) {
262
+ generator.setSourceContent(stylePath, fs.readFileSync(stylePath, 'utf-8'));
359
263
  }
360
- const dirs = ['utils', 'runtime', 'interfaces', 'enums', 'decorators'];
361
- for (const dir of dirs) {
362
- const srcPath = path.join(lighterLibSrc, dir);
363
- const distPath = path.join(lighterLibDist, dir);
364
- if (fs.existsSync(srcPath)) {
365
- if (!fs.existsSync(distPath)) {
366
- fs.mkdirSync(distPath, { recursive: true });
367
- }
368
- const files = fs.readdirSync(srcPath)
369
- .filter(f => f.endsWith('.ts'));
370
- for (const file of files) {
371
- const fullPath = path.join(srcPath, file);
372
- let content = fs.readFileSync(fullPath, 'utf-8');
373
- content = await this.transformLibraryImports(content, fullPath);
374
- content = await this.stripTypeScript(content, fullPath);
375
- const outFile = file.replace('.ts', '.js');
376
- fs.writeFileSync(path.join(distPath, outFile), content);
377
- }
264
+ consumer.eachMapping(mapping => {
265
+ if (mapping.source) {
266
+ generator.addMapping({
267
+ generated: { line: mapping.generatedLine, column: mapping.generatedColumn },
268
+ original: { line: mapping.originalLine, column: mapping.originalColumn },
269
+ source: mapping.source,
270
+ name: mapping.name ?? undefined
271
+ });
378
272
  }
273
+ });
274
+ consumer.destroy();
275
+ return generator.toString();
276
+ }
277
+ addFluffImport(code) {
278
+ const ast = parse(code, { sourceType: 'module' });
279
+ const importSpecifiers = [
280
+ t.importSpecifier(t.identifier('FluffBase'), t.identifier('FluffBase')),
281
+ t.importSpecifier(t.identifier('FluffElement'), t.identifier('FluffElement')),
282
+ t.importSpecifier(t.identifier('MarkerManager'), t.identifier('MarkerManager'))
283
+ ];
284
+ const importDecl = t.importDeclaration(importSpecifiers, t.stringLiteral('@fluffjs/fluff'));
285
+ ast.program.body.unshift(importDecl);
286
+ return generate(ast, { compact: false }).code;
287
+ }
288
+ appendCode(code, additionalCode) {
289
+ const ast = parse(code, { sourceType: 'module' });
290
+ const additionalAst = parse(additionalCode, { sourceType: 'module' });
291
+ ast.program.body.push(...additionalAst.program.body);
292
+ return generate(ast, { compact: false }).code;
293
+ }
294
+ addBindingsMap(code, className, bindingsMap) {
295
+ const ast = parse(code, { sourceType: 'module' });
296
+ const jsonStr = JSON.stringify(bindingsMap);
297
+ const valueAst = parse(`(${jsonStr})`, { sourceType: 'module' });
298
+ const [valueStmt] = valueAst.program.body;
299
+ if (!t.isExpressionStatement(valueStmt)) {
300
+ return code;
379
301
  }
302
+ const assignment = t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(className), t.identifier('__bindings')), valueStmt.expression));
303
+ ast.program.body.push(assignment);
304
+ return generate(ast, { compact: false }).code;
305
+ }
306
+ addCustomElementsDefine(code, selector, className) {
307
+ const ast = parse(code, { sourceType: 'module' });
308
+ const defineCall = t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('customElements'), t.identifier('define')), [t.stringLiteral(selector), t.identifier(className)]));
309
+ ast.program.body.push(defineCall);
310
+ return generate(ast, { compact: false }).code;
380
311
  }
381
312
  }
@@ -0,0 +1,36 @@
1
+ export declare class DomPreProcessor {
2
+ private readonly stack;
3
+ private source;
4
+ private rootFragment;
5
+ private readonly elementStack;
6
+ process(html: string): Promise<string>;
7
+ private handleStartTag;
8
+ private transformAttribute;
9
+ private convertInterpolationToExpression;
10
+ private handleEndTag;
11
+ private handleText;
12
+ private appendNode;
13
+ private appendText;
14
+ private appendDoctype;
15
+ private findNextSpecial;
16
+ private parseInterpolation;
17
+ private parseControlFlow;
18
+ private parseExpressionBlockCore;
19
+ private parseSimpleBlockCore;
20
+ private parseIfStatement;
21
+ private parseElseIfStatement;
22
+ private parseElseStatement;
23
+ private parseForStatement;
24
+ private parseSwitchStatement;
25
+ private parseCaseStatement;
26
+ private parseDefaultStatement;
27
+ private parseEmptyStatement;
28
+ private parseSimpleStatement;
29
+ private parseFallthroughStatement;
30
+ private parseBreakStatement;
31
+ private parseForContent;
32
+ private skipWhitespace;
33
+ private handleComment;
34
+ private getOriginalAttributeName;
35
+ }
36
+ //# sourceMappingURL=DomPreProcessor.d.ts.map