@nlabs/lex 1.51.6 → 1.52.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nlabs/lex",
3
- "version": "1.51.6",
3
+ "version": "1.52.0",
4
4
  "description": "Lex",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -131,7 +131,7 @@
131
131
  "dotenv-webpack": "^8.1.1",
132
132
  "download-npm-package": "^3.1.12",
133
133
  "eslint": "^9.39.1",
134
- "eslint-config-styleguidejs": "^4.1.9",
134
+ "eslint-config-styleguidejs": "^4.1.12",
135
135
  "execa": "9.6.1",
136
136
  "expect": "^30.2.0",
137
137
  "exports-loader": "^5.0.0",
@@ -176,9 +176,12 @@
176
176
  "postcss-nesting": "^13.0.2",
177
177
  "postcss-percentage": "^0.0.0",
178
178
  "postcss-preset-env": "^10.5.0",
179
+ "postcss-value-parser": "^4.2.0",
180
+ "math-expression-evaluator": "^2.0.7",
179
181
  "postcss-simple-vars": "^7.0.1",
180
182
  "postcss-svgo": "7.1.0",
181
183
  "postcss-url": "10.1.3",
184
+ "postcss": "^8.5.6",
182
185
  "process": "^0.11.10",
183
186
  "randombytes": "^2.1.0",
184
187
  "react": "^19.2.1",
@@ -194,7 +197,7 @@
194
197
  "stream-browserify": "^3.0.0",
195
198
  "stream-http": "^3.2.0",
196
199
  "style-loader": "^4.0.0",
197
- "svg-spritemap-webpack-plugin": "^5.0.4",
200
+ "svg-spritemap-webpack-plugin": "^5.1.0",
198
201
  "svgo": "4.0.0",
199
202
  "swc-loader": "^0.2.6",
200
203
  "tailwindcss": "^4.1.17",
@@ -230,10 +233,14 @@
230
233
  "css-loader": "7.1.2"
231
234
  },
232
235
  "overrides": {
236
+ "cheerio": "^1.0.0-rc.13",
233
237
  "cross-spawn": "^7.0.6",
238
+ "css-select": "^5.1.0",
234
239
  "got": "^14.6.5",
235
240
  "http-cache-semantics": "^4.2.0",
236
241
  "http-proxy-middleware": "^3.0.5",
242
+ "lodash.pick": "^4.4.0",
243
+ "nth-check": "^2.1.1",
237
244
  "postcss": "^8.5.6",
238
245
  "semver-regex": "^4.0.5",
239
246
  "tmp": "^0.2.5"
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Manual test script to verify static file serving
4
+ * This script starts the dev server and tests static file access
5
+ */
6
+
7
+ import {spawn} from 'child_process';
8
+ import {createConnection} from 'net';
9
+ import {join} from 'path';
10
+ import {fileURLToPath} from 'url';
11
+ import {dirname} from 'path';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+ const projectRoot = join(__dirname, '..');
16
+
17
+ const testPort = 3002;
18
+ const lexPath = join(projectRoot, 'lib', 'lex.js');
19
+
20
+ console.log('🧪 Starting dev server to test static file serving...');
21
+ console.log(` Port: ${testPort}`);
22
+ console.log(` Static path: ${join(projectRoot, 'src', 'static')}`);
23
+ console.log(` Test file: ${join(projectRoot, 'src', 'static', 'test.txt')}\n`);
24
+
25
+ const devServerProcess = spawn('node', [lexPath, 'dev', '--port', testPort.toString()], {
26
+ cwd: projectRoot,
27
+ stdio: 'inherit',
28
+ env: {
29
+ ...process.env,
30
+ NODE_ENV: 'development'
31
+ }
32
+ });
33
+
34
+ const checkPort = (port) => {
35
+ return new Promise((resolve) => {
36
+ const socket = createConnection(port, 'localhost');
37
+ socket.on('connect', () => {
38
+ socket.destroy();
39
+ resolve(true);
40
+ });
41
+ socket.on('error', () => {
42
+ resolve(false);
43
+ });
44
+ socket.setTimeout(2000, () => {
45
+ socket.destroy();
46
+ resolve(false);
47
+ });
48
+ });
49
+ };
50
+
51
+ const waitForServer = async () => {
52
+ console.log('⏳ Waiting for server to start...');
53
+ for (let i = 0; i < 60; i++) {
54
+ await new Promise(resolve => setTimeout(resolve, 1000));
55
+ const portOpen = await checkPort(testPort);
56
+ if (portOpen) {
57
+ console.log(`✅ Server is running on port ${testPort}\n`);
58
+ return true;
59
+ }
60
+ if (i % 5 === 4) {
61
+ process.stdout.write('.');
62
+ }
63
+ }
64
+ return false;
65
+ };
66
+
67
+ const testStaticFile = async () => {
68
+ try {
69
+ console.log('🌐 Testing static file access...');
70
+ const response = await fetch(`http://localhost:${testPort}/test.txt`);
71
+ console.log(` Status: ${response.status}`);
72
+ console.log(` Headers:`, Object.fromEntries(response.headers.entries()));
73
+
74
+ if (response.ok) {
75
+ const content = await response.text();
76
+ console.log(` Content: "${content}"`);
77
+ console.log('\n✅ Static file is accessible!');
78
+ return true;
79
+ } else {
80
+ console.log(`\n❌ Static file returned status ${response.status}`);
81
+ const text = await response.text();
82
+ console.log(` Response: ${text.substring(0, 200)}`);
83
+ return false;
84
+ }
85
+ } catch (error) {
86
+ console.log(`\n❌ Error accessing static file: ${error.message}`);
87
+ return false;
88
+ }
89
+ };
90
+
91
+ waitForServer().then(async (serverReady) => {
92
+ if (!serverReady) {
93
+ console.log('\n❌ Server did not start within 60 seconds');
94
+ devServerProcess.kill('SIGTERM');
95
+ process.exit(1);
96
+ }
97
+
98
+ // Give it a moment to fully initialize
99
+ await new Promise(resolve => setTimeout(resolve, 2000));
100
+
101
+ const success = await testStaticFile();
102
+
103
+ console.log('\n🛑 Stopping dev server...');
104
+ devServerProcess.kill('SIGTERM');
105
+ setTimeout(() => {
106
+ if (!devServerProcess.killed) {
107
+ devServerProcess.kill('SIGKILL');
108
+ }
109
+ process.exit(success ? 0 : 1);
110
+ }, 1000);
111
+ });
112
+
113
+ devServerProcess.on('error', (error) => {
114
+ console.error('❌ Failed to start dev server:', error.message);
115
+ process.exit(1);
116
+ });
117
+
@@ -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
  ];
