primate 0.15.3 → 0.15.5

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,10 +1,15 @@
1
1
  {
2
2
  "name": "primate",
3
- "version": "0.15.3",
3
+ "version": "0.15.5",
4
4
  "description": "Expressive, minimal and extensible framework for JavaScript",
5
5
  "homepage": "https://primatejs.com",
6
6
  "bugs": "https://github.com/primatejs/primate/issues",
7
7
  "license": "MIT",
8
+ "files": [
9
+ "src/**",
10
+ "!src/**/*.spec.js",
11
+ "!readme/**"
12
+ ],
8
13
  "bin": "src/bin.js",
9
14
  "repository": "https://github.com/primatejs/primate",
10
15
  "scripts": {
@@ -18,9 +23,6 @@
18
23
  "devDependencies": {
19
24
  "maximin": "^0.1.2"
20
25
  },
21
- "engines": {
22
- "node": ">=17.9.0"
23
- },
24
26
  "type": "module",
25
27
  "exports": "./src/exports.js"
26
28
  }
package/src/Logger.js ADDED
@@ -0,0 +1,98 @@
1
+ import {assert, is} from "runtime-compat/dyndef";
2
+
3
+ const colors = {
4
+ black: msg => `\x1b[0m${msg}\x1b[0m`,
5
+ bold: msg => `\x1b[1m${msg}\x1b[0m`,
6
+ red: msg => `\x1b[31m${msg}\x1b[0m`,
7
+ green: msg => `\x1b[32m${msg}\x1b[0m`,
8
+ yellow: msg => `\x1b[33m${msg}\x1b[0m`,
9
+ blue: msg => `\x1b[34m${msg}\x1b[0m`,
10
+ gray: msg => `\x1b[2m${msg}\x1b[0m`,
11
+ };
12
+
13
+ const error = 0;
14
+ const warn = 1;
15
+ const info = 2;
16
+
17
+ // Error natively provided
18
+ const Warn = class Warn extends Error {};
19
+ const Info = class Info extends Error {};
20
+
21
+ const levels = new Map([
22
+ [Error, error],
23
+ [Warn, warn],
24
+ [Info, info],
25
+ ]);
26
+
27
+ const print = (...messages) => process.stdout.write(messages.join(" "));
28
+
29
+ const Logger = class Logger {
30
+ #level; #traceStack;
31
+
32
+ constructor({level = Error, traceStack = false} = {}) {
33
+ assert(level !== undefined && levels.get(level) <= info);
34
+ is(traceStack).boolean();
35
+ this.#level = level;
36
+ this.#traceStack = traceStack;
37
+ }
38
+
39
+ static get Error() {
40
+ return Error;
41
+ }
42
+
43
+ static get Warn() {
44
+ return Warn;
45
+ }
46
+
47
+ static get Info() {
48
+ return Info;
49
+ }
50
+
51
+ #print(pre, error) {
52
+ if (error instanceof Error) {
53
+ print(colors.bold(pre), error.message, "\n");
54
+ if (this.#traceStack) {
55
+ console.log(error);
56
+ }
57
+ } else {
58
+ print(colors.bold(pre), error, "\n");
59
+ }
60
+ }
61
+
62
+ get level() {
63
+ return levels.get(this.#level);
64
+ }
65
+
66
+ info(message) {
67
+ if (this.level >= levels.get(Info)) {
68
+ this.#print(colors.green("--"), message);
69
+ }
70
+ }
71
+
72
+ warn(message) {
73
+ if (this.level >= levels.get(Warn)) {
74
+ this.#print(colors.yellow("??"), message);
75
+ }
76
+ }
77
+
78
+ error(message) {
79
+ if (this.level >= levels.get(Error)) {
80
+ this.#print(colors.red("!!"), message);
81
+ }
82
+ }
83
+
84
+ auto(message) {
85
+ if (message instanceof Info) {
86
+ return this.info(message.message);
87
+ }
88
+ if (message instanceof Warn) {
89
+ return this.warn(message.message);
90
+ }
91
+
92
+ return this.error(message);
93
+ }
94
+ };
95
+
96
+ export default Logger;
97
+
98
+ export {colors, levels, print};
package/src/bin.js CHANGED
@@ -1,8 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  import args from "runtime-compat/args";
3
- import * as commands from "./commands/exports.js";
4
3
  import run from "./run.js";
5
4
 
6
- const command = name => commands[name] ?? commands.help;
7
-
8
- await run(args[0] === undefined ? commands.dev : command(args[0]));
5
+ await run(args[0]);
@@ -1,7 +1,6 @@
1
1
  import {Path} from "runtime-compat/fs";
2
- import package_json from "../../package.json" assert {type: "json"};
3
2
 
4
- const createModule = async () => {
3
+ const createModule = async env => {
5
4
  const space = 2;
6
5
  try {
7
6
  // will throw if cannot find a package.json up the filesystem hierarchy
@@ -11,7 +10,7 @@ const createModule = async () => {
11
10
  name: "primate-app",
12
11
  private: true,
13
12
  dependencies: {
14
- primate: `^${package_json.version}`,
13
+ primate: `^${env.version}`,
15
14
  },
16
15
  scripts: {
17
16
  start: "npx primate",
@@ -37,7 +36,7 @@ const createConfig = async env => {
37
36
  };
38
37
 
39
38
  export default async env => {
40
- await createModule();
39
+ await createModule(env);
41
40
  await createConfig(env);
42
41
  };
43
42
 
@@ -1,16 +1,3 @@
1
- import register from "../register.js";
2
- import compile from "../compile.js";
3
- import publish from "../publish.js";
4
- import route from "../route.js";
5
- import serve from "../serve.js";
1
+ import start from "../start.js";
6
2
 
7
- export default async env => {
8
- // register handlers
9
- await register(env);
10
- // compile server-side code
11
- await compile(env);
12
- // publish client-side code
13
- await publish(env);
14
- // serve
15
- serve({router: await route(env), ...env});
16
- };
3
+ export default async env => start(env);
@@ -1,4 +1,10 @@
1
- export {default as dev} from "./dev.js";
2
- export {default as serve} from "./serve.js";
3
- export {default as create} from "./create.js";
4
- export {default as help} from "./help.js";
1
+ import {default as dev} from "./dev.js";
2
+ import {default as serve} from "./serve.js";
3
+ import {default as create} from "./create.js";
4
+ import {default as help} from "./help.js";
5
+
6
+ const commands = {dev, serve, create, help};
7
+
8
+ const run = name => commands[name] ?? help;
9
+
10
+ export default name => name === undefined ? dev : run(name);
@@ -1,3 +1,3 @@
1
1
  export default env => {
2
- env.log.info("available commands: create start");
2
+ env.log.info("available commands: create dev serve");
3
3
  };
@@ -1,19 +1,3 @@
1
- import register from "../register.js";
2
- import compile from "../compile.js";
3
- import publish from "../publish.js";
4
- import bundle from "../bundle.js";
5
- import route from "../route.js";
6
- import serve from "../serve.js";
1
+ import start from "../start.js";
7
2
 
8
- export default async env => {
9
- // register handlers
10
- await register(env);
11
- // compile server-side code
12
- await compile(env);
13
- // publish client-side code
14
- await publish(env);
15
- // bundle client-side code
16
- await bundle(env);
17
- // serve
18
- serve({router: await route(env), ...env});
19
- };
3
+ export default async env => start(env, {bundle: true});
package/src/config.js CHANGED
@@ -4,9 +4,8 @@ import {File, Path} from "runtime-compat/fs";
4
4
  import cache from "./cache.js";
5
5
  import extend from "./extend.js";
6
6
  import defaults from "./primate.config.js";
7
- import * as log from "./log.js";
7
+ import {colors, print, default as Logger} from "./Logger.js";
8
8
  import * as handlers from "./handlers/exports.js";
9
- import package_json from "../package.json" assert {type: "json"};
10
9
 
11
10
  const qualify = (root, paths) =>
12
11
  Object.keys(paths).reduce((sofar, key) => {
@@ -58,41 +57,54 @@ export default async (filename = "primate.config.js") => {
58
57
  const root = await getRoot();
59
58
  const config = await getConfig(root, filename);
60
59
 
61
- const resources = [];
60
+ const {name, version} = JSON.parse(await new Path(import.meta.url)
61
+ .directory.directory.join("package.json").file.read());
62
+
62
63
  const env = {
63
64
  ...config,
65
+ name, version,
66
+ resources: [],
67
+ entrypoints: [],
64
68
  paths: qualify(root, config.paths),
65
69
  root,
66
- log: {...log, error: error => log.error(error, config)},
70
+ log: new Logger(config.logger),
67
71
  register: (name, handler) => {
68
72
  env.handlers[name] = handler;
69
73
  },
70
74
  handlers: {...handlers},
71
75
  render: async ({body = "", head = ""} = {}) => {
72
76
  const html = await index(env);
73
- const heads = resources.map(({src, code, type, inline, integrity}) => {
74
- const tag = "script";
75
- const pre = `<${tag} type="${type}" integrity="${integrity}"`;
76
- const post = `</${tag}>`;
77
- return inline ? `${pre}>${code}${post}` : `${pre} src="${src}">${post}`;
77
+ const heads = env.resources.map(({src, code, type, inline, integrity}) => {
78
+ const tag = type === "style" ? "link" : "script";
79
+ const pre = type === "style"
80
+ ? `<${tag} rel="stylesheet" integrity="${integrity}"`
81
+ : `<${tag} type="${type}" integrity="${integrity}"`;
82
+ const middle = type === "style"
83
+ ? ` href="${src}">`
84
+ : ` src="${src}">`;
85
+ const post = type === "style" ? "" : `</${tag}>`;
86
+ return inline ? `${pre}>${code}${post}` : `${pre}${middle}${post}`;
78
87
  }).join("\n");
79
88
  return html
80
89
  .replace("%body%", () => body)
81
90
  .replace("%head%", () => `${head}${heads}`);
82
91
  },
83
- publish: async ({src, code, type = "", inline = false, main}) => {
92
+ publish: async ({src, code, type = "", inline = false}) => {
84
93
  const integrity = await hash(code);
85
- resources.push({src, code, type, inline, integrity, main});
94
+ env.resources.push({src, code, type, inline, integrity});
86
95
  return integrity;
87
96
  },
97
+ bootstrap: ({type, code}) => {
98
+ env.entrypoints.push({type, code});
99
+ },
88
100
  };
89
- env.log.info(`${package_json.name} \x1b[34m${package_json.version}\x1b[0m`);
101
+ print(colors.blue(colors.bold(name)), colors.blue(version), "");
90
102
  const {modules} = config;
91
103
  // modules may load other modules
92
104
  const loads = await Promise.all(modules
93
105
  .filter(module => module.load !== undefined)
94
106
  .map(module => module.load()));
95
107
 
96
- return cache("config", filename, () => ({...env, resources,
108
+ return cache("config", filename, () => ({...env,
97
109
  modules: modules.concat(loads.flat())}));
98
110
  };
@@ -0,0 +1 @@
1
+ export default class InfoError extends Error {}
package/src/exports.js CHANGED
@@ -1,5 +1,5 @@
1
- import {serve} from "./commands/exports.js";
2
1
  import run from "./run.js";
3
2
 
4
3
  export * from "./handlers/exports.js";
5
- export default () => run(serve);
4
+ export {default as Logger} from "./Logger.js";
5
+ export default command => run(command);
@@ -1,6 +1,10 @@
1
+ import {Logger} from "primate";
2
+
1
3
  export default {
2
4
  base: "/",
3
- debug: false,
5
+ logger: {
6
+ level: Logger.Warn,
7
+ },
4
8
  http: {
5
9
  host: "localhost",
6
10
  port: 6161,
@@ -19,4 +23,5 @@ export default {
19
23
  components: "components",
20
24
  },
21
25
  modules: [],
26
+ dist: "app",
22
27
  };
package/src/route.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import {Path} from "runtime-compat/fs";
2
- import RouteError from "./errors/Route.js";
2
+ import Logger from "./Logger.js";
3
3
 
4
4
  // insensitive-case equal
5
5
  const ieq = (left, right) => left.toLowerCase() === right.toLowerCase();
@@ -23,7 +23,7 @@ export default async env => {
23
23
  const {pathname, searchParams} = url;
24
24
  const params = Object.fromEntries(searchParams);
25
25
  const verb = find(method, pathname, {handler: () => {
26
- throw new RouteError(`no ${method.toUpperCase()} route to ${pathname}`);
26
+ throw new Logger.Info(`no ${method.toUpperCase()} route to ${pathname}`);
27
27
  }});
28
28
  const path = pathname.split("/").filter(part => part !== "");
29
29
  const named = verb.path?.exec(pathname)?.groups ?? {};
package/src/run.js CHANGED
@@ -1,6 +1,4 @@
1
1
  import config from "./config.js";
2
+ import command from "./commands/exports.js";
2
3
 
3
- export default async command => {
4
- // env should initialised before any commands run
5
- await command(await config());
6
- };
4
+ export default async name => command(name)(await config());
package/src/serve.js CHANGED
@@ -5,6 +5,7 @@ import mimes from "./mimes.js";
5
5
  import {http404} from "./handlers/http.js";
6
6
  import {isResponse} from "./duck.js";
7
7
  import respond from "./respond.js";
8
+ import {colors, print} from "./Logger.js";
8
9
 
9
10
  const regex = /\.([a-z1-9]*)$/u;
10
11
  const mime = filename => mimes[filename.match(regex)[1]] ?? mimes.binary;
@@ -46,7 +47,7 @@ export default env => {
46
47
  input => handler(input, acc));
47
48
  return await respond(await handlers({request, env}))(env, headers);
48
49
  } catch (error) {
49
- env.log.error(error);
50
+ env.log.auto(error);
50
51
  return http404()(env, headers);
51
52
  }
52
53
  };
@@ -89,7 +90,7 @@ export default env => {
89
90
  try {
90
91
  return await _serve(request);
91
92
  } catch (error) {
92
- env.log.error(error);
93
+ env.log.auto(error);
93
94
  return new Response(null, {status: statuses.InternalServerError});
94
95
  }
95
96
  };
@@ -136,5 +137,5 @@ export default env => {
136
137
  return handlers({original: request, pathname: pathname + search, body});
137
138
  }, http);
138
139
 
139
- env.log.info(`running on ${http.host}:${http.port}`);
140
+ print(colors.gray(`at http://${http.host}:${http.port}`), "\n");
140
141
  };
package/src/start.js ADDED
@@ -0,0 +1,27 @@
1
+ import register from "./register.js";
2
+ import compile from "./compile.js";
3
+ import publish from "./publish.js";
4
+ import bundle from "./bundle.js";
5
+ import route from "./route.js";
6
+ import serve from "./serve.js";
7
+
8
+ export default async (env, operations = {}) => {
9
+ // register handlers
10
+ await register(env);
11
+ // compile server-side code
12
+ await compile(env);
13
+ // publish client-side code
14
+ await publish(env);
15
+
16
+ // after publish hook, publish a zero assumptions app.js (no css imports)
17
+ const code = env.entrypoints.filter(({type}) => type === "script")
18
+ .map(({code}) => code).join("");
19
+ await env.publish({src: `${env.dist}.js`, code, type: "module"});
20
+
21
+ if (operations?.bundle) {
22
+ // bundle client-side code
23
+ await bundle(env);
24
+ }
25
+ // serve
26
+ serve({router: await route(env), ...env});
27
+ };
package/src/log.js DELETED
@@ -1,29 +0,0 @@
1
- const colors = {
2
- red: 31,
3
- green: 32,
4
- yellow: 33,
5
- blue: 34,
6
- };
7
- const reset = 0;
8
-
9
- const Log = {
10
- paint: (color, message) => {
11
- process.stdout.write(`\x1b[${color}m${message}\x1b[0m`);
12
- return log;
13
- },
14
- nl: () => log.paint(reset, "\n"),
15
- };
16
-
17
- const log = new Proxy(Log, {
18
- get: (target, property) => target[property] ?? (message =>
19
- log.paint(colors[property] ?? reset, message).paint(reset, " ")),
20
- });
21
-
22
- export const info = (...args) => log.green("[info]").reset(...args).nl();
23
-
24
- export const warn = (...args) => log.yellow("[warn]").reset(...args).nl();
25
-
26
- export const error = (originalError, env) => {
27
- log.red("[error]").reset(originalError.message).nl();
28
- env.debug && console.log(originalError);
29
- };