@kirana-style/compiler-browser 0.0.0 → 0.3.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.
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kirana-style/compiler-browser",
3
- "version": "0.0.0",
4
- "description": "Placeholder: the Kirana engine in a browser page, WebAssembly with an in-memory project, ships with Kirana. https://kirana.style/docs/project/ecosystem",
3
+ "version": "0.3.5",
4
+ "description": "The Kirana engine in a browser: WebAssembly with an in-memory project, for playgrounds and editors on the web.",
5
5
  "license": "MIT",
6
6
  "author": "The Kirana authors",
7
7
  "homepage": "https://kirana.style",
@@ -10,11 +10,38 @@
10
10
  "url": "git+https://github.com/kirana-style/kirana.git",
11
11
  "directory": "packages/kirana/npm/compiler-browser"
12
12
  },
13
+ "bugs": {
14
+ "url": "https://github.com/kirana-style/kirana/issues"
15
+ },
13
16
  "type": "module",
17
+ "sideEffects": false,
14
18
  "exports": {
15
- ".": "./index.js",
16
- "./runtime": "./index.js",
19
+ ".": {
20
+ "types": "./types/index.d.ts",
21
+ "default": "./compiler.js"
22
+ },
23
+ "./runtime": {
24
+ "types": "./types/runtime.d.ts",
25
+ "default": "./runtime.js"
26
+ },
17
27
  "./package.json": "./package.json"
18
28
  },
19
- "files": ["index.js", "README.md", "LICENSE"]
29
+ "files": [
30
+ "compiler.js",
31
+ "runtime.js",
32
+ "wasi-worker-browser.mjs",
33
+ "kirana.wasm32-wasi.wasm",
34
+ "*.js",
35
+ "types",
36
+ "CHANGELOG.md",
37
+ "LICENSE"
38
+ ],
39
+ "peerDependencies": {
40
+ "@kirana-style/core": "0.3.5"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "@kirana-style/core": {
44
+ "optional": true
45
+ }
46
+ }
20
47
  }
