@jnode/server 2.0.1 → 2.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.
package/README.md CHANGED
@@ -264,10 +264,11 @@ We provide two methods to use them:
264
264
  - `<METHOD>/<path_segment>[/<path_segment>...]` [router](#class-serverrouter) | [handler-extended](#handler-extended) Used when the method matches; equals to `'/<path_segment>': r.Method({ '<METHOD>': <value> })`. E.G., `'GET/meow': 'Meow!'` (only works for HTTP method `'GET'`).
265
265
  - `@<METHOD>/<path_segment>[/<path_segment>...]` [router](#class-serverrouter) | [handler-extended](#handler-extended) Used when both the path resolver ends and the method matches; equals to `'/<path_segment>': r.Path(r.Method({ '<METHOD>': <value> }))`. E.G., `'@GET/meow': 'Meow!'` (only works for path `'/meow'` but not `'/meow/something'` and request method is `'GET'`).
266
266
  - `*` [router](#class-serverrouter) | [handler-extended](#handler-extended) Any path segment. E.G. `'*': h.Text('Meow? Nothing here!', { statusCode: 404 })`.
267
+ - `/%:<path_parameter_name>` [router](#class-serverrouter) | [handler-extended](#handler-extended) Match any segment (if exists) and save the segment to `ctx.params` by `<path_parameter_name>`. Do the similar thing as [`PathArgRouter`](#router-pathargrouterparamname-next).
267
268
 
268
269
  `PathRouter` is probably the most important router; almost every server needs it!
269
270
 
270
- By the way, we **DO NOT** support `'/*'` as a universal matching character, don't try this.
271
+ By the way, if you’re looking for a universal matching character, use `'/%:'` instead of `'/*'` (this will match to `'*'` in a literal sense).
271
272
 
272
273
  #### How it works?
273
274
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jnode/server",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Simple web server package for Node.js.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/handlers.js CHANGED
@@ -54,7 +54,7 @@ class DataHandler {
54
54
  // file handler: local file
55
55
  class FileHandler {
56
56
  constructor(file, options = {}) {
57
- this.file = path.resolve(this.file);
57
+ this.file = path.resolve(file);
58
58
  this.options = options;
59
59
 
60
60
  // range may be disabled by `options.disableRange` or when `statusCode` is set to non-200 value
package/src/routers.js CHANGED
@@ -29,20 +29,30 @@ class PathRouter {
29
29
  // key format: '[@ (path end check)][METHOD (method check)]/path/segments'
30
30
  // example: '@ GET /cat/names', 'POST /cats', '/api'
31
31
  const routeEnd = key.startsWith('@');
32
- const routeMethod = key.substring(routeEnd ? 1 : 0, firstSlashIndex).trim().toUpperCase();
33
- const routePath = key.substring(firstSlashIndex).split('/').slice(1).map(decodeURIComponent);
32
+ const routeMethod = key.substring(routeEnd ? 1 : 0, firstSlashIndex).trim();
33
+ const routePath = key.substring(firstSlashIndex).split('/').slice(1);
34
34
 
35
35
  // expand map
36
36
  let current = this.map;
37
- for (const segment of routePath) {
38
- if (!current['/' + segment]) current['/' + segment] = {};
39
- current = current['/' + segment];
37
+ let args = [];
38
+ for (let segment of routePath) {
39
+ if (segment.startsWith('%:')) {
40
+ args.push(segment.substring(2));
41
+ if (!current[':']) current[':'] = {};
42
+ current = current[':'];
43
+ continue;
44
+ }
45
+
46
+ segment = '/' + decodeURIComponent(segment);
47
+ if (!current[segment]) current[segment] = {};
48
+ current = current[segment];
40
49
  }
41
50
 
42
51
  // '*' for non-end check, '@' for end check
43
52
  // '*' for any method, 'METHOD' for specific method
44
53
  if (!current[routeEnd ? '@' : '*']) current[routeEnd ? '@' : '*'] = {};
45
54
  current[routeEnd ? '@' : '*'][routeMethod || '*'] = value;
55
+ if (args.length > 0) current[routeEnd ? '@' : '*']['::' + (routeMethod || '*')] = args;
46
56
  }
47
57
  }
48
58
 
@@ -52,14 +62,23 @@ class PathRouter {
52
62
  let result = this.map['*'];
53
63
  let resultPointer = env.pathPointer;
54
64
  let current = this.map;
65
+ let resultArgs = [];
66
+ let currentArgs = [];
67
+ let resultArgNames = [];
55
68
  while (env.pathPointer < env.path.length) {
56
- const segment = '/' + env.path[env.pathPointer];
57
- if (!current[segment]) break;
69
+ let segment = '/' + env.path[env.pathPointer];
70
+ if (!current[segment] && !current[':']) break;
71
+ if (!current[segment]) {
72
+ segment = ':';
73
+ currentArgs.push(env.path[env.pathPointer]);
74
+ }
58
75
 
59
76
  // prepare fallback
60
77
  if (current[segment]['*']?.['*'] || current[segment]['*']?.[ctx.method]) {
61
78
  result = current[segment]['*'][ctx.method] ?? current[segment]['*']['*'];
62
79
  resultPointer = env.pathPointer + 1;
80
+ resultArgs = [...currentArgs];
81
+ resultArgNames = current[segment]['*']['::' + ctx.method] ?? current[segment]['*']['::*'];
63
82
  }
64
83
 
65
84
  current = current[segment];
@@ -69,10 +88,15 @@ class PathRouter {
69
88
  if (env.pathPointer >= env.path.length && (current['@']?.['*'] || current['@']?.[ctx.method])) {
70
89
  result = current['@'][ctx.method] ?? current['@']['*'];
71
90
  resultPointer = env.pathPointer;
91
+ resultArgs = currentArgs;
92
+ resultArgNames = current['@']['::' + ctx.method] ?? current['@']['::*'];
72
93
  }
73
94
  }
74
95
 
75
96
  env.pathPointer = resultPointer;
97
+ if (resultArgNames) resultArgs.forEach((arg, index) => {
98
+ ctx.params[resultArgNames[index]] = arg;
99
+ });
76
100
  return result;
77
101
  }
78
102
  }