wirejs-scripts 3.0.9 → 3.0.11

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/README.md ADDED
@@ -0,0 +1,3 @@
1
+ Experimental.
2
+
3
+ If this package interests you, contact the author.
package/bin.js CHANGED
@@ -4,11 +4,13 @@ import process from 'process';
4
4
  import http from 'http';
5
5
  import fs from 'fs';
6
6
  import path from 'path';
7
+ import { Worker } from 'worker_threads';
7
8
 
8
9
  import webpack from 'webpack';
9
10
  import webpackConfigure from './configs/webpack.config.js';
10
11
  import { rimraf } from 'rimraf';
11
12
  import esbuild from 'esbuild';
13
+ import { contentType } from 'mime-types';
12
14
 
13
15
  import { JSDOM } from 'jsdom';
14
16
  import { useJSDOM } from 'wirejs-dom/v2';
@@ -115,7 +117,7 @@ async function handleApiResponse(req, res) {
115
117
  const calls = JSON.parse(body);
116
118
  logger.info('handling API request', body);
117
119
 
118
- const apiPath = path.join(CWD, 'api', 'index.js');
120
+ const apiPath = path.join(CWD, 'api', 'dist', 'index.js');
119
121
  const api = await import(`${apiPath}?cache-id=${new Date().getTime()}`);
120
122
 
121
123
  const responses = [];
@@ -174,13 +176,7 @@ async function tryStaticPath(req, res) {
174
176
  if (!fs.existsSync(fullPath)) return false;
175
177
  logger.info('static found');
176
178
 
177
- if (fullPath.endsWith(".html")) {
178
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
179
- } else if (fullPath.endsWith(".js")) {
180
- res.setHeader('Content-Type', 'text/javascript; charset=utf-8');
181
- } else {
182
- res.setHeader('Content-Type', 'text/plain; charset=utf-8');
183
- }
179
+ res.setHeader('Content-Type', contentType(path.extname(fullPath)));
184
180
 
185
181
  try {
186
182
  res.end(fs.readFileSync(fullPath));
@@ -214,6 +210,16 @@ function globMatch(pattern, text) {
214
210
  return regex.test(text);
215
211
  }
216
212
 
213
+ function toJSPath(path) {
214
+ if (path.endsWith('/')) {
215
+ return path + 'index.js';
216
+ } else if (!path.endsWith('.js')) {
217
+ return path + '.js';
218
+ } else {
219
+ return path;
220
+ }
221
+ }
222
+
217
223
  /**
218
224
  *
219
225
  * @param {Context} context
@@ -221,10 +227,11 @@ function globMatch(pattern, text) {
221
227
  */
222
228
  function routeSSR(context, forceExt) {
223
229
  const SSR_ROOT = path.join(CWD, 'dist', 'ssr');
224
- const asJSPath = forceExt ?
225
- context.location.pathname.replace(/\.(\w+)$/, `.${forceExt}`)
226
- : context.location.pathname
227
- ;
230
+
231
+ const asJSPath = forceExt
232
+ ? toJSPath(context.location.pathname)
233
+ : context.location.pathname;
234
+
228
235
  const allHandlers = fs.readdirSync(SSR_ROOT, { recursive: true })
229
236
  .filter(p => p.endsWith('.js'))
230
237
  .map(p => `/${p}`)
@@ -270,7 +277,7 @@ async function trySSRScriptPath(req, res) {
270
277
  async function trySSRPath(req, res) {
271
278
  const context = createContext(req);
272
279
 
273
- const asJSPath = context.location.pathname.replace(/\.(\w+)$/, '.js');
280
+ const asJSPath = context.location.pathname.replace(/\.(\w+)$/, '') + '.js';
274
281
  const srcPath = routeSSR(context, 'js');
275
282
  if (!srcPath) return false;
276
283
 
@@ -284,26 +291,32 @@ async function trySSRPath(req, res) {
284
291
  const module = self.exports;
285
292
  if (typeof module.generate === 'function') {
286
293
  const doc = await module.generate(context);
287
- const doctype = doc.parentNode.doctype?.name || '';
288
-
289
- let hydrationsFound = 0;
290
- while (globalThis.pendingDehydrations?.length > 0) {
291
- globalThis.pendingDehydrations.shift()(doc);
292
- hydrationsFound++;
293
- }
294
+ if (typeof doc.outerHTML === 'undefined') {
295
+ res.setHeader('Content-Type', contentType(
296
+ context.location.pathname.split('.').pop()
297
+ ));
298
+ res.end(doc);
299
+ } else {
300
+ const doctype = doc.parentNode.doctype?.name || '';
294
301
 
295
- if (hydrationsFound) {
296
- const script = doc.parentNode.createElement('script');
297
- script.src = asJSPath;
298
- doc.parentNode.body.appendChild(script);
302
+ let hydrationsFound = 0;
303
+ while (globalThis.pendingDehydrations?.length > 0) {
304
+ globalThis.pendingDehydrations.shift()(doc);
305
+ hydrationsFound++;
306
+ }
307
+
308
+ if (hydrationsFound) {
309
+ const script = doc.parentNode.createElement('script');
310
+ script.src = asJSPath;
311
+ doc.parentNode.body.appendChild(script);
312
+ }
313
+
314
+ res.setHeader('Content-type', 'text/html; charset=utf-8');
315
+ res.end([
316
+ doctype ? `<!doctype ${doctype}>\n` : '',
317
+ doc.outerHTML
318
+ ].join(''));
299
319
  }
300
-
301
- res.setHeader('Content-type', 'text/html; charset=utf-8');
302
- res.end([
303
- doctype ? `<!doctype ${doctype}>\n` : '',
304
- doc.outerHTML
305
- ].join(''));
306
-
307
320
  return true;
308
321
  } else {
309
322
  logger.info('SSR module missing generate function');
@@ -367,6 +380,25 @@ async function compile(watch = false) {
367
380
  const stats = await new Promise(async (resolve, reject) => {
368
381
  let compiler;
369
382
  if (watch) {
383
+
384
+ const apiDir = path.join(CWD, 'api');
385
+ const prebuildApiWatch = await esbuild.context({
386
+ entryPoints: [path.join(CWD, 'api', '**', '*.ts')],
387
+ outdir: path.join(apiDir, 'dist'),
388
+ platform: 'node',
389
+ bundle: true,
390
+ external: ['./node_modules/*'],
391
+ format: 'esm',
392
+ plugins: [{
393
+ name: 'post-build-rebuild-api-client',
394
+ setup(build) {
395
+ build.onEnd(() => prebuildApi(apiDir))
396
+ }
397
+ }]
398
+ });
399
+ await prebuildApiWatch.rebuild();
400
+ prebuildApiWatch.watch();
401
+
370
402
  const prebuild = await esbuild.context({
371
403
  entryPoints: [`${CWD}/src/**/*.ts`],
372
404
  outdir: `${CWD}/pre-dist`,
@@ -376,6 +408,7 @@ async function compile(watch = false) {
376
408
  });
377
409
  await prebuild.rebuild();
378
410
  prebuild.watch();
411
+
379
412
  webpack({
380
413
  ...getWebpackConfig(),
381
414
  mode: 'development',
@@ -390,6 +423,7 @@ async function compile(watch = false) {
390
423
  server.listen(3000).on('listening', () => {
391
424
  console.log('Started listening on http://localhost:3000/')
392
425
  });
426
+
393
427
  } else {
394
428
  logger.log('prebundling JS');
395
429
  await esbuild.build({
@@ -470,13 +504,13 @@ const engine = {
470
504
  async ['prebuild-api']() {
471
505
  logger.log("prebuilding api ...");
472
506
  await esbuild.build({
473
- entryPoints: [`${CWD}/**/*.ts`],
474
- outdir: CWD,
507
+ entryPoints: [path.join(CWD, '**', '*.ts')],
508
+ outdir: path.join(CWD, 'dist'),
475
509
  platform: 'node',
476
510
  bundle: false,
477
511
  format: 'esm',
478
512
  });
479
- await prebuildApi();
513
+ await prebuildApi(CWD);
480
514
  logger.log("api prebuild finished");
481
515
  },
482
516
  };
@@ -1,28 +1,14 @@
1
- import { useJSDOM } from 'wirejs-dom/v2';
2
- import fs from 'fs';
3
1
  import path from 'path';
4
2
  import glob from 'glob';
5
3
  import process from 'process';
6
4
  import CopyWebpackPlugin from 'copy-webpack-plugin';
7
- import marked from 'marked';
8
- import { JSDOM } from 'jsdom';
9
-
5
+ import { execSync } from 'child_process';
10
6
 
11
7
  const CWD = process.cwd();
12
8
  const SRC = 'pre-dist';
13
9
 
14
- // https://marked.js.org/using_advanced
15
- marked.setOptions({
16
- highlight: function (code, lang) {
17
- try {
18
- const highlighter = require('highlight.js');
19
- const language = highlighter.getLanguage(lang) ? lang : 'plaintext';
20
- return highlighter.highlight(code, { language }).value;
21
- } catch (e) {
22
- console.log("highlight.js not installed. Skipping syntax highlighting.");
23
- }
24
- }
25
- });
10
+ const __filename = import.meta.url.replace(/^file:/, '');
11
+ const SELF_DIR = path.dirname(__filename);
26
12
 
27
13
  function distPath({ subpathOut = '', subpathIn = '', extensionOut } = {}) {
28
14
  return function ({ context, absoluteFilename }) {
@@ -37,90 +23,24 @@ function distPath({ subpathOut = '', subpathIn = '', extensionOut } = {}) {
37
23
  };
38
24
  };
39
25
 
40
- const layouts = {};
41
- const CollectLayouts = {
42
- transformer: (content, path) => {
43
- // add one to dirname prefix to include separating slash
44
- const relativePath = path.slice(CWD.length + 1);
45
- layouts[relativePath] = content.toString();
46
- return layouts[relativePath];
47
- }
48
- };
49
-
50
- const SSG = {
51
- transformer: async (content, _path) => {
52
- let _meta = {};
53
-
54
- let body;
55
- try {
56
- body = _path.endsWith('.md') ? marked(content.toString()) : content.toString();
57
- } catch (err) {
58
- console.error(`Could not parse page ${_path}`, err);
59
- throw err;
60
- }
61
-
62
- // apply no layout if the document has already provided the
63
- // overarching html structure.
64
- if (!_meta.layout && body && (
65
- String(body).startsWith('<!doctype html>')
66
- || String(body).startsWith('<html'))
67
- ) {
68
- return body;
69
- }
70
-
71
- const layoutPath = path.join(
72
- SRC,
73
- 'layouts',
74
- (_meta.layout || 'default')
75
- ) + '.html';
76
-
77
- const layout = layouts[layoutPath];
78
-
79
- try {
80
- return layout;
81
- } catch (err) {
82
- console.error(`Could not parse layout ${layoutPath}`, err);
83
- throw err;
84
- }
85
- }
86
- };
87
-
88
26
  const Generated = {
89
27
  transformer: async (content, contentPath) => {
90
- useJSDOM(JSDOM);
91
-
92
28
  try {
93
29
  if (contentPath.endsWith('.js')) {
94
- const module = await import(contentPath + '?' + new Date() + Math.random());
95
- if (typeof module.generate === 'function') {
96
- const doc = await module.generate(contentPath);
97
- const doctype = doc.parentNode.doctype?.name || '';
98
-
99
- let hydrationsFound = 0;
100
- while (globalThis.pendingDehydrations?.length > 0) {
101
- globalThis.pendingDehydrations.shift()(doc);
102
- hydrationsFound++;
103
- }
104
-
105
- if (hydrationsFound) {
106
- const script = doc.parentNode.createElement('script');
107
- script.src = contentPath.substring((`${CWD}/${SRC}/ssg`).length);
108
- doc.parentNode.body.appendChild(script);
30
+ // generated in a subprocess to ensure imports are clean, especially
31
+ // when in watch mode.
32
+ const generatedContent = execSync(
33
+ `node ${SELF_DIR}/../ssg.js ${CWD}/${SRC} ${contentPath}`,
34
+ {
35
+ stdio: [ 'pipe', 'pipe', 'pipe' ]
109
36
  }
110
-
111
- return [
112
- doctype ? `<!doctype ${doctype}>\n` : '',
113
- doc.outerHTML
114
- ].join('');
115
- } else {
116
- return;
117
- }
37
+ );
38
+ return generatedContent;
118
39
  } else {
119
40
  return content.toString();
120
41
  }
121
42
  } catch (err) {
122
43
  console.error(`Could not generate page ${contentPath}`, err);
123
- throw err;
124
44
  }
125
45
  }
126
46
  };
@@ -177,25 +97,6 @@ export default (env, argv) => {
177
97
  target: 'web',
178
98
  devtool,
179
99
  plugins: [
180
-
181
- // TODO: does it make sense to actually handle static assets
182
- // first? then layouts? then everything else?
183
-
184
- // handle layouts first. other things depend on them.
185
- new CopyWebpackPlugin({
186
- patterns: [
187
- {
188
- from: `./${SRC}/layouts/**/*.html`,
189
- to: distPath({
190
- subpathIn: `${SRC}/layouts`,
191
- subpathOut: 'layouts'
192
- }),
193
- transform: CollectLayouts,
194
- noErrorOnMissing: true,
195
- },
196
- ]
197
- }),
198
-
199
100
  // now pages, etc.
200
101
  new CopyWebpackPlugin({
201
102
  patterns: [
@@ -211,111 +112,8 @@ export default (env, argv) => {
211
112
  noErrorOnMissing: true,
212
113
  priority: 5
213
114
  },
214
- // {
215
- // from: './src/routes/**/*.md',
216
- // to: distPath({ subpathIn: 'src/routes' }),
217
- // transform: SSG,
218
- // noErrorOnMissing: true,
219
- // priority: 3,
220
- // },
221
- // {
222
- // from: './src/routes/**/*.html',
223
- // to: distPath({ subpathIn: 'src/routes' }),
224
- // transform: SSG,
225
- // noErrorOnMissing: true,
226
- // priority: 3,
227
- // },
228
- // {
229
- // from: './src/routes/**/*.xml',
230
- // to: distPath({ subpathIn: 'src/routes' }),
231
- // transform: SSG,
232
- // noErrorOnMissing: true,
233
- // priority: 3,
234
- // },
235
- // {
236
- // from: './src/routes/**/*.rss',
237
- // to: distPath({ subpathIn: 'src/routes' }),
238
- // transform: SSG,
239
- // noErrorOnMissing: true,
240
- // priority: 3,
241
- // },
242
- // {
243
- // from: './src/routes/**/*.css',
244
- // to: distPath({ subpathIn: 'src/routes' }),
245
- // noErrorOnMissing: true,
246
- // // trasform: ???
247
- // priority: 3,
248
- // },
249
- // {
250
- // from: './src/routes/**/*.png',
251
- // to: distPath({ subpathIn: 'src/routes' }),
252
- // noErrorOnMissing: true,
253
- // priority: 3,
254
- // },
255
- // {
256
- // from: './src/routes/**/*.jpg',
257
- // to: distPath({ subpathIn: 'src/routes' }),
258
- // noErrorOnMissing: true,
259
- // priority: 3,
260
- // },
261
- // {
262
- // from: './src/routes/**/*.json',
263
- // to: distPath({ subpathIn: 'src/routes' }),
264
- // noErrorOnMissing: true,
265
- // priority: 3,
266
- // },
267
- // {
268
- // from: './src/routes/**/*.svg',
269
- // to: distPath({ subpathIn: 'src/routes' }),
270
- // noErrorOnMissing: true,
271
- // priority: 3,
272
- // },
273
- // {
274
- // from: './src/routes/**/*.mp3',
275
- // to: distPath({ subpathIn: 'src/routes' }),
276
- // noErrorOnMissing: true,
277
- // priority: 3,
278
- // },
279
115
  ],
280
116
  }),
281
117
  ],
282
- module: {
283
- rules: [
284
- {
285
- test: /\.css$/,
286
- use: [
287
- "style-loader",
288
- // path.resolve(__dirname, '../node_modules/style-loader'),
289
- {
290
- loader: "css-loader",
291
- // loader: path.resolve(__dirname, '../node_modules/css-loader'),
292
- options: {
293
- // don't try to require() url assets
294
- url: false
295
- }
296
- }
297
- ]
298
- },
299
- {
300
- test: /\.html$/,
301
- loader: "file-loader",
302
- // loader: path.resolve(__dirname, '../node_modules/file-loader'),
303
- options: {
304
- name: "[name].[ext]",
305
- }
306
- },
307
- {
308
- test: /\.mjs$/,
309
- resolve: {
310
- fullySpecified: false
311
- }
312
- },
313
- {
314
- test: /\.(md|tpl)$/,
315
- use: "raw-loader",
316
- // use: path.resolve(__dirname, '../node_modules/raw-loader')
317
- },
318
- ]
319
- }
320
118
  };
321
119
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wirejs-scripts",
3
- "version": "3.0.9",
3
+ "version": "3.0.11",
4
4
  "description": "Basic build and start commands for wirejs apps",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,11 +28,12 @@
28
28
  "glob": "^7.2.0",
29
29
  "jsdom": "^25.0.1",
30
30
  "marked": "^2.0.1",
31
+ "mime-types": "^2.1.35",
31
32
  "raw-loader": "^4.0.2",
32
33
  "rimraf": "^6.0.1",
33
34
  "style-loader": "^2.0.0",
34
35
  "webpack": "^5.97.1",
35
36
  "wirejs-dom": "^1.0.38",
36
- "wirejs-resources": "^0.1.11"
37
+ "wirejs-resources": "^0.1.13"
37
38
  }
38
- }
39
+ }
package/ssg.js ADDED
@@ -0,0 +1,42 @@
1
+ import process from 'process';
2
+ import { JSDOM } from 'jsdom';
3
+ import { useJSDOM } from 'wirejs-dom/v2';
4
+
5
+ const SRC_DIR = process.argv[2];
6
+
7
+ async function build(filename) {
8
+ useJSDOM(JSDOM);
9
+
10
+ try {
11
+ const module = await import(filename);
12
+
13
+ if (typeof module.generate === 'function') {
14
+ const doc = await module.generate(filename);
15
+ const doctype = doc.parentNode.doctype?.name || '';
16
+
17
+ let hydrationsFound = 0;
18
+ while (globalThis.pendingDehydrations?.length > 0) {
19
+ globalThis.pendingDehydrations.shift()(doc);
20
+ hydrationsFound++;
21
+ }
22
+
23
+ if (hydrationsFound) {
24
+ const script = doc.parentNode.createElement('script');
25
+ script.src = filename.substring((`${SRC_DIR}/ssg`).length);
26
+ doc.parentNode.body.appendChild(script);
27
+ }
28
+
29
+ return [
30
+ doctype ? `<!doctype ${doctype}>\n` : '',
31
+ doc.outerHTML
32
+ ].join('');
33
+ } else {
34
+ return;
35
+ }
36
+ } catch (err) {
37
+ console.error(`Could not generate page ${filename}`, err);
38
+ process.exit(1);
39
+ }
40
+ }
41
+
42
+ console.log(await build(process.argv[3]));