package/runtime.js ADDED
@@ -0,0 +1,142 @@
1
+ // ../core/src/internal.ts
2
+ /**
3
+ * The private runtime (KRN-DYN-005, D-018). The compiler imports it; applications never do.
4
+ *
5
+ * A compiled handle is a class string, or `{ c, v }` when it carries bridged custom properties.
6
+ * Kirana class names are `{prefix}{key}_{value}`: merging keeps the last class per key, a
7
+ * `{prefix}{key}_` tombstone removes one key, and `{prefix}{property}*_` removes every key of a
8
+ * property, and `{prefix}{shorthand}/{codes}{scope}_` unfolds a shorthand atom into longhands.
9
+ * Classes without the prefix pass through in order.
10
+ */
11
+ /** A base-62 property code (`0-9a-zA-Z`) for an index, as class names spell them (D-037). */
12
+ const code = (index) => (index > 61 ? code(index / 62 | 0) : "") + String.fromCharCode((index %= 62) < 10 ? index + 48 : index < 36 ? index + 87 : index + 29);
13
+ /**
14
+ * `key` is `base`, or — for every condition set — `base` plus `-` and a condition segment: a
15
+ * 4-character hash, or a 1–2 character code in dense names. A 3-character segment is a target, and
16
+ * a second `-` starts another segment, so neither is a condition of `base`.
17
+ */
18
+ const scoped = (key, base, every) => key === base || every && key.startsWith(base + "-") && !/-|^...$/.test(key.slice(base.length + 1));
19
+ function runtime(prefix, attribute) {
20
+ const add = (merged, handle) => {
21
+ if (!handle) return;
22
+ if (Array.isArray(handle)) {
23
+ for (const inner of handle) add(merged, inner);
24
+ return;
25
+ }
26
+ if (typeof handle === "object") {
27
+ Object.assign(merged.v, handle.v);
28
+ handle = handle.c;
29
+ }
30
+ let classes = merged.c;
31
+ for (const token of handle.split(" ")) {
32
+ if (!token) continue;
33
+ const end = token.lastIndexOf("_");
34
+ if (!token.startsWith(prefix) || end < 0) {
35
+ classes.set(" " + token, token);
36
+ continue;
37
+ }
38
+ const key = token.slice(prefix.length, end);
39
+ const head = key.split("-")[0];
40
+ if (!head[2]) {
41
+ let index = 0;
42
+ for (const digit of head) {
43
+ const unit = digit.charCodeAt(0);
44
+ index = index * 62 + unit - (unit > 96 ? 87 : unit > 64 ? 29 : 48);
45
+ }
46
+ let size = index < 45 ? index % 5 ? 0 : 4 : index < 111 && !((index - 45) % 3) ? 2 : 0;
47
+ while (size) classes.delete(code(index + size--) + key.slice(head.length));
48
+ }
49
+ const slash = key.indexOf("/");
50
+ if (slash > 0) {
51
+ const every = key.endsWith("*");
52
+ const tail = key.slice(slash + 1, every ? -1 : void 0);
53
+ const dash = tail.indexOf("-") >>> 0;
54
+ const shorthand = key.slice(0, slash);
55
+ const earlier = classes;
56
+ merged.c = classes = /* @__PURE__ */ new Map();
57
+ for (const [existing, atom] of earlier) if (!atom.endsWith("_") && scoped(existing, shorthand + tail.slice(dash), every)) {
58
+ const rest = existing.slice(shorthand.length);
59
+ for (let kept of tail.slice(0, dash).split(".")) if (classes.delete(kept += rest) || !earlier.has(kept)) classes.set(kept, prefix + kept + atom.slice(atom.lastIndexOf("_")));
60
+ } else classes.set(existing, atom);
61
+ } else if (key.endsWith("*")) {
62
+ for (const existing of classes.keys()) if (scoped(existing, key.slice(0, -1), true) && !existing.endsWith("*")) classes.delete(existing);
63
+ }
64
+ classes.delete(key);
65
+ classes.set(key, token);
66
+ }
67
+ };
68
+ const merge = (handles) => {
69
+ const merged = {
70
+ c: /* @__PURE__ */ new Map(),
71
+ v: {}
72
+ };
73
+ add(merged, handles);
74
+ return merged;
75
+ };
76
+ /** Merges handles into one handle. */
77
+ const cx = (...handles) => {
78
+ const merged = merge(handles);
79
+ const c = [...merged.c.values()].join(" ");
80
+ return Object.keys(merged.v).length ? {
81
+ c,
82
+ v: merged.v
83
+ } : c;
84
+ };
85
+ /**
86
+ * Props already merged, by their inputs' text. An element renders the same few combinations over
87
+ * and over (a table cell with no `sx` of its own is one), so each is merged once and shared, with
88
+ * its `className`, by every element that renders it; past a bound the cache starts over, so text
89
+ * built at runtime cannot grow it without end.
90
+ */
91
+ const settled = /* @__PURE__ */ new Map();
92
+ /** The props for a host element: foreign classes first, then handles; bridged variables last. */
93
+ const sx = (handles, classNames, styles) => {
94
+ const key = JSON.stringify([
95
+ handles,
96
+ classNames,
97
+ styles
98
+ ]);
99
+ let props = settled.get(key);
100
+ if (!props) {
101
+ const merged = merge([classNames.filter((name) => typeof name === "string"), handles]);
102
+ const className = [...merged.c.values()].filter((token) => !token.endsWith("_")).join(" ");
103
+ const style = Object.assign({}, ...styles.filter((value) => value && typeof value === "object"), merged.v);
104
+ props = {
105
+ ...className ? { [attribute]: className } : {},
106
+ ...Object.keys(style).length ? { style } : {}
107
+ };
108
+ if (settled.size > 4095) settled.clear();
109
+ settled.set(key, props);
110
+ }
111
+ return props.style ? {
112
+ ...props,
113
+ style: { ...props.style }
114
+ } : { ...props };
115
+ };
116
+ /** A bridged value: `null` and `undefined` become `initial` so the declaration falls back. */
117
+ const b = (c, variable, value) => ({
118
+ c,
119
+ v: { [variable]: value == null ? "initial" : String(value) }
120
+ });
121
+ /** A recipe resolved at runtime from its compiled table. */
122
+ const recipe = (table) => (options) => {
123
+ const pick = (group) => {
124
+ const value = options?.[group] ?? table.d[group];
125
+ return value == null ? void 0 : String(value);
126
+ };
127
+ const slots = (table.s ?? [""]).map((_, index) => {
128
+ const parts = [table.b[index]];
129
+ for (const group in table.v) parts.push(table.v[group][pick(group)]?.[index]);
130
+ for (const [selection, handles] of table.c) if (Object.keys(selection).every((group) => pick(group) === selection[group])) parts.push(handles[index]);
131
+ return cx(parts);
132
+ });
133
+ return table.s ? Object.fromEntries(table.s.map((name, index) => [name, slots[index]])) : slots[0];
134
+ };
135
+ return {
136
+ cx,
137
+ sx,
138
+ b,
139
+ recipe
140
+ };
141
+ }
142
+ export { runtime };
@@ -0,0 +1,21 @@
1
+ import type { Project, ProjectOptions } from "./project.js"
2
+
3
+ export type {
4
+ Diagnostic,
5
+ Position,
6
+ Project,
7
+ ProjectOptions,
8
+ Transformed,
9
+ } from "./project.js"
10
+ export { positionOf } from "./project.js"
11
+ import type { KiranaConfig } from "@kirana-style/core/config"
12
+ /** The engine's JSON for a configuration (KRN-INT-001). */
13
+ export declare function configJson(
14
+ root: string,
15
+ config: KiranaConfig,
16
+ inline: KiranaConfig,
17
+ options?: { jsx?: "preact" | "solid" | "qwik" }
18
+ ): string
19
+
20
+ /** Opens a project in the engine's in-memory file system. */
21
+ export declare function createProject(options?: ProjectOptions): Project
@@ -0,0 +1,95 @@
1
+ import { KiranaConfig } from "@kirana-style/core/config";
2
+ /** The file system calls a project makes: memfs's volume in a browser, `node:fs` in the tests. */
3
+ export type Volume = {
4
+ mkdirSync: (path: string, options: {
5
+ recursive: true;
6
+ }) => unknown;
7
+ writeFileSync: (path: string, data: string) => void;
8
+ rmSync: (path: string, options: {
9
+ force: true;
10
+ }) => void;
11
+ };
12
+ /** The engine's compilation session, as the binding gives it. */
13
+ export type Session = {
14
+ transform: (id: string, code: string) => {
15
+ code?: string;
16
+ map?: string;
17
+ diagnostics: string;
18
+ cssChanged: boolean;
19
+ dependencies: string[];
20
+ };
21
+ stylesheet: (minify: boolean) => string;
22
+ diagnostics: () => string;
23
+ invalidate: (id: string) => string[];
24
+ remove: (id: string) => boolean;
25
+ };
26
+ /** What `openProject` needs from the engine. */
27
+ export type Engine = {
28
+ Compiler: new (configJson: string) => Session;
29
+ __volume: Volume;
30
+ };
31
+ /** A place in a file: line and column from 1, the column in UTF-16 code units as editors count. */
32
+ export type Position = {
33
+ line: number;
34
+ column: number;
35
+ };
36
+ /** A diagnostic, with its span as positions in the file it names as well as the engine's bytes. */
37
+ export type Diagnostic = {
38
+ code: string;
39
+ name: string;
40
+ severity: "info" | "warning" | "error";
41
+ /** The file, relative to the project root; empty for the project as a whole. */
42
+ file: string;
43
+ message: string;
44
+ help: string[];
45
+ /** UTF-8 byte offsets, as the engine counts. */
46
+ span: {
47
+ start: number;
48
+ end: number;
49
+ };
50
+ /** Where the span starts and ends; undefined when the diagnostic names no file of the project. */
51
+ start?: Position;
52
+ end?: Position;
53
+ };
54
+ export type Transformed = {
55
+ /** The rewritten module, still TypeScript and JSX; undefined when the module has no styles. */
56
+ code?: string;
57
+ map?: string;
58
+ diagnostics: Diagnostic[];
59
+ /** Whether the stylesheet changed. */
60
+ cssChanged: boolean;
61
+ /** Files the module read while it compiled, relative to the root. */
62
+ dependencies: string[];
63
+ };
64
+ export type ProjectOptions = {
65
+ /** The configuration, already evaluated: what `kirana.config.ts`'s default export holds. */
66
+ config?: KiranaConfig;
67
+ /** Where the project lives in the engine's file system. Default `/app`. */
68
+ root?: string;
69
+ /** Files to write first, by path relative to the root. */
70
+ files?: Readonly<Record<string, string>>;
71
+ };
72
+ export type Project = {
73
+ readonly root: string;
74
+ /** The configuration as the engine received it. */
75
+ readonly configJson: string;
76
+ /**
77
+ * Writes a file (relative to the root) and invalidates what read it: the files returned, relative
78
+ * to the root, need transforming again.
79
+ */
80
+ write: (file: string, code: string) => string[];
81
+ /** Removes a file, and what it contributed to the stylesheet. */
82
+ remove: (file: string) => void;
83
+ /** Compiles a file written before. */
84
+ transform: (file: string) => Transformed;
85
+ /** The stylesheet of every module transformed so far. */
86
+ stylesheet: (minify?: boolean) => string;
87
+ /** Diagnostics about the project as a whole (token and relation checks). */
88
+ diagnostics: () => Diagnostic[];
89
+ /** The files written, relative to the root, in the order they were first written. */
90
+ files: () => string[];
91
+ };
92
+ /** The position of a UTF-8 byte offset in `source`. */
93
+ export declare function positionOf(source: string, offset: number): Position;
94
+ /** Opens a project on `engine`: the configuration becomes the engine's JSON, the files are written. */
95
+ export declare function openProject(engine: Engine, { config, root, files }?: ProjectOptions): Project;
@@ -0,0 +1 @@
1
+ export { runtime } from "@kirana-style/core/internal"
@@ -0,0 +1,44 @@
1
+ import { a as instantiateNapiModuleSync, c as WASI, l as asyncWork, o as createFsProxy, r as MessageHandler, t as import_lib, u as threadsafeFunction } from "./fs.js";
2
+ // ../../dist-packages/stage/compiler-browser/engine/wasi-worker-browser.mjs
3
+ const fs = createFsProxy(import_lib);
4
+ const handler = new MessageHandler({ onLoad({ wasmModule, wasmMemory }) {
5
+ const wasi = new WASI({
6
+ fs,
7
+ preopens: { "/": "/" },
8
+ print: function() {
9
+ console.log.apply(console, arguments);
10
+ },
11
+ printErr: function() {
12
+ console.error.apply(console, arguments);
13
+ }
14
+ });
15
+ return instantiateNapiModuleSync(wasmModule, {
16
+ childThread: true,
17
+ wasi,
18
+ plugins: [asyncWork, threadsafeFunction],
19
+ overwriteImports(importObject) {
20
+ importObject.env = {
21
+ ...importObject.env,
22
+ ...importObject.napi,
23
+ ...importObject.emnapi,
24
+ memory: wasmMemory
25
+ };
26
+ }
27
+ });
28
+ } });
29
+ let __addonCrashFlag;
30
+ const __beforeReportError = handler.beforeReportError;
31
+ handler.beforeReportError = function(...args) {
32
+ if (__addonCrashFlag !== void 0) try {
33
+ Atomics.store(__addonCrashFlag, 0, 1);
34
+ } catch {}
35
+ return __beforeReportError.apply(this, args);
36
+ };
37
+ globalThis.onmessage = function(e) {
38
+ const data = e && e.data;
39
+ if (data && data.__napiRsAddonCrashFlag instanceof Int32Array) {
40
+ __addonCrashFlag = data.__napiRsAddonCrashFlag;
41
+ return;
42
+ }
43
+ handler.handle(e);
44
+ };
package/README.md DELETED
@@ -1,7 +0,0 @@
1
- # @kirana-style/compiler-browser
2
-
3
- A placeholder holding the name for the Kirana engine in a browser page: the WebAssembly engine with
4
- an in-memory project, for playgrounds and editors on the web. It will release together with Kirana.
5
- Install `@kirana-style/compiler-browser@latest`.
6
-
7
- Documentation: https://kirana.style/docs/project/ecosystem · Source: https://github.com/kirana-style/kirana
package/index.js DELETED
@@ -1,5 +0,0 @@
1
- // A placeholder that holds the name on npm until the first release. It fails loudly rather than
2
- // doing nothing.
3
- throw new Error(
4
- "@kirana-style/compiler-browser@0.0.0 is a placeholder. Install @kirana-style/compiler-browser@latest."
5
- )