runtime-compiler 4.0.0 → 4.0.1

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
@@ -1,131 +1,33 @@
1
1
  A code generation system.
2
2
 
3
3
  # Usage
4
- See [examples](https://github.com/re-utils/runtime-compiler/tree/main/examples).
5
-
6
- # Code generation
7
- Code generation is a trick used in a lot of high performance libraries, especially schema validators (TypeBox, Ajv, Sury, ...).
8
- ```ts
9
- const schema = {
10
- id: t.string,
11
- pwd: t.string
12
- };
13
-
14
- // Can be compiled to a fast check function
15
- const check = (0, eval)(`
16
- (d) => typeof d === 'object'
17
- && d !== null
18
- && typeof d.id === 'string'
19
- && typeof d.pwd === 'string'
20
- `);
21
- ```
22
-
23
- Backend frameworks like Elysia also use it to optimize their request handling:
24
- ```ts
25
- // Not exactly what Elysia generates but u get the idea
26
- switch (path) {
27
- case '/': return handleHome();
28
- case '/user': {
29
- // Calls can be analyzed to remove
30
- // unused fields in the context object
31
- const c = { ... };
32
-
33
- // Inline hook calls
34
- onRequestHook(c);
35
- handleRequest(c);
36
- afterRequestHook(c);
37
-
38
- return sendResponse(c);
39
- }
40
- }
41
- ```
42
-
43
- For cases that code generation works, it is usually much easier to write and setup than AST transformations, compiles faster, and also has more flexibility.
44
-
45
- The output startup cost is higher than AST transformations though, but this library has primitives to minimize that additional cost.
46
-
47
- ## Writing codegen
48
- The first example was pretty simple, but what if we want to use an user-defined value?
49
- ```ts
50
- const check = (0, eval)(`
51
- (d) => typeof d === 'string' && checkEmail(d)
52
- `);
53
- ```
54
-
55
- This works but it breaks with closures (similarly for other types of unserializable data):
56
4
  ```ts
57
- import { checkEmail } from './utils.ts';
58
-
59
- // eval returns the value of the last statement
60
- const check = (0, eval)(`
61
- const checkEmail = ${checkEmail.toString()};
62
- (d) => typeof d === 'string' && checkEmail(d)
63
- `);
5
+ import { IS_AOT, IS_BUILD } from 'runtime-compiler/env';
6
+ import { emit, evaluate } from 'runtime-compiler/globals';
64
7
 
65
- // For a checkEmail function like this it would
66
- // break as emailRegex is not available in eval scope
67
- const emailRegex = /.../;
68
- const checkEmail = (str: string) => emailRegex.test(str);
69
- ```
8
+ // Only emit code if not in AOT mode
9
+ IS_AOT || emit(`return()=>console.log("Hi")`);
70
10
 
71
- We gonna need a dependency array to inject user-defined values into the eval scope:
72
- ```ts
73
- import { checkEmail } from './utils.ts';
11
+ // Only log when building
12
+ IS_BUILD && console.log('Building...');
74
13
 
75
- const buildCheck = (0, eval)(`(deps) => {
76
- const checkEmail = deps[0];
77
- return (d) => typeof d === 'string' && checkEmail(d)
78
- }`);
79
-
80
- const check = buildCheck([checkEmail]);
14
+ // Run emitted code
15
+ const fn = evaluate<() => void>();
16
+ fn();
81
17
  ```
82
18
 
83
- ## Problems
84
- Code generation has 2 main problems:
85
- - It has a startup time, memory usage and bundle size cost.
86
- - It does not work on certain runtimes.
87
-
88
- This library merges all `eval()` calls into 1 (which makes it able to do ahead of time compilation) and make compiled values easier to work with.
19
+ Build with `rolldown` or `vite`:
89
20
  ```ts
90
- const check1 = compile(schema1);
91
- const check2 = compile(schema2);
92
-
93
- // Can be rewritten as
94
- const check1Id = compile(schema1);
95
- const check2Id = compile(schema2);
21
+ ...
22
+ import rtc from 'runtime-compiler/build/rolldown';
96
23
 
97
- // For ahead-of-time compilation to work
98
- // this evaluate() call can just be replaced with
99
- // whatever code is generated at the moment
100
- evaluate();
101
-
102
- const check1 = artifact(check1Id);
103
- const check2 = artifact(check2Id);
24
+ export default defineConfig({
25
+ ...,
26
+ plugins: [
27
+ ...,
28
+ rtc()
29
+ ]
30
+ });
104
31
  ```
105
32
 
106
- The ahead of time output can be:
107
- ```js
108
- // Store dependencies and build outputs
109
- const check1Id = compile(schema1);
110
- const check2Id = compile(schema2);
111
-
112
- $[0] = (d) => typeof d === 'string';
113
- $[1] = (d) => Number.isInteger(d) && d > -1 && d < 256;
114
-
115
- const check1 = artifact(check1Id);
116
- const check2 = artifact(check2Id);
117
- ```
118
-
119
- We can remove the codegen cost with flags:
120
- ```ts
121
- const compile = IS_AOT
122
- // This is the compile() function after build, since
123
- // we don't need to codegen anymore
124
- ? () => reserveArtifact()
125
- : (schema1) => {
126
- ...;
127
- const schemaId = reserveArtifact();
128
- emit(`$[${schemaId}]=${builtFnCode}`);
129
- return schemaId;
130
- }
131
- ```
33
+ You only need to build when the target runtime doesn't allow `eval()`.
@@ -1,16 +1,3 @@
1
1
  import type { Plugin } from "rolldown";
2
- interface ImportBinding {
3
- name: string;
4
- alias: string;
5
- }
6
- interface ImportStatement {
7
- start: number;
8
- end: number;
9
- bindings: ImportBinding[];
10
- }
11
- export declare const parseImports: (code: string) => {
12
- envImports: ImportStatement;
13
- globalsImports: ImportStatement;
14
- };
15
2
  declare const _default: () => Plugin;
16
3
  export default _default;
package/build/rolldown.js CHANGED
@@ -1 +1 @@
1
- import{runInWorker}from"./index.js";let parseImportBindings=(code,start,end)=>{let startSpecs=code.indexOf("{",start),endSpecs=code.lastIndexOf("}",end);if(-1===start&&-1===end)throw new Error("Only named imports replacement is supported!");let bindings=[];for(let i=0,specifiers=code.slice(startSpecs+1,endSpecs-1).split(",");i<specifiers.length;i++){let specifier=specifiers[i].trim();if(""===specifier)continue;let parts=specifier.split(" as ",2);if(1===parts.length){let name=parts[0].trim();bindings.push({name,alias:name})}else bindings.push({name:parts[0].trim(),alias:parts[1].trim()})}return{bindings,start,end}};export const parseImports=code=>{let envImports,globalsImports;for(let start=0,eol=code.indexOf("\n");eol>-1&&(null==envImports||null==globalsImports);eol=code.indexOf("\n",start=eol+1))code.startsWith("import",start)&&(code.endsWith('"runtime-compiler/env";',eol)?envImports=parseImportBindings(code,start,eol):code.endsWith('"runtime-compiler/globals";',eol)&&(globalsImports=parseImportBindings(code,start,eol)));return{envImports,globalsImports}};export default(()=>({name:"rolldown-plugin-runtime-compiler",resolveId:{filter:{id:/^runtime-compiler\/(?:env|globals)$/},handler:()=>!1},renderChunk:async(code,chunk)=>{if(!chunk.imports.includes("runtime-compiler/globals")||!chunk.imports.includes("runtime-compiler/env"))return null;let{envImports,globalsImports}=parseImports(code),aotCode="const __rtcpl_atf__=[],__rtcpl_aot_fns__=[],__rtcpl_setup_aot__=f=>{__rtcpl_aot_fns__.push(f)};let __rtcpl_aot_fn_idx__=0;";for(let i=0,{bindings}=envImports;i<bindings.length;i++){let{name,alias}=bindings[i];aotCode+="IS_AOT"===name?`const ${alias}=true;`:"IS_BUILD"===name||"IS_JIT"===name?`const ${alias}=false;`:""}for(let i=0,{bindings}=globalsImports;i<bindings.length;i++){let{name,alias}=bindings[i];aotCode+="artifact"===name?`const ${alias}=i=>__rtcpl_atf__[i];`:"emit"===name?`const ${alias}=()=>{};`:"evaluate"===name?`const ${alias}=()=>__rtcpl_aot_fns__[__rtcpl_aot_fn_idx__++](__rtcpl_atf__);`:"reserveArtifact"===name?`const ${alias}=()=>__rtcpl_atf__.push(undefined)-1;`:"importArtifact"===name?`const ${alias}=v=>__rtcpl_atf__.push(v)-1;`:""}for(let i=0,codes=await runInWorker(code);i<codes.length;i++)aotCode+=`__rtcpl_setup_aot__($=>{${codes[i]}});`;aotCode+=envImports.start<globalsImports.start?code.slice(0,envImports.start)+code.slice(envImports.end,globalsImports.start)+code.slice(globalsImports.end):code.slice(0,globalsImports.start)+code.slice(globalsImports.end,envImports.start)+code.slice(envImports.end);return{code:aotCode}}}));
1
+ import{runInWorker}from"./index.js";let parseImportBindings=(code,start,end)=>{let startSpecs=code.indexOf("{",start),endSpecs=code.lastIndexOf("}",end);if(-1===start&&-1===end)throw new Error("Only named imports replacement is supported!");let bindings=[];for(let i=0,specifiers=code.slice(startSpecs+1,endSpecs-1).split(",");i<specifiers.length;i++){let specifier=specifiers[i].trim();if(""===specifier)continue;let parts=specifier.split(" as ",2);if(1===parts.length){let name=parts[0].trim();bindings.push({name,alias:name})}else bindings.push({name:parts[0].trim(),alias:parts[1].trim()})}return{bindings,start,end}};export default(()=>({name:"rolldown-plugin-runtime-compiler",resolveId:{filter:{id:/^runtime-compiler\/(?:env|globals)$/},handler:()=>!1},renderChunk:async(code,{imports})=>{let globalsImportIdx=imports.indexOf("runtime-compiler/globals");if(-1===globalsImportIdx)return null;imports.splice(globalsImportIdx);let envImportIdx=imports.indexOf("runtime-compiler/env"),envImport=null,globalsImport=null;-1===envImportIdx?envImport={bindings:[],start:0,end:0}:imports.splice(envImportIdx,1);for(let start=0,eol=code.indexOf("\n");eol>-1&&(null==envImport||null==globalsImport);eol=code.indexOf("\n",start=eol+1))code.startsWith("import",start)&&(code.endsWith('"runtime-compiler/env";',eol)?envImport=parseImportBindings(code,start,eol):code.endsWith('"runtime-compiler/globals";',eol)&&(globalsImport=parseImportBindings(code,start,eol)));let aotCode="const __rtcpl_atf__=[],__rtcpl_aot_fns__=[],__rtcpl_setup_aot__=f=>{__rtcpl_aot_fns__.push(f)};let __rtcpl_aot_fn_idx__=0;";for(let i=0,{bindings}=envImport;i<bindings.length;i++){let{name,alias}=bindings[i];aotCode+="IS_AOT"===name?`const ${alias}=true;`:"IS_BUILD"===name||"IS_JIT"===name?`const ${alias}=false;`:""}for(let i=0,{bindings}=globalsImport;i<bindings.length;i++){let{name,alias}=bindings[i];aotCode+="deref"===name?`const ${alias}=i=>__rtcpl_atf__[i];`:"emit"===name?`const ${alias}=()=>{};`:"evaluate"===name?`const ${alias}=()=>__rtcpl_aot_fns__[__rtcpl_aot_fn_idx__++](__rtcpl_atf__);`:"createRef"===name?`const ${alias}=()=>__rtcpl_atf__.push(undefined)-1;`:"importRef"===name?`const ${alias}=v=>__rtcpl_atf__.push(v)-1;`:""}for(let i=0,codes=await runInWorker(code);i<codes.length;i++)aotCode+=`__rtcpl_setup_aot__($=>{${codes[i]}});`;aotCode+=envImport.start<globalsImport.start?code.slice(0,envImport.start)+code.slice(envImport.end,globalsImport.start)+code.slice(globalsImport.end):code.slice(0,globalsImport.start)+code.slice(globalsImport.end,envImport.start)+code.slice(envImport.end);return{code:aotCode}}}));
package/globals.d.ts CHANGED
@@ -1,10 +1,8 @@
1
- export type Artifact<T> = number & {
1
+ export type Ref<T> = number & {
2
2
  "~": T;
3
3
  };
4
- export declare const importArtifact: <T>(value: T) => Artifact<T>;
5
- export declare const reserveArtifact: <T>() => Artifact<T>;
4
+ export declare const importRef: <T>(value: T) => Ref<T>;
5
+ export declare const createRef: <T>() => Ref<T>;
6
6
  export declare let evaluate: <T>() => T;
7
7
  export declare const emit: (code: string) => void;
8
- export type ArtifactsRecord = Record<any, Artifact<any>>;
9
- export type ArtifactGroup<T extends ArtifactsRecord> = <K extends keyof T>(k: K) => T[K]["~"];
10
- export declare const artifact: <T>(id: Artifact<T>) => T;
8
+ export declare const deref: <T>(id: Ref<T>) => T;
package/globals.js CHANGED
@@ -1 +1 @@
1
- import{IS_AOT,IS_BUILD}from"./env/index.js";import{emptyFn}from"./utils.js";let __rtcpl_atf__=[];export const importArtifact=value=>__rtcpl_atf__.push(value)-1;export const reserveArtifact=()=>__rtcpl_atf__.push(void 0)-1;let content="";export let __rtcpl_ct__=[];export let evaluate=IS_AOT?()=>__rtcpl_aot_fns__[__rtcpl_aot_fn_idx__++](__rtcpl_atf__):IS_BUILD?()=>{__rtcpl_ct__.push(content);let currentContent=content;content="";if(currentContent.length>0)return(0,eval)(`$=>{${currentContent}}`)(__rtcpl_atf__)}:()=>{let currentContent=content;content="";if(currentContent.length>0)return(0,eval)(`$=>{${currentContent}}`)(__rtcpl_atf__)};export const emit=IS_AOT?emptyFn:code=>{content+=code};let __rtcpl_aot_fns__=[],__rtcpl_aot_fn_idx__=0;export const __rtcpl_setup_aot__=fn=>{__rtcpl_aot_fns__.push(fn)};export const artifact=id=>__rtcpl_atf__[id];
1
+ import{IS_AOT,IS_BUILD}from"./env/index.js";import{emptyFn}from"./utils.js";let refs=[];export const importRef=value=>refs.push(value)-1;export const createRef=()=>refs.push(void 0)-1;let content="";export let __rtcpl_ct__=[];export let evaluate=IS_AOT?()=>__rtcpl_aot_fns__[__rtcpl_aot_fn_idx__++](refs):IS_BUILD?()=>{__rtcpl_ct__.push(content);let currentContent=content;content="";if(currentContent.length>0)return(0,eval)(`$=>{${currentContent}}`)(refs)}:()=>{let currentContent=content;content="";if(currentContent.length>0)return(0,eval)(`$=>{${currentContent}}`)(refs)};export const emit=IS_AOT?emptyFn:code=>{content+=code};let __rtcpl_aot_fns__=[],__rtcpl_aot_fn_idx__=0;export const __rtcpl_setup_aot__=fn=>{__rtcpl_aot_fns__.push(fn)};export const deref=id=>refs[id];
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"runtime-compiler","version":"4.0.0","description":"A compiler system.","keywords":[],"homepage":"","license":"MIT","repository":"","type":"module","exports":{"./*":"./*.js","./build":"./build/index.js","./env":"./env/index.js"}}
1
+ {"name":"runtime-compiler","version":"4.0.1","description":"A compiler system.","keywords":[],"homepage":"","license":"MIT","repository":"","type":"module","exports":{"./*":"./*.js","./build":"./build/index.js","./env":"./env/index.js"}}