@@ -270,6 +315,8 @@ const alias = aliasKeys.reduce((aliases, key) => {
270
315
 
271
316
  export default (webpackEnv, webpackOptions) => {
272
317
  const {bundleAnalyzer, watch, entry: cliEntry, mode: cliMode, port} = webpackOptions;
318
+ const envPort = process.env.WEBPACK_DEV_PORT ? parseInt(process.env.WEBPACK_DEV_PORT, 10) : null;
319
+ const finalPort = port || envPort || 3000;
273
320
  const entryValue = Array.isArray(cliEntry) ? cliEntry[0] : cliEntry;
274
321
 
275
322
  // Debug printout for environment and mode
@@ -312,8 +359,42 @@ export default (webpackEnv, webpackOptions) => {
312
359
  {
313
360
  test: /\.js$/,
314
361
  include: /node_modules/,
362
+ exclude: [
363
+ /[\\/]websocket\.js$/,
364
+ /[\\/]websocket.*\.js$/
365
+ ],
315
366
  type: 'javascript/auto'
316
367
  },
368
+ {
369
+ test: /[\\/]websocket(.*)?\.js$/,
370
+ include: /node_modules/,
371
+ use: {
372
+ loader: swcLoaderPath,
373
+ options: {
374
+ jsc: {
375
+ parser: {
376
+ syntax: 'ecmascript',
377
+ dynamicImport: true,
378
+ importAssertions: true
379
+ },
380
+ target: 'es2020',
381
+ transform: {
382
+ legacyDecorator: false,
383
+ decoratorMetadata: false
384
+ }
385
+ },
386
+ module: {
387
+ type: 'es6',
388
+ strict: false,
389
+ strictMode: true,
390
+ lazy: false,
391
+ noInterop: true
392
+ },
393
+ minify: false,
394
+ sourceMaps: false
395
+ }
396
+ }
397
+ },
317
398
  {
318
399
  enforce: 'pre',
319
400
  exclude: /(node_modules)/,
@@ -328,6 +409,7 @@ export default (webpackEnv, webpackOptions) => {
328
409
  `${sourceFullPath}/**/*.test.ts*`
329
410
  ],
330
411
  include: sourceFullPath,
412
+ type: 'javascript/esm',
331
413
  loader: swcLoaderPath,
332
414
  options: {
333
415
  ...LexConfig.config.swc,
@@ -335,8 +417,14 @@ export default (webpackEnv, webpackOptions) => {
335
417
  ...LexConfig.config.swc?.jsc,
336
418
  parser: {
337
419
  ...LexConfig.config.swc?.jsc?.parser,
338
- tsx: false
339
- }
420
+ tsx: false,
421
+ dynamicImport: true
422
+ },
423
+ target: LexConfig.config.swc?.jsc?.target || 'es2020'
424
+ },
425
+ module: {
426
+ ...LexConfig.config.swc?.module,
427
+ type: 'es6'
340
428
  }
341
429
  },
342
430
  resolve: {
@@ -351,6 +439,7 @@ export default (webpackEnv, webpackOptions) => {
351
439
  `${sourceFullPath}/**/*.test.ts*`
352
440
  ],
353
441
  include: sourceFullPath,
442
+ type: 'javascript/esm',
354
443
  loader: swcLoaderPath,
355
444
  options: {
356
445
  ...LexConfig.config.swc,
@@ -543,7 +632,10 @@ export default (webpackEnv, webpackOptions) => {
543
632
  library: libraryName,
544
633
  libraryTarget,
545
634
  path: outputFullPath,
546
- publicPath: '/'
635
+ publicPath: '/',
636
+ environment: {
637
+ module: true
638
+ }
547
639
  },
548
640
  plugins,
549
641
  recordsPath: relativeFilePath('webpack.records.json', process.cwd()),
@@ -620,7 +712,7 @@ export default (webpackEnv, webpackOptions) => {
620
712
  }
621
713
  },
622
714
  {
623
- from: /\\.(css|gif|ico|jpg|json|png|svg)$/,
715
+ from: /\\.(css|gif|ico|jpg|json|png|svg|txt)$/,
624
716
  to: ({parsedUrl: {pathname}}) => pathname
625
717
  }
626
718
  ],
@@ -628,17 +720,52 @@ export default (webpackEnv, webpackOptions) => {
628
720
  },
629
721
  hmr: false,
630
722
  log: {level: 'trace'},
631
- middleware: (app) =>
723
+ middleware: (app, builtins) => {
724
+ if(existsSync(staticPathFull)) {
725
+ if (process.env.LEX_CONFIG_DEBUG) {
726
+ console.log(`[LEX_DEBUG] Setting up static file serving from: ${staticPathFull}`);
727
+ }
728
+
729
+ const koaStatic = require('koa-static');
730
+
731
+ app.use(koaStatic(staticPathFull, {
732
+ index: false, // Don't auto-serve index files
733
+ defer: false, // CRITICAL: Don't defer - serve immediately if file exists
734
+ hidden: false,
735
+ gzip: true,
736
+ br: false
737
+ }));
738
+
739
+ if (process.env.LEX_CONFIG_DEBUG) {
740
+ app.use(async (ctx, next) => {
741
+ const path = ctx.path || ctx.url || '';
742
+ if (path && !path.match(/^\/wps/) && !path.match(/^\/webpack/)) {
743
+ console.log(`[LEX_DEBUG] Request: ${path}`);
744
+ }
745
+ await next();
746
+ if (ctx.status === 404 && path && path.includes('.')) {
747
+ console.log(`[LEX_DEBUG] 404 for: ${path}, body set: ${ctx.body !== undefined}`);
748
+ }
749
+ });
750
+ }
751
+ } else {
752
+ if (process.env.LEX_CONFIG_DEBUG) {
753
+ console.log(`[LEX_DEBUG] Static path does not exist: ${staticPathFull}`);
754
+ }
755
+ }
756
+
632
757
  app.use(async (ctx, next) => {
633
- if(ctx.path.match(/^\/wps/)) {
758
+ const path = ctx.path || ctx.url || (ctx.request && ctx.request.path) || '';
759
+ if(path && path.match(/^\/wps/)) {
634
760
  const {accept, Accept, ...remainingHeaders} =
635
761
  ctx.request.header;
636
762
  ctx.request.header = remainingHeaders;
637
763
  }
638
764
  await next();
639
- }),
765
+ });
766
+ },
640
767
  open: process.env.WEBPACK_DEV_OPEN === 'true',
641
- port: port || 3000,
768
+ port: finalPort,
642
769
  progress: 'minimal',
643
770
  static: existsSync(outputFullPath) ? [outputFullPath] : [],
644
771
  status: true