pulse-js-framework 1.11.3 → 1.11.4
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/cli/analyze.js +21 -8
- package/cli/build.js +83 -56
- package/cli/dev.js +108 -94
- package/cli/docs-test.js +52 -33
- package/cli/index.js +81 -51
- package/cli/mobile.js +92 -40
- package/cli/release.js +64 -46
- package/cli/scaffold.js +14 -13
- package/compiler/lexer.js +55 -54
- package/compiler/parser/core.js +1 -0
- package/compiler/parser/state.js +6 -12
- package/compiler/parser/style.js +17 -20
- package/compiler/parser/view.js +1 -3
- package/compiler/preprocessor.js +124 -262
- package/compiler/sourcemap.js +10 -4
- package/compiler/transformer/expressions.js +122 -106
- package/compiler/transformer/index.js +2 -4
- package/compiler/transformer/style.js +74 -7
- package/compiler/transformer/view.js +86 -36
- package/loader/esbuild-plugin-server-components.js +209 -0
- package/loader/esbuild-plugin.js +41 -93
- package/loader/parcel-plugin.js +37 -97
- package/loader/rollup-plugin-server-components.js +30 -169
- package/loader/rollup-plugin.js +27 -78
- package/loader/shared.js +362 -0
- package/loader/swc-plugin.js +65 -82
- package/loader/vite-plugin-server-components.js +30 -171
- package/loader/vite-plugin.js +25 -10
- package/loader/webpack-loader-server-components.js +21 -134
- package/loader/webpack-loader.js +25 -80
- package/package.json +52 -12
- package/runtime/dom-selector.js +2 -1
- package/runtime/form.js +4 -3
- package/runtime/http.js +6 -1
- package/runtime/logger.js +44 -24
- package/runtime/router/utils.js +14 -7
- package/runtime/security.js +13 -1
- package/runtime/server-components/actions-server.js +23 -19
- package/runtime/server-components/error-sanitizer.js +18 -18
- package/runtime/server-components/security.js +41 -24
- package/runtime/ssr-preload.js +5 -3
- package/runtime/testing.js +759 -0
- package/runtime/utils.js +3 -2
- package/server/utils.js +15 -9
- package/sw/index.js +2 -0
- package/types/loaders.d.ts +1043 -0
- package/compiler/parser/_extract.js +0 -393
- package/loader/README.md +0 -509
package/loader/parcel-plugin.js
CHANGED
|
@@ -25,44 +25,22 @@
|
|
|
25
25
|
* import MyComponent from './MyComponent.pulse';
|
|
26
26
|
*
|
|
27
27
|
* Features:
|
|
28
|
-
* -
|
|
29
|
-
* -
|
|
30
|
-
* -
|
|
31
|
-
* -
|
|
32
|
-
* -
|
|
33
|
-
* -
|
|
28
|
+
* - Automatic .pulse file transformation
|
|
29
|
+
* - CSS extraction to Parcel's CSS pipeline
|
|
30
|
+
* - Source map generation
|
|
31
|
+
* - SASS/LESS/Stylus auto-detection and compilation
|
|
32
|
+
* - Hot Module Replacement (HMR)
|
|
33
|
+
* - Watch mode support
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import { compile } from '../compiler/index.js';
|
|
37
37
|
import {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
getStylusVersion,
|
|
45
|
-
detectPreprocessor
|
|
46
|
-
} from '../compiler/preprocessor.js';
|
|
47
|
-
import { dirname } from 'path';
|
|
48
|
-
|
|
49
|
-
// Cache for preprocessor availability checks
|
|
50
|
-
let preprocessorCache = null;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Check available preprocessors once
|
|
54
|
-
*/
|
|
55
|
-
function checkPreprocessors() {
|
|
56
|
-
if (preprocessorCache) return preprocessorCache;
|
|
57
|
-
|
|
58
|
-
preprocessorCache = {
|
|
59
|
-
sass: isSassAvailable(),
|
|
60
|
-
less: isLessAvailable(),
|
|
61
|
-
stylus: isStylusAvailable()
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
return preprocessorCache;
|
|
65
|
-
}
|
|
38
|
+
logPreprocessorAvailability,
|
|
39
|
+
extractCssFromOutput,
|
|
40
|
+
removeInlineStyles,
|
|
41
|
+
processStyles,
|
|
42
|
+
getPreprocessorOptions
|
|
43
|
+
} from './shared.js';
|
|
66
44
|
|
|
67
45
|
/**
|
|
68
46
|
* Transform function for Parcel
|
|
@@ -80,6 +58,7 @@ export async function transformPulse({ asset, logger }) {
|
|
|
80
58
|
const {
|
|
81
59
|
sourceMap = true,
|
|
82
60
|
extractCss = true,
|
|
61
|
+
quiet = false,
|
|
83
62
|
sass: sassOptions = {},
|
|
84
63
|
less: lessOptions = {},
|
|
85
64
|
stylus: stylusOptions = {},
|
|
@@ -87,27 +66,9 @@ export async function transformPulse({ asset, logger }) {
|
|
|
87
66
|
} = config || {};
|
|
88
67
|
|
|
89
68
|
// Log preprocessor availability once
|
|
90
|
-
if (!
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (available.sass) {
|
|
95
|
-
preprocessors.push(`SASS ${getSassVersion() || 'unknown'}`);
|
|
96
|
-
}
|
|
97
|
-
if (available.less) {
|
|
98
|
-
preprocessors.push(`LESS ${getLessVersion() || 'unknown'}`);
|
|
99
|
-
}
|
|
100
|
-
if (available.stylus) {
|
|
101
|
-
preprocessors.push(`Stylus ${getStylusVersion() || 'unknown'}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (preprocessors.length > 0) {
|
|
105
|
-
logger.info({
|
|
106
|
-
message: `[Pulse] Preprocessor support: ${preprocessors.join(', ')}`
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
checkPreprocessors._logged = true;
|
|
69
|
+
if (!transformPulse._logged && verbose && !quiet) {
|
|
70
|
+
logPreprocessorAvailability('Pulse', { logFn: (msg) => logger.info({ message: msg }) });
|
|
71
|
+
transformPulse._logged = true;
|
|
111
72
|
}
|
|
112
73
|
|
|
113
74
|
try {
|
|
@@ -130,48 +91,31 @@ export async function transformPulse({ asset, logger }) {
|
|
|
130
91
|
const outputMap = result.map;
|
|
131
92
|
|
|
132
93
|
// Extract CSS from compiled output
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
if (stylesMatch && extractCss) {
|
|
136
|
-
let css = stylesMatch[1];
|
|
137
|
-
|
|
138
|
-
// Check available preprocessors
|
|
139
|
-
const available = checkPreprocessors();
|
|
140
|
-
const preprocessor = detectPreprocessor(css);
|
|
141
|
-
|
|
142
|
-
// Preprocess if preprocessor detected and available
|
|
143
|
-
if (preprocessor !== 'none' && available[preprocessor]) {
|
|
144
|
-
try {
|
|
145
|
-
const preprocessorOptions = {
|
|
146
|
-
sass: sassOptions,
|
|
147
|
-
less: lessOptions,
|
|
148
|
-
stylus: stylusOptions
|
|
149
|
-
}[preprocessor];
|
|
150
|
-
|
|
151
|
-
const preprocessed = preprocessStylesSync(css, {
|
|
152
|
-
filename: filePath,
|
|
153
|
-
loadPaths: [dirname(filePath), ...(preprocessorOptions.loadPaths || [])],
|
|
154
|
-
compressed: preprocessorOptions.compressed || false,
|
|
155
|
-
preprocessor // Force detected preprocessor
|
|
156
|
-
});
|
|
94
|
+
const { css: extractedCss, found } = extractCssFromOutput(outputCode);
|
|
157
95
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
// Emit warning but continue with original CSS
|
|
168
|
-
logger.warn({
|
|
169
|
-
message: `${preprocessor.toUpperCase()} compilation warning: ${preprocessorError.message}`,
|
|
170
|
-
filePath
|
|
96
|
+
if (found && extractCss) {
|
|
97
|
+
const styleResult = processStyles(extractedCss, filePath, { sassOptions, lessOptions, stylusOptions });
|
|
98
|
+
|
|
99
|
+
// Log preprocessor usage in verbose mode
|
|
100
|
+
if (styleResult.preprocessor && styleResult.preprocessor !== 'none') {
|
|
101
|
+
const opts = getPreprocessorOptions(styleResult.preprocessor, { sassOptions, lessOptions, stylusOptions });
|
|
102
|
+
if (opts && (opts.verbose || verbose)) {
|
|
103
|
+
logger.verbose({
|
|
104
|
+
message: `[Pulse] Compiled ${styleResult.preprocessor.toUpperCase()} in ${filePath}`
|
|
171
105
|
});
|
|
172
106
|
}
|
|
173
107
|
}
|
|
174
108
|
|
|
109
|
+
if (styleResult.warning) {
|
|
110
|
+
// Emit warning but continue with original CSS
|
|
111
|
+
logger.warn({
|
|
112
|
+
message: styleResult.warning,
|
|
113
|
+
filePath
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const css = styleResult.css;
|
|
118
|
+
|
|
175
119
|
// Create a CSS asset for Parcel to process
|
|
176
120
|
// This allows Parcel's CSS pipeline to handle minification, autoprefixing, etc.
|
|
177
121
|
asset.addDependency({
|
|
@@ -187,11 +131,7 @@ export async function transformPulse({ asset, logger }) {
|
|
|
187
131
|
});
|
|
188
132
|
|
|
189
133
|
// Remove inline CSS injection from output
|
|
190
|
-
|
|
191
|
-
outputCode = outputCode.replace(
|
|
192
|
-
/\/\/ Styles[\s\S]*?\/\/ Inject styles[\s\S]*?document\.head\.appendChild\(styleEl\);/,
|
|
193
|
-
'// Styles extracted to CSS asset'
|
|
194
|
-
);
|
|
134
|
+
outputCode = removeInlineStyles(outputCode, '// Styles extracted to CSS asset');
|
|
195
135
|
}
|
|
196
136
|
|
|
197
137
|
// Set asset type and content
|
|
@@ -27,24 +27,24 @@
|
|
|
27
27
|
* @module pulse-js-framework/loader/rollup-plugin-server-components
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
import {
|
|
31
|
-
import { dirname, relative, posix } from 'path';
|
|
30
|
+
import { relative } from 'path';
|
|
32
31
|
import {
|
|
33
32
|
getComponentTypeFromSource
|
|
34
33
|
} from '../compiler/directives.js';
|
|
34
|
+
import {
|
|
35
|
+
extractImports, createImportViolationError, buildManifest, writeManifestToDiskAsync,
|
|
36
|
+
DIRECTIVE_REGEX, COMPONENT_ID_REGEX, EXPORT_CONST_REGEX, CLIENT_CHUNK_PREFIX,
|
|
37
|
+
DEFAULT_MANIFEST_PATH, DEFAULT_MANIFEST_FILENAME
|
|
38
|
+
} from './shared.js';
|
|
35
39
|
|
|
36
40
|
/**
|
|
37
41
|
* Default options for Server Components plugin
|
|
38
42
|
*/
|
|
39
43
|
const DEFAULT_OPTIONS = {
|
|
40
|
-
|
|
41
|
-
manifestPath: 'dist/.pulse-manifest.json',
|
|
42
|
-
|
|
43
|
-
// Public base path for chunk URLs (empty for relative paths)
|
|
44
|
+
manifestPath: DEFAULT_MANIFEST_PATH,
|
|
44
45
|
base: '',
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
manifestFilename: '.pulse-manifest.json'
|
|
46
|
+
manifestFilename: DEFAULT_MANIFEST_FILENAME,
|
|
47
|
+
quiet: false
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
/**
|
|
@@ -97,12 +97,12 @@ export default function pulseServerComponentsPlugin(options = {}) {
|
|
|
97
97
|
|
|
98
98
|
// Check if this module exports a Client Component
|
|
99
99
|
// Look for: __directive: "use client"
|
|
100
|
-
const directiveMatch = code.match(
|
|
100
|
+
const directiveMatch = code.match(DIRECTIVE_REGEX);
|
|
101
101
|
|
|
102
102
|
if (directiveMatch) {
|
|
103
103
|
// Extract component ID from export
|
|
104
|
-
const exportMatch = code.match(
|
|
105
|
-
const componentIdMatch = code.match(
|
|
104
|
+
const exportMatch = code.match(EXPORT_CONST_REGEX);
|
|
105
|
+
const componentIdMatch = code.match(COMPONENT_ID_REGEX);
|
|
106
106
|
|
|
107
107
|
const componentId = componentIdMatch ? componentIdMatch[1] : (exportMatch ? exportMatch[1] : null);
|
|
108
108
|
|
|
@@ -113,7 +113,9 @@ export default function pulseServerComponentsPlugin(options = {}) {
|
|
|
113
113
|
chunk: null // Will be filled in during generateBundle
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
if (!config.quiet) {
|
|
117
|
+
console.log(`[Pulse Server Components] Detected Client Component: ${componentId} (${relative(process.cwd(), id)})`);
|
|
118
|
+
}
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
121
|
|
|
@@ -164,7 +166,7 @@ export default function pulseServerComponentsPlugin(options = {}) {
|
|
|
164
166
|
for (const [componentId, info] of clientComponents.entries()) {
|
|
165
167
|
if (id === info.file) {
|
|
166
168
|
// Create a separate chunk for this Client Component
|
|
167
|
-
return
|
|
169
|
+
return `${CLIENT_CHUNK_PREFIX}${componentId}`;
|
|
168
170
|
}
|
|
169
171
|
}
|
|
170
172
|
|
|
@@ -201,36 +203,21 @@ export default function pulseServerComponentsPlugin(options = {}) {
|
|
|
201
203
|
// Check if this chunk corresponds to a Client Component
|
|
202
204
|
for (const [componentId, info] of clientComponents.entries()) {
|
|
203
205
|
// Match by chunk name or by checking if the component file is in the chunk
|
|
204
|
-
if (chunk.name ===
|
|
206
|
+
if (chunk.name === `${CLIENT_CHUNK_PREFIX}${componentId}` ||
|
|
205
207
|
(chunk.facadeModuleId && chunk.facadeModuleId === info.file)) {
|
|
206
208
|
|
|
207
209
|
// Store the chunk filename
|
|
208
210
|
info.chunk = fileName;
|
|
209
211
|
|
|
210
|
-
|
|
212
|
+
if (!config.quiet) {
|
|
213
|
+
console.log(`[Pulse Server Components] Mapped ${componentId} → ${fileName}`);
|
|
214
|
+
}
|
|
211
215
|
}
|
|
212
216
|
}
|
|
213
217
|
}
|
|
214
218
|
}
|
|
215
219
|
|
|
216
|
-
|
|
217
|
-
const manifest = {
|
|
218
|
-
version: '1.0',
|
|
219
|
-
components: {}
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
for (const [componentId, info] of clientComponents.entries()) {
|
|
223
|
-
if (info.chunk) {
|
|
224
|
-
const base = config.base || '';
|
|
225
|
-
const chunkUrl = posix.join(base, info.chunk);
|
|
226
|
-
|
|
227
|
-
manifest.components[componentId] = {
|
|
228
|
-
id: componentId,
|
|
229
|
-
chunk: chunkUrl,
|
|
230
|
-
exports: ['default', componentId]
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
}
|
|
220
|
+
const manifest = buildManifest(clientComponents, config);
|
|
234
221
|
|
|
235
222
|
// Emit manifest as JSON asset
|
|
236
223
|
const manifestJson = JSON.stringify(manifest, null, 2);
|
|
@@ -241,151 +228,25 @@ export default function pulseServerComponentsPlugin(options = {}) {
|
|
|
241
228
|
source: manifestJson
|
|
242
229
|
});
|
|
243
230
|
|
|
244
|
-
|
|
231
|
+
if (!config.quiet) {
|
|
232
|
+
console.log(`[Pulse Server Components] Generated client manifest with ${clientComponents.size} components`);
|
|
233
|
+
}
|
|
245
234
|
},
|
|
246
235
|
|
|
247
236
|
/**
|
|
248
|
-
* Write manifest to file system after build completes
|
|
237
|
+
* Write manifest to file system after build completes (async)
|
|
249
238
|
*/
|
|
250
|
-
closeBundle() {
|
|
239
|
+
async closeBundle() {
|
|
251
240
|
// Skip if no Client Components detected
|
|
252
241
|
if (clientComponents.size === 0) {
|
|
253
242
|
return;
|
|
254
243
|
}
|
|
255
244
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
version: '1.0',
|
|
259
|
-
components: {}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
for (const [componentId, info] of clientComponents.entries()) {
|
|
263
|
-
if (info.chunk) {
|
|
264
|
-
const base = config.base || '';
|
|
265
|
-
const chunkUrl = posix.join(base, info.chunk);
|
|
266
|
-
|
|
267
|
-
manifest.components[componentId] = {
|
|
268
|
-
id: componentId,
|
|
269
|
-
chunk: chunkUrl,
|
|
270
|
-
exports: ['default', componentId]
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Write to file system (in addition to emitted asset)
|
|
276
|
-
if (config.manifestPath) {
|
|
277
|
-
try {
|
|
278
|
-
const manifestDir = dirname(config.manifestPath);
|
|
279
|
-
mkdirSync(manifestDir, { recursive: true });
|
|
280
|
-
writeFileSync(config.manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');
|
|
281
|
-
console.log(`[Pulse Server Components] Manifest written to ${config.manifestPath}`);
|
|
282
|
-
} catch (error) {
|
|
283
|
-
console.warn(`[Pulse Server Components] Failed to write manifest: ${error.message}`);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
245
|
+
const manifest = buildManifest(clientComponents, config);
|
|
246
|
+
await writeManifestToDiskAsync(manifest, config);
|
|
286
247
|
}
|
|
287
248
|
};
|
|
288
249
|
}
|
|
289
250
|
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
// ============================================================================
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Extract import statements from source code
|
|
296
|
-
* @param {string} code - Source code
|
|
297
|
-
* @returns {Array<string>} Import sources
|
|
298
|
-
*/
|
|
299
|
-
function extractImports(code) {
|
|
300
|
-
const imports = [];
|
|
301
|
-
|
|
302
|
-
// Match ES6 import statements
|
|
303
|
-
const importRegex = /import\s+(?:{[^}]*}|[\w$]+|\*\s+as\s+[\w$]+)\s+from\s+['"]([^'"]+)['"]/g;
|
|
304
|
-
let match;
|
|
305
|
-
|
|
306
|
-
while ((match = importRegex.exec(code)) !== null) {
|
|
307
|
-
imports.push(match[1]);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Match dynamic imports
|
|
311
|
-
const dynamicImportRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
312
|
-
while ((match = dynamicImportRegex.exec(code)) !== null) {
|
|
313
|
-
imports.push(match[1]);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return imports;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Create import violation error message
|
|
321
|
-
* @param {string} clientPath - Client Component path
|
|
322
|
-
* @param {string} serverPath - Server Component path
|
|
323
|
-
* @param {string} importSource - Import statement source
|
|
324
|
-
* @returns {string} Error message
|
|
325
|
-
*/
|
|
326
|
-
function createImportViolationError(clientPath, serverPath, importSource) {
|
|
327
|
-
const clientRelative = relative(process.cwd(), clientPath);
|
|
328
|
-
const serverRelative = relative(process.cwd(), serverPath);
|
|
329
|
-
|
|
330
|
-
return `
|
|
331
|
-
[Pulse] Import Violation: Client Component cannot import Server Component
|
|
332
|
-
at ${clientRelative}
|
|
333
|
-
importing ${importSource}
|
|
334
|
-
resolved to ${serverRelative}
|
|
335
|
-
|
|
336
|
-
Client Components can only import:
|
|
337
|
-
• Other Client Components ('use client')
|
|
338
|
-
• Shared utilities (no directive)
|
|
339
|
-
• Third-party packages
|
|
340
|
-
|
|
341
|
-
→ Move shared logic to a Client Component
|
|
342
|
-
→ Use Server Actions for server-side operations
|
|
343
|
-
→ Create a wrapper Client Component that calls Server Actions
|
|
344
|
-
|
|
345
|
-
See: https://pulse-js.fr/server-components#import-rules
|
|
346
|
-
`.trim();
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Helper function to load client manifest (for SSR)
|
|
351
|
-
*
|
|
352
|
-
* @param {string} manifestPath - Path to manifest file
|
|
353
|
-
* @returns {Object} Client manifest
|
|
354
|
-
*
|
|
355
|
-
* @example
|
|
356
|
-
* import { loadClientManifest } from 'pulse-js-framework/rollup/server-components';
|
|
357
|
-
*
|
|
358
|
-
* const manifest = loadClientManifest('./dist/.pulse-manifest.json');
|
|
359
|
-
* // Use manifest for SSR: renderServerComponent(Component, props, { clientManifest: manifest.components })
|
|
360
|
-
*/
|
|
361
|
-
export function loadClientManifest(manifestPath) {
|
|
362
|
-
try {
|
|
363
|
-
const { readFileSync } = require('fs');
|
|
364
|
-
const content = readFileSync(manifestPath, 'utf-8');
|
|
365
|
-
return JSON.parse(content);
|
|
366
|
-
} catch (error) {
|
|
367
|
-
console.warn(`Failed to load client manifest from ${manifestPath}:`, error.message);
|
|
368
|
-
return { version: '1.0', components: {} };
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Helper function to get client component chunk URL
|
|
374
|
-
*
|
|
375
|
-
* @param {Object} manifest - Client manifest
|
|
376
|
-
* @param {string} componentId - Component ID
|
|
377
|
-
* @returns {string|null} Chunk URL or null if not found
|
|
378
|
-
*/
|
|
379
|
-
export function getComponentChunk(manifest, componentId) {
|
|
380
|
-
return manifest.components[componentId]?.chunk || null;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Helper function to get all client component IDs
|
|
385
|
-
*
|
|
386
|
-
* @param {Object} manifest - Client manifest
|
|
387
|
-
* @returns {Set<string>} Set of component IDs
|
|
388
|
-
*/
|
|
389
|
-
export function getClientComponentIds(manifest) {
|
|
390
|
-
return new Set(Object.keys(manifest.components));
|
|
391
|
-
}
|
|
251
|
+
// Re-export shared manifest helpers
|
|
252
|
+
export { loadClientManifest, getComponentChunk, getClientComponentIds } from './shared.js';
|
package/loader/rollup-plugin.js
CHANGED
|
@@ -29,34 +29,12 @@
|
|
|
29
29
|
|
|
30
30
|
import { compile } from '../compiler/index.js';
|
|
31
31
|
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
getStylusVersion,
|
|
39
|
-
detectPreprocessor
|
|
40
|
-
} from '../compiler/preprocessor.js';
|
|
41
|
-
import { dirname } from 'path';
|
|
42
|
-
|
|
43
|
-
// Cache for preprocessor availability checks
|
|
44
|
-
let preprocessorCache = null;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Check available preprocessors once
|
|
48
|
-
*/
|
|
49
|
-
function checkPreprocessors() {
|
|
50
|
-
if (preprocessorCache) return preprocessorCache;
|
|
51
|
-
|
|
52
|
-
preprocessorCache = {
|
|
53
|
-
sass: isSassAvailable(),
|
|
54
|
-
less: isLessAvailable(),
|
|
55
|
-
stylus: isStylusAvailable()
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
return preprocessorCache;
|
|
59
|
-
}
|
|
32
|
+
logPreprocessorAvailability,
|
|
33
|
+
extractCssFromOutput,
|
|
34
|
+
removeInlineStyles,
|
|
35
|
+
processStyles,
|
|
36
|
+
getPreprocessorOptions
|
|
37
|
+
} from './shared.js';
|
|
60
38
|
|
|
61
39
|
/**
|
|
62
40
|
* Create Pulse Rollup plugin
|
|
@@ -67,6 +45,7 @@ export default function pulsePlugin(options = {}) {
|
|
|
67
45
|
exclude = /node_modules/,
|
|
68
46
|
sourceMap = true,
|
|
69
47
|
extractCss = null, // Path to output CSS file, or null for inline
|
|
48
|
+
quiet = false,
|
|
70
49
|
sass: sassOptions = {},
|
|
71
50
|
less: lessOptions = {},
|
|
72
51
|
stylus: stylusOptions = {}
|
|
@@ -89,22 +68,9 @@ export default function pulsePlugin(options = {}) {
|
|
|
89
68
|
accumulatedCss = '';
|
|
90
69
|
cssEmitted = false;
|
|
91
70
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (available.sass) {
|
|
97
|
-
preprocessors.push(`SASS ${getSassVersion() || 'unknown'}`);
|
|
98
|
-
}
|
|
99
|
-
if (available.less) {
|
|
100
|
-
preprocessors.push(`LESS ${getLessVersion() || 'unknown'}`);
|
|
101
|
-
}
|
|
102
|
-
if (available.stylus) {
|
|
103
|
-
preprocessors.push(`Stylus ${getStylusVersion() || 'unknown'}`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (preprocessors.length > 0) {
|
|
107
|
-
console.log(`[Pulse Rollup] Preprocessor support: ${preprocessors.join(', ')}`);
|
|
71
|
+
// Log preprocessor availability
|
|
72
|
+
if (!quiet) {
|
|
73
|
+
logPreprocessorAvailability('Pulse Rollup');
|
|
108
74
|
}
|
|
109
75
|
},
|
|
110
76
|
|
|
@@ -155,40 +121,24 @@ export default function pulsePlugin(options = {}) {
|
|
|
155
121
|
let outputMap = result.map;
|
|
156
122
|
|
|
157
123
|
// Extract CSS from compiled output
|
|
158
|
-
const
|
|
124
|
+
const { css: extractedCss, found: cssFound } = extractCssFromOutput(outputCode);
|
|
159
125
|
|
|
160
|
-
if (
|
|
161
|
-
let css =
|
|
162
|
-
|
|
163
|
-
// Check available preprocessors
|
|
164
|
-
const available = checkPreprocessors();
|
|
165
|
-
const preprocessor = detectPreprocessor(css);
|
|
126
|
+
if (cssFound) {
|
|
127
|
+
let css = extractedCss;
|
|
166
128
|
|
|
167
129
|
// Preprocess if preprocessor detected and available
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const preprocessorOptions = {
|
|
171
|
-
sass: sassOptions,
|
|
172
|
-
less: lessOptions,
|
|
173
|
-
stylus: stylusOptions
|
|
174
|
-
}[preprocessor];
|
|
175
|
-
|
|
176
|
-
const preprocessed = preprocessStylesSync(css, {
|
|
177
|
-
filename: id,
|
|
178
|
-
loadPaths: [dirname(id), ...(preprocessorOptions.loadPaths || [])],
|
|
179
|
-
compressed: preprocessorOptions.compressed || false,
|
|
180
|
-
preprocessor // Force detected preprocessor
|
|
181
|
-
});
|
|
130
|
+
const styleResult = processStyles(css, id, { sassOptions, lessOptions, stylusOptions });
|
|
131
|
+
css = styleResult.css;
|
|
182
132
|
|
|
183
|
-
|
|
133
|
+
if (styleResult.warning) {
|
|
134
|
+
this.warn(styleResult.warning);
|
|
135
|
+
}
|
|
184
136
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// Emit warning but continue with original CSS
|
|
191
|
-
this.warn(`${preprocessor.toUpperCase()} compilation warning: ${preprocessorError.message}`);
|
|
137
|
+
// Log preprocessor usage in verbose mode
|
|
138
|
+
if (styleResult.preprocessor !== 'none') {
|
|
139
|
+
const preprocessorOptions = getPreprocessorOptions(styleResult.preprocessor, { sassOptions, lessOptions, stylusOptions });
|
|
140
|
+
if (preprocessorOptions?.verbose) {
|
|
141
|
+
console.log(`[Pulse] Compiled ${styleResult.preprocessor.toUpperCase()} in ${id}`);
|
|
192
142
|
}
|
|
193
143
|
}
|
|
194
144
|
|
|
@@ -197,10 +147,7 @@ export default function pulsePlugin(options = {}) {
|
|
|
197
147
|
accumulatedCss += `/* ${id} */\n${css}\n\n`;
|
|
198
148
|
|
|
199
149
|
// Remove inline CSS injection from output
|
|
200
|
-
outputCode = outputCode
|
|
201
|
-
/\/\/ Styles\nconst styles = `[\s\S]*?`;\n\/\/ Inject styles\nconst styleEl = document\.createElement\("style"\);\nstyleEl\.textContent = styles;\ndocument\.head\.appendChild\(styleEl\);/,
|
|
202
|
-
'// Styles extracted to CSS file'
|
|
203
|
-
);
|
|
150
|
+
outputCode = removeInlineStyles(outputCode, '// Styles extracted to CSS file');
|
|
204
151
|
}
|
|
205
152
|
// else: keep inline CSS injection
|
|
206
153
|
}
|
|
@@ -226,7 +173,9 @@ export default function pulsePlugin(options = {}) {
|
|
|
226
173
|
source: accumulatedCss
|
|
227
174
|
});
|
|
228
175
|
cssEmitted = true;
|
|
229
|
-
|
|
176
|
+
if (!quiet) {
|
|
177
|
+
console.log(`[Pulse] Emitted CSS to ${extractCss}`);
|
|
178
|
+
}
|
|
230
179
|
}
|
|
231
180
|
}
|
|
232
181
|
};
|