@nlabs/lex 1.51.5 → 1.51.7

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.
@@ -215,9 +215,14 @@ try {
215
215
  });
216
216
 
217
217
  let serverOutput = '';
218
+ let serverStartedOutput = false;
218
219
  devServerProcess.stdout.on('data', (data) => {
219
220
  const output = data.toString();
220
221
  serverOutput += output;
222
+ // Check for server ready indicators
223
+ if (output.includes('compiled') || output.includes('Local:') || output.includes('http://') || output.includes('webpack compiled')) {
224
+ serverStartedOutput = true;
225
+ }
221
226
  });
222
227
 
223
228
  devServerProcess.stderr.on('data', (data) => {
@@ -226,6 +231,10 @@ try {
226
231
  if (output.includes('error') || output.includes('Error') || output.includes('ERROR')) {
227
232
  serverError = output;
228
233
  }
234
+ // Sometimes webpack outputs to stderr but it's not an error
235
+ if (output.includes('compiled') || output.includes('webpack')) {
236
+ serverStartedOutput = true;
237
+ }
229
238
  });
230
239
 
231
240
  devServerProcess.on('error', (error) => {
@@ -253,33 +262,65 @@ try {
253
262
  };
254
263
 
255
264
  const waitForServer = async () => {
256
- for (let i = 0; i < 60; i++) {
265
+ for (let i = 0; i < 90; i++) {
257
266
  await new Promise(resolve => setTimeout(resolve, 1000));
258
267
 
259
268
  const portOpen = await checkPort(testPort);
260
- if (portOpen) {
261
- await new Promise(resolve => setTimeout(resolve, 5000));
262
- const testEndpoints = ['/test.txt', '/index.html', '/'];
263
- for (const endpoint of testEndpoints) {
264
- try {
265
- const controller = new AbortController();
266
- const timeoutId = setTimeout(() => controller.abort(), 5000);
267
- const response = await fetch(`http://localhost:${testPort}${endpoint}`, {
268
- signal: controller.signal
269
- });
270
- clearTimeout(timeoutId);
271
- if (response.ok || response.status === 200) {
269
+ if (portOpen || serverStartedOutput) {
270
+ // Give it a bit more time to fully initialize
271
+ await new Promise(resolve => setTimeout(resolve, 3000));
272
+
273
+ // Try to access the static file directly
274
+ try {
275
+ const controller = new AbortController();
276
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
277
+ const response = await fetch(`http://localhost:${testPort}/test.txt`, {
278
+ signal: controller.signal
279
+ });
280
+ clearTimeout(timeoutId);
281
+ if (response.ok && response.status === 200) {
282
+ const content = await response.text();
283
+ if (content.includes('Static file content')) {
272
284
  return true;
273
285
  }
274
- } catch (error) {
275
- if (error.name !== 'AbortError') {
286
+ }
287
+ } catch (error) {
288
+ if (error.name !== 'AbortError' && i > 5) {
289
+ // Only log after a few attempts
290
+ }
291
+ }
292
+
293
+ // Also try index.html as a fallback check
294
+ try {
295
+ const controller = new AbortController();
296
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
297
+ const response = await fetch(`http://localhost:${testPort}/index.html`, {
298
+ signal: controller.signal
299
+ });
300
+ clearTimeout(timeoutId);
301
+ if (response.ok || response.status === 200) {
302
+ // Server is up, even if static file test didn't work yet
303
+ if (i > 10) {
304
+ // After 10 seconds, if server is up, try static file again
305
+ try {
306
+ const staticResponse = await fetch(`http://localhost:${testPort}/test.txt`, {
307
+ signal: controller.signal
308
+ });
309
+ if (staticResponse.ok) {
310
+ return true;
311
+ }
312
+ } catch {
313
+ }
276
314
  }
277
315
  }
316
+ } catch (error) {
317
+ if (error.name !== 'AbortError') {
318
+ }
278
319
  }
279
320
  }
280
321
 
281
322
  if (i % 10 === 9 && i > 0) {
282
- console.log(` Still waiting... (${i + 1}/60 seconds)`);
323
+ console.log(` Still waiting... (${i + 1}/90 seconds)`);
283
324
  }
284
325
  }
285
326
  return false;
@@ -288,29 +329,49 @@ try {
288
329
  serverReady = await waitForServer();
289
330
 
290
331
  if (serverError) {
291
- console.log(`⚠️ Dev server error: ${serverError}`);
292
- console.log('⚠️ Skipping HTTP tests due to server error');
332
+ console.log(`❌ Dev server error: ${serverError}`);
333
+ console.log(' HTTP tests cannot run due to server error');
293
334
  console.log('💡 Note: Static files are copied to build directory and should be accessible via dev server');
335
+ throw new Error(`Dev server failed to start: ${serverError}`);
294
336
  } else if (!serverReady) {
295
- console.log('⚠️ Dev server did not start within 60 seconds, skipping HTTP tests');
296
- console.log('💡 Note: Static files are copied to build directory and should be accessible via dev server');
337
+ console.log('Dev server did not start within 60 seconds');
338
+ console.log(' HTTP tests cannot run - this is a required test');
297
339
  console.log('💡 To test manually, run: cd <test-dir> && lex dev --port 3001');
298
340
  if (serverOutput) {
299
- const lastOutput = serverOutput.slice(-1000);
300
- console.log('\nServer output (last 1000 chars):');
341
+ const lastOutput = serverOutput.slice(-2000);
342
+ console.log('\nServer output (last 2000 chars):');
301
343
  console.log(lastOutput);
344
+ // Check if there are any obvious errors
345
+ if (lastOutput.includes('Error') || lastOutput.includes('error') || lastOutput.includes('Cannot find')) {
346
+ console.log('\n⚠️ Potential errors detected in server output above');
347
+ }
302
348
  } else {
303
349
  console.log(' (No server output captured - server may not have started)');
350
+ console.log(' This could indicate the process failed to spawn or exited immediately');
351
+ }
352
+
353
+ // Check if process is still running
354
+ if (devServerProcess && !devServerProcess.killed) {
355
+ try {
356
+ devServerProcess.kill(0); // Check if process exists
357
+ console.log(' (Dev server process is still running but not responding)');
358
+ } catch {
359
+ console.log(' (Dev server process has exited)');
360
+ }
304
361
  }
362
+ throw new Error('Dev server did not start within timeout period - HTTP static file access test cannot run');
305
363
  } else {
306
364
  console.log(`✅ Dev server started on port ${testPort}`);
307
365
 
308
366
  const testUrls = [
309
- {url: '/test.txt', expectedContent: 'Static file content', description: 'Static file from staticPath'},
310
- {url: '/index.html', expectedContent: 'Test App', description: 'HTML file'},
311
- {url: '/images/test.png', expectedContent: 'fake-png-content', description: 'Image file'}
367
+ {url: '/test.txt', expectedContent: 'Static file content', description: 'Static file from staticPath', required: true},
368
+ {url: '/index.html', expectedContent: 'Test App', description: 'HTML file', required: false},
369
+ {url: '/images/test.png', expectedContent: 'fake-png-content', description: 'Image file', required: false}
312
370
  ];
313
371
 
372
+ let httpTestFailed = false;
373
+ const httpTestErrors = [];
374
+
314
375
  for (const test of testUrls) {
315
376
  try {
316
377
  const response = await fetch(`http://localhost:${testPort}${test.url}`);
@@ -319,19 +380,47 @@ try {
319
380
  if (content.includes(test.expectedContent)) {
320
381
  console.log(`✅ ${test.description} accessible via HTTP (${test.url})`);
321
382
  } else {
322
- console.log(`⚠️ ${test.description} accessible but content doesn't match (${test.url})`);
383
+ const errorMsg = `${test.description} accessible but content doesn't match (${test.url})`;
384
+ console.log(`❌ ${errorMsg}`);
385
+ if (test.required) {
386
+ httpTestFailed = true;
387
+ httpTestErrors.push(errorMsg);
388
+ }
323
389
  }
324
390
  } else {
325
- console.log(`❌ ${test.description} returned status ${response.status} (${test.url})`);
391
+ const errorMsg = `${test.description} returned status ${response.status} (${test.url})`;
392
+ console.log(`❌ ${errorMsg}`);
393
+ if (test.required) {
394
+ httpTestFailed = true;
395
+ httpTestErrors.push(errorMsg);
396
+ }
326
397
  }
327
398
  } catch (error) {
328
- console.log(`❌ Failed to fetch ${test.description}: ${error.message}`);
399
+ const errorMsg = `Failed to fetch ${test.description}: ${error.message}`;
400
+ console.log(`❌ ${errorMsg}`);
401
+ if (test.required) {
402
+ httpTestFailed = true;
403
+ httpTestErrors.push(errorMsg);
404
+ }
329
405
  }
330
406
  }
407
+
408
+ if (httpTestFailed) {
409
+ console.log('\n❌ HTTP static file access test FAILED!');
410
+ console.log('Errors:');
411
+ httpTestErrors.forEach(err => console.log(` - ${err}`));
412
+ console.log('\n💡 The dev server must be able to serve static files from the staticPath directory.');
413
+ console.log('💡 Check that the middleware in webpack.config.js is correctly serving files from staticPathFull.');
414
+ throw new Error('HTTP static file access test failed');
415
+ } else {
416
+ console.log('\n✅ All HTTP static file access tests passed!');
417
+ }
331
418
  }
332
419
 
333
420
  } catch (error) {
334
- console.log(`⚠️ Dev server test skipped: ${error.message}`);
421
+ console.error(`\n❌ Dev server HTTP test failed: ${error.message}`);
422
+ console.error('This test is required and must pass.');
423
+ throw error;
335
424
  } finally {
336
425
  if (devServerProcess) {
337
426
  console.log('🛑 Stopping dev server...');
@@ -344,12 +433,17 @@ try {
344
433
  }
345
434
  }
346
435
 
436
+ // Only print success if we didn't throw an error
347
437
  console.log('\n🎉 All tests passed!');
348
438
  console.log(`\n📁 Test project location: ${testDir}`);
349
439
  console.log('💡 You can inspect the build output in the build/ directory');
350
440
 
351
441
  } catch (error) {
352
- console.error('❌ Build failed:', error.message);
442
+ console.error('\nTest suite failed:', error.message);
443
+ if (error.stack) {
444
+ console.error('\nStack trace:');
445
+ console.error(error.stack);
446
+ }
353
447
  process.exit(1);
354
448
  } finally {
355
449
  console.log('\n🧹 Cleaning up...');
package/webpack.config.js CHANGED
@@ -15,7 +15,9 @@ import {existsSync} from 'fs';
15
15
  import {sync as globSync} from 'glob';
16
16
  import HtmlWebPackPlugin from 'html-webpack-plugin';
17
17
  import isEmpty from 'lodash/isEmpty.js';
18
+ import {createRequire} from 'module';
18
19
  import {resolve as pathResolve} from 'path';
20
+ import {fileURLToPath} from 'url';
19
21
  import postcssBrowserReporter from 'postcss-browser-reporter';
20
22
  import postcssCustomProperties from 'postcss-custom-properties';
21
23
  import postcssFlexbugsFixes from 'postcss-flexbugs-fixes';
@@ -40,6 +42,7 @@ const {ProgressPlugin, ProvidePlugin} = webpack;
40
42
  const isProduction = process.env.NODE_ENV === 'production';
41
43
  const lexConfig = JSON.parse(process.env.LEX_CONFIG) || {};
42
44
  const dirName = new URL('.', import.meta.url).pathname;
45
+ const require = createRequire(fileURLToPath(import.meta.url));
43
46
 
44
47
  const {
45
48
  isStatic,
@@ -100,6 +103,48 @@ const plugins = [
100
103
  console.log('\x1b[32m[webpack]\x1b[0m Build complete. Watching for changes...');
101
104
  }
102
105
  });
106
+ compiler.hooks.normalModuleFactory.tap('ImportMetaTransform', (normalModuleFactory) => {
107
+ normalModuleFactory.hooks.parser.for('javascript/auto').tap('ImportMetaTransform', (parser) => {
108
+ parser.hooks.expression.for('import.meta').tap('ImportMetaTransform', (expr) => {
109
+ const dep = new webpack.dependencies.ConstDependency(
110
+ '({ url: typeof document !== "undefined" && document.currentScript && document.currentScript.src ? new URL(document.currentScript.src, window.location.href).href : (typeof window !== "undefined" ? new URL("", window.location.href).href : "") })',
111
+ expr.range
112
+ );
113
+ dep.loc = expr.loc;
114
+ parser.state.module.addPresentationalDependency(dep);
115
+ return true;
116
+ });
117
+ parser.hooks.expression.for('import.meta.url').tap('ImportMetaTransform', (expr) => {
118
+ const dep = new webpack.dependencies.ConstDependency(
119
+ '(typeof document !== "undefined" && document.currentScript && document.currentScript.src ? new URL(document.currentScript.src, window.location.href).href : (typeof window !== "undefined" ? new URL("", window.location.href).href : ""))',
120
+ expr.range
121
+ );
122
+ dep.loc = expr.loc;
123
+ parser.state.module.addPresentationalDependency(dep);
124
+ return true;
125
+ });
126
+ });
127
+ normalModuleFactory.hooks.parser.for('javascript/esm').tap('ImportMetaTransform', (parser) => {
128
+ parser.hooks.expression.for('import.meta').tap('ImportMetaTransform', (expr) => {
129
+ const dep = new webpack.dependencies.ConstDependency(
130
+ '({ url: typeof document !== "undefined" && document.currentScript && document.currentScript.src ? new URL(document.currentScript.src, window.location.href).href : (typeof window !== "undefined" ? new URL("", window.location.href).href : "") })',
131
+ expr.range
132
+ );
133
+ dep.loc = expr.loc;
134
+ parser.state.module.addPresentationalDependency(dep);
135
+ return true;
136
+ });
137
+ parser.hooks.expression.for('import.meta.url').tap('ImportMetaTransform', (expr) => {
138
+ const dep = new webpack.dependencies.ConstDependency(
139
+ '(typeof document !== "undefined" && document.currentScript && document.currentScript.src ? new URL(document.currentScript.src, window.location.href).href : (typeof window !== "undefined" ? new URL("", window.location.href).href : ""))',
140
+ expr.range
141
+ );
142
+ dep.loc = expr.loc;
143
+ parser.state.module.addPresentationalDependency(dep);
144
+ return true;
145
+ });
146
+ });
147
+ });
103
148
  }
104
149
  }
105
150
  ];
@@ -155,26 +200,31 @@ const fontPath = `${sourceFullPath}/fonts/`;
155
200
  const docPath = `${sourceFullPath}/docs/`;
156
201
 
157
202
  const staticPathFull = pathResolve(process.cwd(), webpackStaticPath);
203
+
158
204
  if(existsSync(staticPathFull)) {
159
205
  staticPaths.push({
160
206
  from: staticPathFull,
207
+ globOptions: {
208
+ ignore: ['**/.DS_Store']
209
+ },
210
+ noErrorOnMissing: true,
161
211
  to: './'
162
212
  });
163
213
  watchIgnorePaths.push(staticPathFull);
164
214
  }
165
215
 
166
216
  if(existsSync(imagePath)) {
167
- staticPaths.push({from: imagePath, to: './images/'});
217
+ staticPaths.push({from: imagePath, noErrorOnMissing: true, to: './images/'});
168
218
  watchIgnorePaths.push(imagePath);
169
219
  }
170
220
 
171
221
  if(existsSync(fontPath)) {
172
- staticPaths.push({from: fontPath, to: './fonts/'});
222
+ staticPaths.push({from: fontPath, noErrorOnMissing: true, to: './fonts/'});
173
223
  watchIgnorePaths.push(fontPath);
174
224
  }
175
225
 
176
226
  if(existsSync(docPath)) {
177
- staticPaths.push({from: docPath, to: './docs/'});
227
+ staticPaths.push({from: docPath, noErrorOnMissing: true, to: './docs/'});
178
228
  }
179
229
 
180
230
  if(staticPaths.length) {
@@ -307,8 +357,42 @@ export default (webpackEnv, webpackOptions) => {
307
357
  {
308
358
  test: /\.js$/,
309
359
  include: /node_modules/,
360
+ exclude: [
361
+ /[\\/]websocket\.js$/,
362
+ /[\\/]websocket.*\.js$/
363
+ ],
310
364
  type: 'javascript/auto'
311
365
  },
366
+ {
367
+ test: /[\\/]websocket(.*)?\.js$/,
368
+ include: /node_modules/,
369
+ use: {
370
+ loader: swcLoaderPath,
371
+ options: {
372
+ jsc: {
373
+ parser: {
374
+ syntax: 'ecmascript',
375
+ dynamicImport: true,
376
+ importAssertions: true
377
+ },
378
+ target: 'es2020',
379
+ transform: {
380
+ legacyDecorator: false,
381
+ decoratorMetadata: false
382
+ }
383
+ },
384
+ module: {
385
+ type: 'es6',
386
+ strict: false,
387
+ strictMode: true,
388
+ lazy: false,
389
+ noInterop: true
390
+ },
391
+ minify: false,
392
+ sourceMaps: false
393
+ }
394
+ }
395
+ },
312
396
  {
313
397
  enforce: 'pre',
314
398
  exclude: /(node_modules)/,
@@ -323,6 +407,7 @@ export default (webpackEnv, webpackOptions) => {
323
407
  `${sourceFullPath}/**/*.test.ts*`
324
408
  ],
325
409
  include: sourceFullPath,
410
+ type: 'javascript/esm',
326
411
  loader: swcLoaderPath,
327
412
  options: {
328
413
  ...LexConfig.config.swc,
@@ -330,8 +415,14 @@ export default (webpackEnv, webpackOptions) => {
330
415
  ...LexConfig.config.swc?.jsc,
331
416
  parser: {
332
417
  ...LexConfig.config.swc?.jsc?.parser,
333
- tsx: false
334
- }
418
+ tsx: false,
419
+ dynamicImport: true
420
+ },
421
+ target: LexConfig.config.swc?.jsc?.target || 'es2020'
422
+ },
423
+ module: {
424
+ ...LexConfig.config.swc?.module,
425
+ type: 'es6'
335
426
  }
336
427
  },
337
428
  resolve: {
@@ -346,6 +437,7 @@ export default (webpackEnv, webpackOptions) => {
346
437
  `${sourceFullPath}/**/*.test.ts*`
347
438
  ],
348
439
  include: sourceFullPath,
440
+ type: 'javascript/esm',
349
441
  loader: swcLoaderPath,
350
442
  options: {
351
443
  ...LexConfig.config.swc,
@@ -538,7 +630,10 @@ export default (webpackEnv, webpackOptions) => {
538
630
  library: libraryName,
539
631
  libraryTarget,
540
632
  path: outputFullPath,
541
- publicPath: '/'
633
+ publicPath: '/',
634
+ environment: {
635
+ module: true
636
+ }
542
637
  },
543
638
  plugins,
544
639
  recordsPath: relativeFilePath('webpack.records.json', process.cwd()),
@@ -615,7 +710,7 @@ export default (webpackEnv, webpackOptions) => {
615
710
  }
616
711
  },
617
712
  {
618
- from: /\\.(css|gif|ico|jpg|json|png|svg)$/,
713
+ from: /\\.(css|gif|ico|jpg|json|png|svg|txt)$/,
619
714
  to: ({parsedUrl: {pathname}}) => pathname
620
715
  }
621
716
  ],
@@ -623,15 +718,50 @@ export default (webpackEnv, webpackOptions) => {
623
718
  },
624
719
  hmr: false,
625
720
  log: {level: 'trace'},
626
- middleware: (app) =>
721
+ middleware: (app, builtins) => {
722
+ if(existsSync(staticPathFull)) {
723
+ if (process.env.LEX_CONFIG_DEBUG) {
724
+ console.log(`[LEX_DEBUG] Setting up static file serving from: ${staticPathFull}`);
725
+ }
726
+
727
+ const koaStatic = require('koa-static');
728
+
729
+ app.use(koaStatic(staticPathFull, {
730
+ index: false, // Don't auto-serve index files
731
+ defer: false, // CRITICAL: Don't defer - serve immediately if file exists
732
+ hidden: false,
733
+ gzip: true,
734
+ br: false
735
+ }));
736
+
737
+ if (process.env.LEX_CONFIG_DEBUG) {
738
+ app.use(async (ctx, next) => {
739
+ const path = ctx.path || ctx.url || '';
740
+ if (path && !path.match(/^\/wps/) && !path.match(/^\/webpack/)) {
741
+ console.log(`[LEX_DEBUG] Request: ${path}`);
742
+ }
743
+ await next();
744
+ if (ctx.status === 404 && path && path.includes('.')) {
745
+ console.log(`[LEX_DEBUG] 404 for: ${path}, body set: ${ctx.body !== undefined}`);
746
+ }
747
+ });
748
+ }
749
+ } else {
750
+ if (process.env.LEX_CONFIG_DEBUG) {
751
+ console.log(`[LEX_DEBUG] Static path does not exist: ${staticPathFull}`);
752
+ }
753
+ }
754
+
627
755
  app.use(async (ctx, next) => {
628
- if(ctx.path.match(/^\/wps/)) {
756
+ const path = ctx.path || ctx.url || (ctx.request && ctx.request.path) || '';
757
+ if(path && path.match(/^\/wps/)) {
629
758
  const {accept, Accept, ...remainingHeaders} =
630
759
  ctx.request.header;
631
760
  ctx.request.header = remainingHeaders;
632
761
  }
633
762
  await next();
634
- }),
763
+ });
764
+ },
635
765
  open: process.env.WEBPACK_DEV_OPEN === 'true',
636
766
  port: port || 3000,
637
767
  progress: 'minimal',