nattoppet 4.0.1 → 4.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/GUIDE.md +8 -8
- package/LICENSE +21 -0
- package/dist/nattoppet-dev.js +84 -0
- package/{nattoppet-init.ts → dist/nattoppet-init.js} +46 -48
- package/dist/nattoppet-native.js +116 -0
- package/dist/nattoppet.js +87083 -0
- package/package.json +21 -37
- package/readme.md +12 -6
- package/compiler.ts +0 -169
- package/nattoppet-dev.ts +0 -94
- package/nattoppet-native.ts +0 -116
- package/nattoppet.ts +0 -35
- package/stdlib.ts +0 -120
- /package/{common.ymd → dist/common.ymd} +0 -0
- /package/{form.css → dist/form.css} +0 -0
- /package/{form.js → dist/form.js} +0 -0
- /package/{form.ymd → dist/form.ymd} +0 -0
- /package/{katex.css → dist/katex.css} +0 -0
- /package/{katex.ymd → dist/katex.ymd} +0 -0
- /package/{koa.css → dist/koa.css} +0 -0
- /package/{koa.ymd → dist/koa.ymd} +0 -0
- /package/{ppt.css → dist/ppt.css} +0 -0
- /package/{ppt.js → dist/ppt.js} +0 -0
- /package/{ppt.ymd → dist/ppt.ymd} +0 -0
- /package/{tml.css → dist/tml.css} +0 -0
- /package/{tml.js → dist/tml.js} +0 -0
- /package/{tml.ymd → dist/tml.ymd} +0 -0
- /package/{vue.css → dist/vue.css} +0 -0
- /package/{vue.ymd → dist/vue.ymd} +0 -0
package/package.json
CHANGED
|
@@ -1,50 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nattoppet",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "A tiny macro-based markup language for blogging",
|
|
5
5
|
"author": "ylxdzsw <ylxdzsw@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"files": [
|
|
9
|
-
"
|
|
10
|
-
"nattoppet-dev.ts",
|
|
11
|
-
"nattoppet-init.ts",
|
|
12
|
-
"nattoppet-native.ts",
|
|
13
|
-
"compiler.ts",
|
|
14
|
-
"stdlib.ts",
|
|
15
|
-
"common.ymd",
|
|
16
|
-
"vue.ymd",
|
|
17
|
-
"form.ymd",
|
|
18
|
-
"katex.ymd",
|
|
19
|
-
"koa.ymd",
|
|
20
|
-
"ppt.ymd",
|
|
21
|
-
"tml.ymd",
|
|
22
|
-
"vue.css",
|
|
23
|
-
"form.css",
|
|
24
|
-
"form.js",
|
|
25
|
-
"koa.css",
|
|
26
|
-
"ppt.css",
|
|
27
|
-
"ppt.js",
|
|
28
|
-
"tml.css",
|
|
29
|
-
"tml.js",
|
|
30
|
-
"katex.css",
|
|
9
|
+
"dist",
|
|
31
10
|
"GUIDE.md",
|
|
32
|
-
"
|
|
11
|
+
"readme.md"
|
|
33
12
|
],
|
|
34
13
|
"bin": {
|
|
35
|
-
"nattoppet": "./nattoppet.
|
|
36
|
-
"nattoppet-dev": "./nattoppet-dev.
|
|
37
|
-
"nattoppet-init": "./nattoppet-init.
|
|
38
|
-
"nattoppet-native": "./nattoppet-native.
|
|
14
|
+
"nattoppet": "./dist/nattoppet.js",
|
|
15
|
+
"nattoppet-dev": "./dist/nattoppet-dev.js",
|
|
16
|
+
"nattoppet-init": "./dist/nattoppet-init.js",
|
|
17
|
+
"nattoppet-native": "./dist/nattoppet-native.js"
|
|
39
18
|
},
|
|
40
19
|
"scripts": {
|
|
41
|
-
"build": "
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
20
|
+
"build": "node scripts/build-package.mjs",
|
|
21
|
+
"build:binary": "bun build ./nattoppet.ts --compile --outfile=./binary-dist/nattoppet",
|
|
22
|
+
"prepack": "npm run build",
|
|
23
|
+
"dev": "node nattoppet-dev.ts",
|
|
24
|
+
"test": "node --test",
|
|
25
|
+
"test:bun": "bun test",
|
|
26
|
+
"test:watch": "node --test --watch",
|
|
27
|
+
"compile": "node nattoppet.ts",
|
|
28
|
+
"init": "node nattoppet-init.ts",
|
|
29
|
+
"native": "node nattoppet-native.ts"
|
|
48
30
|
},
|
|
49
31
|
"dependencies": {
|
|
50
32
|
"coffeescript": "^2.7.0",
|
|
@@ -53,8 +35,10 @@
|
|
|
53
35
|
"less": "^4.2.0",
|
|
54
36
|
"marked": "^9.1.6"
|
|
55
37
|
},
|
|
56
|
-
"devDependencies": {
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"esbuild": "^0.25.0"
|
|
40
|
+
},
|
|
57
41
|
"engines": {
|
|
58
|
-
"
|
|
42
|
+
"node": ">=22.18.0"
|
|
59
43
|
}
|
|
60
44
|
}
|
package/readme.md
CHANGED
|
@@ -5,36 +5,42 @@ A tiny markup language and themes for making documents, slides, or web apps as s
|
|
|
5
5
|
|
|
6
6
|
### Usage
|
|
7
7
|
|
|
8
|
+
Install Nattoppet with Node.js 22.18 or newer:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g nattoppet
|
|
12
|
+
```
|
|
13
|
+
|
|
8
14
|
Compile a nattoppet file:
|
|
9
15
|
|
|
10
16
|
```bash
|
|
11
|
-
|
|
17
|
+
nattoppet in.ymd > out.html
|
|
12
18
|
```
|
|
13
19
|
|
|
14
20
|
Compile a nattoppet file but don't minimize (for debugging):
|
|
15
21
|
|
|
16
22
|
```bash
|
|
17
|
-
|
|
23
|
+
nattoppet in.ymd --dev > out.html
|
|
18
24
|
```
|
|
19
25
|
|
|
20
26
|
Open a browser to preview a nattoppet file; rebuild every time the page is refreshed:
|
|
21
27
|
|
|
22
28
|
```bash
|
|
23
|
-
|
|
29
|
+
nattoppet-dev in.ymd
|
|
24
30
|
```
|
|
25
31
|
|
|
26
32
|
Initialize a new project:
|
|
27
33
|
|
|
28
34
|
```bash
|
|
29
|
-
|
|
35
|
+
nattoppet-init form
|
|
30
36
|
```
|
|
31
37
|
|
|
32
38
|
Build a double-clickable executable for the page:
|
|
33
39
|
|
|
34
40
|
```bash
|
|
35
|
-
|
|
41
|
+
nattoppet-native init
|
|
36
42
|
# (edit the metadata as needed)
|
|
37
|
-
|
|
43
|
+
nattoppet-native build in.ymd
|
|
38
44
|
```
|
|
39
45
|
|
|
40
46
|
|
package/compiler.ts
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import vm from "node:vm"
|
|
2
|
-
import { extname } from "node:path"
|
|
3
|
-
import { fileURLToPath } from "node:url"
|
|
4
|
-
import * as fs from "node:fs"
|
|
5
|
-
|
|
6
|
-
const pattern = /^(?:\[(.+?)\]([:=])|\[(mixin|#slot)\] ?(.*?)\n)/m
|
|
7
|
-
|
|
8
|
-
const fetch_text_file = async (path: string) => {
|
|
9
|
-
const filePath = new URL(path, import.meta.url)
|
|
10
|
-
return fs.readFileSync(fileURLToPath(filePath), 'utf-8')
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const file_exists = (path: string) => {
|
|
14
|
-
try {
|
|
15
|
-
const filePath = new URL(path, import.meta.url)
|
|
16
|
-
fs.accessSync(fileURLToPath(filePath))
|
|
17
|
-
return true
|
|
18
|
-
} catch {
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const resolve_mixin_path = (path: string): string => {
|
|
24
|
-
if (extname(path)) return path
|
|
25
|
-
// Compatibility hack: bare name only resolves to .ymd if exact file doesn't exist
|
|
26
|
-
if (file_exists(path)) return path
|
|
27
|
-
const ymdPath = path + ".ymd"
|
|
28
|
-
if (file_exists(ymdPath)) return ymdPath
|
|
29
|
-
throw `mixin not found: ${path}`
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// tokenize also process mixins
|
|
33
|
-
// mixins are special direvatives
|
|
34
|
-
// by default, they are relative to the root directory of nattoppet, unless they are prefixed with "./"
|
|
35
|
-
export const tokenize = async (str: string) => {
|
|
36
|
-
const tokens: any[] = []
|
|
37
|
-
|
|
38
|
-
while (true) {
|
|
39
|
-
const m = str.match(pattern)
|
|
40
|
-
if (!m || m.index == null) {
|
|
41
|
-
if (str) tokens.push({ type: "code", content: str })
|
|
42
|
-
break
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (m.index && m.index > 0) {
|
|
46
|
-
tokens.push({ type: "code", content: str.substring(0, m.index) })
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (m[3]) {
|
|
50
|
-
if (m[3] === 'mixin') {
|
|
51
|
-
tokens.push({ type: 'mixin', path: m[4] })
|
|
52
|
-
} else if (m[3] === '#slot') {
|
|
53
|
-
tokens.push({ type: 'slot' })
|
|
54
|
-
}
|
|
55
|
-
str = str.substring(m.index + m[0].length)
|
|
56
|
-
continue
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const name = m[1]
|
|
60
|
-
const type = m[2] == ':' ? 'ref' : 'fn'
|
|
61
|
-
str = str.substring(m.index + m[0].length)
|
|
62
|
-
|
|
63
|
-
const p1 = str.indexOf('\n\n')
|
|
64
|
-
const p2 = str.search(pattern)
|
|
65
|
-
const p = p1 >= 0 && p2 >= 0 ? Math.min(p1+2, p2) :
|
|
66
|
-
p1 < 0 && p2 < 0 ? str.length : Math.max(p1+2, p2)
|
|
67
|
-
|
|
68
|
-
const content = str.substring(0, p).trim()
|
|
69
|
-
str = str.substring(p)
|
|
70
|
-
|
|
71
|
-
tokens.push({ type, name, content })
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
for (let i = 0; i < tokens.length; i++) if (tokens[i].type == 'mixin') {
|
|
75
|
-
const resolvedPath = resolve_mixin_path(tokens[i].path)
|
|
76
|
-
const ext = extname(resolvedPath)
|
|
77
|
-
|
|
78
|
-
if (ext === ".ymd") {
|
|
79
|
-
const text = await fetch_text_file(resolvedPath)
|
|
80
|
-
const inlineTokens = await tokenize(text)
|
|
81
|
-
const slotIndices = inlineTokens
|
|
82
|
-
.map((t: any, idx: number) => t.type === 'slot' ? idx : -1)
|
|
83
|
-
.filter((idx: number) => idx >= 0)
|
|
84
|
-
|
|
85
|
-
if (slotIndices.length > 1) {
|
|
86
|
-
throw `multiple [#slot] in mixin ${resolvedPath}`
|
|
87
|
-
} else if (slotIndices.length === 1) {
|
|
88
|
-
const slotIdx = slotIndices[0]
|
|
89
|
-
const head = inlineTokens.slice(0, slotIdx)
|
|
90
|
-
const tail = inlineTokens.slice(slotIdx + 1)
|
|
91
|
-
tokens.splice(i, 1, ...head)
|
|
92
|
-
tokens.push(...tail)
|
|
93
|
-
} else {
|
|
94
|
-
tokens.splice(i, 1, ...inlineTokens)
|
|
95
|
-
}
|
|
96
|
-
} else {
|
|
97
|
-
tokens[i] = { type: "raw", content: await fetch_text_file(resolvedPath) }
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
i -= 1 // revisit i since we replaced it
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return tokens
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const _interpret = (str: string, env: any, defs: any[]): any => {
|
|
107
|
-
const p1 = str.search(/^ /m)
|
|
108
|
-
const p2 = str.search(/\[(.+?)\]/)
|
|
109
|
-
if (p1 < 0 && p2 < 0) return str
|
|
110
|
-
|
|
111
|
-
if (p2 < 0 || (p1 >= 0 && p1 < p2)) {
|
|
112
|
-
const head = str.substring(0, p1)
|
|
113
|
-
str = str.substring(p1+2)
|
|
114
|
-
let p = str.indexOf('\n\n')
|
|
115
|
-
if (p < 0) p = str.length
|
|
116
|
-
return head + '<p>' + _interpret(str.substring(0, p), env, defs) + '</p>' + _interpret(str.substring(p+2), env, defs)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const name = str.match(/\[(.+?)\]/)![1]
|
|
120
|
-
const i = defs.findIndex(x => x.name == name)
|
|
121
|
-
if (i < 0) throw `definition ${name} not found`
|
|
122
|
-
|
|
123
|
-
const def = defs[i]
|
|
124
|
-
switch (def.type) {
|
|
125
|
-
case 'fn':
|
|
126
|
-
env.remaining = str.substring(p2 + name.length + 2)
|
|
127
|
-
env.interpret = (str: string) => { // a "scoped" interpret function capable for interpreting substrings. We preserve the "remaining" property outside.
|
|
128
|
-
const remaining = env.remaining
|
|
129
|
-
const result = _interpret(str, env, defs) // We gaved full defs available on the call site (dynamic scoping) as the text to be interpreted is part of the call site text.
|
|
130
|
-
env.remaining = remaining
|
|
131
|
-
return result
|
|
132
|
-
}
|
|
133
|
-
const result = vm.runInContext('{' + def.content + '}', env)
|
|
134
|
-
return str.substring(0, p2) + result + _interpret(env.remaining, env, defs)
|
|
135
|
-
case 'ref':
|
|
136
|
-
return str.substring(0, p2) +
|
|
137
|
-
_interpret(def.content, env, defs.slice(i+1)) + // Exclude self from scope to prevent infinite recursion, but allow forward references
|
|
138
|
-
_interpret(str.substring(p2 + name.length + 2), env, defs)
|
|
139
|
-
default: throw 'unknown defs type'
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export const compile = async (str: string, locals: any = {}) => {
|
|
144
|
-
const env = vm.createContext(locals)
|
|
145
|
-
for (const k in env) if (typeof(env[k]) == 'function')
|
|
146
|
-
env[k] = env[k].bind(env)
|
|
147
|
-
|
|
148
|
-
let tokens = await tokenize(str)
|
|
149
|
-
let output = ''
|
|
150
|
-
while (true) {
|
|
151
|
-
const token = tokens.shift()
|
|
152
|
-
if (!token) return output
|
|
153
|
-
switch (token.type) {
|
|
154
|
-
case "code":
|
|
155
|
-
output += _interpret(token.content, env, tokens)
|
|
156
|
-
break
|
|
157
|
-
case "raw":
|
|
158
|
-
output += token.content
|
|
159
|
-
break
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/*
|
|
165
|
-
TODO:
|
|
166
|
-
1. support first-class markdown-like nestable lists
|
|
167
|
-
2. cleanup the spaces, carefully define when to trim
|
|
168
|
-
3. require a newline before indent to open paragraph?
|
|
169
|
-
*/
|
package/nattoppet-dev.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs"
|
|
2
|
-
import * as path from "node:path"
|
|
3
|
-
import { fileURLToPath } from "node:url"
|
|
4
|
-
|
|
5
|
-
const file = process.argv[2]
|
|
6
|
-
|
|
7
|
-
if (process.argv.length < 3 || file == "--help") {
|
|
8
|
-
console.log("Usage: nattoppet-dev [file] [hook cmd]...")
|
|
9
|
-
process.exit(0)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const nattoppetPath = fileURLToPath(new URL("nattoppet.ts", import.meta.url))
|
|
13
|
-
|
|
14
|
-
Bun.serve({
|
|
15
|
-
port: 3939,
|
|
16
|
-
async fetch(request) {
|
|
17
|
-
const url = new URL(request.url)
|
|
18
|
-
|
|
19
|
-
// Static file serving
|
|
20
|
-
if (url.pathname !== '/') {
|
|
21
|
-
try {
|
|
22
|
-
const filePath = path.join(process.cwd(), decodeURIComponent(url.pathname))
|
|
23
|
-
const file = Bun.file(filePath)
|
|
24
|
-
if (await file.exists()) {
|
|
25
|
-
return new Response(file)
|
|
26
|
-
}
|
|
27
|
-
} catch (e) {
|
|
28
|
-
console.error(e)
|
|
29
|
-
}
|
|
30
|
-
return new Response(null, { status: 404 })
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Run build hooks if specified
|
|
34
|
-
if (process.argv.length > 3) {
|
|
35
|
-
const hookProc = Bun.spawn(process.argv.slice(3))
|
|
36
|
-
const exitCode = await hookProc.exited
|
|
37
|
-
|
|
38
|
-
if (exitCode !== 0) {
|
|
39
|
-
console.warn("!!! hook command exit with non-0 status")
|
|
40
|
-
return new Response("Hook command failed", { status: 500 })
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Compile the nattoppet file
|
|
45
|
-
const compileProc = Bun.spawn([
|
|
46
|
-
process.execPath,
|
|
47
|
-
"run",
|
|
48
|
-
nattoppetPath,
|
|
49
|
-
file,
|
|
50
|
-
"--dev"
|
|
51
|
-
], {
|
|
52
|
-
stdout: 'pipe',
|
|
53
|
-
stderr: 'pipe'
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
const [stdout, stderr] = await Promise.all([
|
|
57
|
-
new Response(compileProc.stdout).arrayBuffer(),
|
|
58
|
-
new Response(compileProc.stderr).text()
|
|
59
|
-
])
|
|
60
|
-
|
|
61
|
-
const exitCode = await compileProc.exited
|
|
62
|
-
|
|
63
|
-
if (stderr) {
|
|
64
|
-
console.error(stderr)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const content = exitCode === 0 ? new Uint8Array(stdout) : new TextEncoder().encode("Compilation failed")
|
|
68
|
-
|
|
69
|
-
return new Response(content, {
|
|
70
|
-
headers: {
|
|
71
|
-
"Content-Type": "text/html"
|
|
72
|
-
},
|
|
73
|
-
status: 200
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
console.log("Server running at http://127.0.0.1:3939")
|
|
79
|
-
|
|
80
|
-
// Open browser
|
|
81
|
-
const platform = process.platform
|
|
82
|
-
const browser = process.env.BROWSER ?? (
|
|
83
|
-
platform === 'darwin' ? 'open' :
|
|
84
|
-
platform === 'win32' ? 'start' :
|
|
85
|
-
'xdg-open'
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
const browserCmd = browser.includes('chrom')
|
|
89
|
-
? [browser, "--app=http://127.0.0.1:3939"]
|
|
90
|
-
: [browser, "http://127.0.0.1:3939"]
|
|
91
|
-
|
|
92
|
-
Bun.spawn(browserCmd, {
|
|
93
|
-
stdio: ['ignore', 'ignore', 'ignore']
|
|
94
|
-
})
|
package/nattoppet-native.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs"
|
|
2
|
-
import * as path from "node:path"
|
|
3
|
-
import { fileURLToPath } from "node:url"
|
|
4
|
-
|
|
5
|
-
const main_source = `\
|
|
6
|
-
#![windows_subsystem = "windows"]
|
|
7
|
-
|
|
8
|
-
use web_view::*;
|
|
9
|
-
|
|
10
|
-
fn main() {
|
|
11
|
-
static HTML_CONTENT: &str = include_str!("../target/bundle.html");
|
|
12
|
-
|
|
13
|
-
web_view::builder()
|
|
14
|
-
.title("Nattoppet Native")
|
|
15
|
-
.content(Content::Html(HTML_CONTENT))
|
|
16
|
-
.size(600, 480)
|
|
17
|
-
.resizable(false)
|
|
18
|
-
.debug(cfg!(debug_assertions))
|
|
19
|
-
.user_data(())
|
|
20
|
-
.invoke_handler(handler)
|
|
21
|
-
.run()
|
|
22
|
-
.unwrap();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
fn handler(webview: &mut WebView<()>, arg: &str) -> WVResult {
|
|
26
|
-
Ok(())
|
|
27
|
-
}
|
|
28
|
-
`
|
|
29
|
-
|
|
30
|
-
const build_source = `\
|
|
31
|
-
#[cfg(windows)]
|
|
32
|
-
extern crate winres;
|
|
33
|
-
|
|
34
|
-
#[cfg(windows)]
|
|
35
|
-
fn main() {
|
|
36
|
-
let mut res = winres::WindowsResource::new();
|
|
37
|
-
res.set_icon("../icon.ico");
|
|
38
|
-
res.compile().unwrap();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
#[cfg(not(windows))]
|
|
42
|
-
fn main() {}
|
|
43
|
-
`
|
|
44
|
-
|
|
45
|
-
const cargo_source = `\
|
|
46
|
-
[package]
|
|
47
|
-
name = "nattoppet_native"
|
|
48
|
-
version = "0.1.0"
|
|
49
|
-
edition = "2021"
|
|
50
|
-
|
|
51
|
-
[dependencies]
|
|
52
|
-
web-view = { version = "0.7", features = ["edge"] }
|
|
53
|
-
|
|
54
|
-
[target.'cfg(windows)'.build-dependencies]
|
|
55
|
-
winres = "0.1"
|
|
56
|
-
|
|
57
|
-
[package.metadata.winres]
|
|
58
|
-
ProductName = "A nattoppet native app"
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
const init = () => {
|
|
62
|
-
fs.mkdirSync('native', { recursive: true })
|
|
63
|
-
fs.mkdirSync('native/src', { recursive: true })
|
|
64
|
-
fs.mkdirSync('native/target', { recursive: true })
|
|
65
|
-
fs.writeFileSync("native/src/main.rs", main_source)
|
|
66
|
-
fs.writeFileSync("native/build.rs", build_source)
|
|
67
|
-
fs.writeFileSync("native/Cargo.toml", cargo_source)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const bundle = async () => {
|
|
71
|
-
const nattoppetPath = fileURLToPath(new URL("nattoppet.ts", import.meta.url))
|
|
72
|
-
const proc = Bun.spawn([
|
|
73
|
-
process.execPath,
|
|
74
|
-
"run",
|
|
75
|
-
nattoppetPath,
|
|
76
|
-
process.argv[3]
|
|
77
|
-
], {
|
|
78
|
-
stdout: 'pipe',
|
|
79
|
-
stderr: 'pipe'
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
const [stdout, stderr] = await Promise.all([
|
|
83
|
-
new Response(proc.stdout).arrayBuffer(),
|
|
84
|
-
new Response(proc.stderr).text()
|
|
85
|
-
])
|
|
86
|
-
|
|
87
|
-
if (stderr)
|
|
88
|
-
console.error(stderr)
|
|
89
|
-
|
|
90
|
-
await Bun.write('native/target/bundle.html', stdout)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const build = async () => {
|
|
94
|
-
await bundle()
|
|
95
|
-
const proc = Bun.spawn(['cargo', 'build', '--release'], {
|
|
96
|
-
cwd: 'native'
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
const exitCode = await proc.exited
|
|
100
|
-
if (exitCode != 0) {
|
|
101
|
-
console.error("Cargo exit with code: " + exitCode)
|
|
102
|
-
} else {
|
|
103
|
-
console.log("building finished. Check native/target/release/")
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const help = () => {
|
|
108
|
-
console.log("Usage: nattoppet-native [init|bundle|build] <file.ymd>")
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
switch (process.argv[2]) {
|
|
112
|
-
case 'init': init(); break
|
|
113
|
-
case 'bundle': bundle(); break
|
|
114
|
-
case 'build': build(); break
|
|
115
|
-
default: help(); break
|
|
116
|
-
}
|
package/nattoppet.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import * as path from "node:path"
|
|
2
|
-
import * as fs from "node:fs"
|
|
3
|
-
import minifier from "html-minifier-terser"
|
|
4
|
-
|
|
5
|
-
import stdlib from "./stdlib.ts"
|
|
6
|
-
import * as compiler from "./compiler.ts"
|
|
7
|
-
|
|
8
|
-
const compiled = await (async () => {
|
|
9
|
-
const file = process.argv[2]
|
|
10
|
-
if (file) {
|
|
11
|
-
const dir = path.dirname(path.resolve(file))
|
|
12
|
-
const code = fs.readFileSync(file, 'utf-8')
|
|
13
|
-
return await compiler.compile(code, { ...stdlib, base_dir: dir })
|
|
14
|
-
} else {
|
|
15
|
-
const dir = process.cwd()
|
|
16
|
-
const chunks: Uint8Array[] = []
|
|
17
|
-
for await (const chunk of process.stdin) {
|
|
18
|
-
chunks.push(chunk)
|
|
19
|
-
}
|
|
20
|
-
const code = Buffer.concat(chunks).toString('utf-8')
|
|
21
|
-
return await compiler.compile(code, { ...stdlib, base_dir: dir })
|
|
22
|
-
}
|
|
23
|
-
})()
|
|
24
|
-
|
|
25
|
-
const minified = process.argv.includes('--dev') ? compiled : await minifier.minify(compiled, {
|
|
26
|
-
collapseWhitespace: true,
|
|
27
|
-
removeAttributeQuotes: true,
|
|
28
|
-
removeComments: true,
|
|
29
|
-
removeRedundantAttributes: true,
|
|
30
|
-
removeOptionalTags: true,
|
|
31
|
-
minifyCSS: true,
|
|
32
|
-
minifyJS: true,
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
process.stdout.write(minified)
|
package/stdlib.ts
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import * as path from "node:path"
|
|
2
|
-
import * as fs from "node:fs"
|
|
3
|
-
import * as zlib from "node:zlib"
|
|
4
|
-
|
|
5
|
-
import { marked } from "marked"
|
|
6
|
-
import coffee from "coffeescript"
|
|
7
|
-
import katex from "katex"
|
|
8
|
-
import less from "less"
|
|
9
|
-
|
|
10
|
-
function compress_sync(data: ArrayBuffer) {
|
|
11
|
-
// Keep using zlib.deflateRawSync for browser compatibility
|
|
12
|
-
// Browser uses DecompressionStream("deflate-raw") which expects raw deflate
|
|
13
|
-
return zlib.deflateRawSync(new Uint8Array(data), {
|
|
14
|
-
flush: zlib.constants.Z_FINISH,
|
|
15
|
-
level: zlib.constants.Z_BEST_COMPRESSION
|
|
16
|
-
})
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default {
|
|
20
|
-
skip(n: number) {
|
|
21
|
-
this.remaining = this.remaining.substring(n)
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
capture_until(delimiter: string) {
|
|
25
|
-
const p = this.remaining.indexOf(delimiter)
|
|
26
|
-
if (p < 0) {
|
|
27
|
-
const result = this.remaining
|
|
28
|
-
this.remaining = ""
|
|
29
|
-
return result
|
|
30
|
-
}
|
|
31
|
-
const result = this.remaining.substring(0, p)
|
|
32
|
-
this.remaining = this.remaining.substring(p + delimiter.length)
|
|
33
|
-
return result
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
std_call(hascontent = false) {
|
|
37
|
-
const opts: string[] = [], args: string[] = []
|
|
38
|
-
const parse_option = () => {
|
|
39
|
-
const m = this.remaining.match(/^\.([\w\-]+)/)
|
|
40
|
-
this.skip(m[0].length)
|
|
41
|
-
opts.push(m[1])
|
|
42
|
-
}
|
|
43
|
-
const parse_argument = () => {
|
|
44
|
-
const m = this.remaining.match(/^\((.*?)\)|{(.*?)}/)
|
|
45
|
-
this.skip(m[0].length)
|
|
46
|
-
args.push(m[1] || m[2])
|
|
47
|
-
}
|
|
48
|
-
const parse_block = () => {
|
|
49
|
-
const m = this.remaining.match(/^(>+)\n?/)
|
|
50
|
-
this.skip(m[0].length)
|
|
51
|
-
return this.capture_until('<'.repeat(m[1].length))
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
while (true) {
|
|
55
|
-
switch (this.remaining[0]) {
|
|
56
|
-
case '.':
|
|
57
|
-
parse_option()
|
|
58
|
-
break
|
|
59
|
-
case '(':
|
|
60
|
-
case '{':
|
|
61
|
-
parse_argument()
|
|
62
|
-
break
|
|
63
|
-
case '>':
|
|
64
|
-
return { opts, args, block: parse_block() }
|
|
65
|
-
case ' ':
|
|
66
|
-
if (hascontent)
|
|
67
|
-
return { opts, args, block: this.capture_until('\n').slice(1) }
|
|
68
|
-
default:
|
|
69
|
-
return { opts, args }
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
rpath(file: string) {
|
|
75
|
-
if (!path.isAbsolute(file))
|
|
76
|
-
return path.join(this.base_dir, file)
|
|
77
|
-
return file
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
read(file: string, encoding = "utf-8") {
|
|
81
|
-
const filePath = this.rpath(file)
|
|
82
|
-
switch (encoding) {
|
|
83
|
-
case "utf8":
|
|
84
|
-
case "utf-8":
|
|
85
|
-
return fs.readFileSync(filePath, 'utf-8')
|
|
86
|
-
case "base64":
|
|
87
|
-
return Buffer.from(fs.readFileSync(filePath)).toString('base64')
|
|
88
|
-
case "compressed-base64":
|
|
89
|
-
return Buffer.from(compress_sync(fs.readFileSync(filePath))).toString('base64')
|
|
90
|
-
default:
|
|
91
|
-
throw "unknown encoding: " + encoding
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
extname(file: string) {
|
|
96
|
-
return path.extname(file).slice(1)
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
basename(file: string) {
|
|
100
|
-
return path.basename(file)
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
render_coffee(str: string, options: any) {
|
|
104
|
-
return coffee.compile(str, options)
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
render_less(str: string) {
|
|
108
|
-
let x: any; less.render(str, {}, (err: any, out: any) => !err && (x = out))
|
|
109
|
-
if (!x) throw "less failed to compile synchronously"
|
|
110
|
-
return x.css
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
render_markdown(str: string) {
|
|
114
|
-
return marked.parse(str)
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
render_katex(str: string, displayMode = false) {
|
|
118
|
-
return katex.renderToString(str, { displayMode, output: 'html' })
|
|
119
|
-
}
|
|
120
|
-
} as any
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/{ppt.js → dist/ppt.js}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|