@algosail/lang 0.2.12 → 0.5.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/bin/sail.mjs +4 -0
- package/cli/run-cli.js +176 -0
- package/index.js +12 -2
- package/lib/codegen/README.md +230 -0
- package/lib/codegen/codegen-diagnostics.js +164 -0
- package/lib/codegen/compile-graph.js +107 -0
- package/lib/codegen/emit-adt.js +177 -0
- package/lib/codegen/emit-body.js +1265 -0
- package/lib/codegen/emit-builtin.js +371 -0
- package/lib/codegen/emit-jsdoc-sail.js +383 -0
- package/lib/codegen/emit-module.js +498 -0
- package/lib/codegen/esm-imports.js +26 -0
- package/lib/codegen/index.js +69 -0
- package/lib/codegen/out-layout.js +102 -0
- package/lib/ffi/extract-jsdoc-sail.js +34 -0
- package/lib/io-node/index.js +4 -0
- package/lib/io-node/package-root.js +18 -0
- package/lib/io-node/read-file.js +12 -0
- package/lib/io-node/resolve-package.js +24 -0
- package/lib/io-node/resolve-sail-names-from-disk.js +21 -0
- package/lib/ir/assert-json-serializable.js +30 -0
- package/lib/ir/attach-call-effects.js +108 -0
- package/lib/ir/bind-values.js +594 -0
- package/lib/ir/build-module-ir.js +290 -0
- package/lib/ir/index.js +31 -0
- package/lib/ir/lower-body-steps.js +170 -0
- package/lib/ir/module-metadata.js +65 -0
- package/lib/ir/schema-version.js +15 -0
- package/lib/ir/serialize.js +202 -0
- package/lib/ir/stitch-types.js +92 -0
- package/lib/names/adt-autogen.js +22 -0
- package/lib/names/import-path.js +28 -0
- package/lib/names/index.js +1 -0
- package/lib/names/local-declarations.js +127 -0
- package/lib/names/lower-first.js +6 -0
- package/lib/names/module-scope.js +120 -0
- package/lib/names/resolve-sail.js +365 -0
- package/lib/names/walk-ast-refs.js +91 -0
- package/lib/parse/ast-build.js +51 -0
- package/lib/parse/ast-spec.js +212 -0
- package/lib/parse/builtins-set.js +12 -0
- package/lib/parse/diagnostics.js +180 -0
- package/lib/parse/index.js +46 -0
- package/lib/parse/lexer.js +390 -0
- package/lib/parse/parse-source.js +912 -0
- package/lib/typecheck/adt-autogen-sigs.js +345 -0
- package/lib/typecheck/build-type-env.js +148 -0
- package/lib/typecheck/builtin-signatures.js +183 -0
- package/lib/typecheck/check-word-body.js +1021 -0
- package/lib/typecheck/effect-decl.js +124 -0
- package/lib/typecheck/index.js +55 -0
- package/lib/typecheck/normalize-sig.js +369 -0
- package/lib/typecheck/stack-step-snapshots.js +56 -0
- package/lib/typecheck/unify-type.js +665 -0
- package/lib/typecheck/validate-adt.js +201 -0
- package/package.json +4 -9
- package/scripts/regen-demo-full-syntax-ast.mjs +22 -0
- package/test/cli/sail-cli.test.js +64 -0
- package/test/codegen/compile-bracket-ffi-e2e.test.js +64 -0
- package/test/codegen/compile-stage0.test.js +128 -0
- package/test/codegen/compile-stage4-layout.test.js +124 -0
- package/test/codegen/e2e-prelude-ffi-adt/app/extra.sail +6 -0
- package/test/codegen/e2e-prelude-ffi-adt/app/lib.sail +34 -0
- package/test/codegen/e2e-prelude-ffi-adt/app/main.sail +28 -0
- package/test/codegen/e2e-prelude-ffi-adt/artifacts/.gitignore +2 -0
- package/test/codegen/e2e-prelude-ffi-adt/ffi/helpers.js +27 -0
- package/test/codegen/e2e-prelude-ffi-adt.test.js +100 -0
- package/test/codegen/emit-adt-stage6.test.js +168 -0
- package/test/codegen/emit-async-stage5.test.js +164 -0
- package/test/codegen/emit-body-stage2.test.js +139 -0
- package/test/codegen/emit-body.test.js +163 -0
- package/test/codegen/emit-builtins-stage7.test.js +258 -0
- package/test/codegen/emit-diagnostics-stage9.test.js +90 -0
- package/test/codegen/emit-jsdoc-stage8.test.js +113 -0
- package/test/codegen/emit-module-stage3.test.js +78 -0
- package/test/conformance/conformance-ir-l4.test.js +38 -0
- package/test/conformance/conformance-l5-codegen.test.js +111 -0
- package/test/conformance/conformance-runner.js +91 -0
- package/test/conformance/conformance-suite-l3.test.js +32 -0
- package/test/ffi/prelude-jsdoc.test.js +49 -0
- package/test/fixtures/demo-full-syntax.ast.json +1471 -0
- package/test/fixtures/demo-full-syntax.sail +35 -0
- package/test/fixtures/io-node-ffi-adt/ffi.js +7 -0
- package/test/fixtures/io-node-ffi-adt/use.sail +4 -0
- package/test/fixtures/io-node-mini/dep.sail +2 -0
- package/test/fixtures/io-node-mini/entry.sail +4 -0
- package/test/fixtures/io-node-prelude/entry.sail +4 -0
- package/test/fixtures/io-node-reexport-chain/a.sail +4 -0
- package/test/fixtures/io-node-reexport-chain/b.sail +2 -0
- package/test/fixtures/io-node-reexport-chain/c.sail +2 -0
- package/test/io-node/resolve-disk.test.js +59 -0
- package/test/ir/bind-values.test.js +84 -0
- package/test/ir/build-module-ir.test.js +100 -0
- package/test/ir/call-effects.test.js +97 -0
- package/test/ir/ffi-bracket-ir.test.js +59 -0
- package/test/ir/full-ir-document.test.js +51 -0
- package/test/ir/ir-document-assert.js +67 -0
- package/test/ir/lower-body-steps.test.js +90 -0
- package/test/ir/module-metadata.test.js +42 -0
- package/test/ir/serialization-model.test.js +172 -0
- package/test/ir/stitch-types.test.js +74 -0
- package/test/names/l2-resolve-adt-autogen.test.js +155 -0
- package/test/names/l2-resolve-bracket-ffi.test.js +108 -0
- package/test/names/l2-resolve-declaration-and-bracket-errors.test.js +276 -0
- package/test/names/l2-resolve-graph.test.js +105 -0
- package/test/names/l2-resolve-single-file.test.js +79 -0
- package/test/parse/ast-spec.test.js +56 -0
- package/test/parse/ast.test.js +476 -0
- package/test/parse/contract.test.js +37 -0
- package/test/parse/fixtures-full-syntax.test.js +24 -0
- package/test/parse/helpers.js +27 -0
- package/test/parse/l0-lex-diagnostics-matrix.test.js +59 -0
- package/test/parse/l0-lex.test.js +40 -0
- package/test/parse/l1-diagnostics.test.js +77 -0
- package/test/parse/l1-import.test.js +28 -0
- package/test/parse/l1-parse-diagnostics-matrix.test.js +32 -0
- package/test/parse/l1-top-level.test.js +47 -0
- package/test/parse/l1-types.test.js +31 -0
- package/test/parse/l1-words.test.js +49 -0
- package/test/parse/l2-diagnostics-contract.test.js +67 -0
- package/test/parse/l3-diagnostics-contract.test.js +66 -0
- package/test/typecheck/adt-decl-stage2.test.js +83 -0
- package/test/typecheck/container-contract-e1309.test.js +258 -0
- package/test/typecheck/ffi-bracket-l3.test.js +61 -0
- package/test/typecheck/l3-diagnostics-matrix.test.js +248 -0
- package/test/typecheck/l3-partial-pipeline.test.js +74 -0
- package/test/typecheck/opaque-ffi-type.test.js +78 -0
- package/test/typecheck/sig-type-stage3.test.js +190 -0
- package/test/typecheck/stack-check-stage4.test.js +149 -0
- package/test/typecheck/stack-check-stage5.test.js +74 -0
- package/test/typecheck/stack-check-stage6.test.js +56 -0
- package/test/typecheck/stack-check-stage7.test.js +160 -0
- package/test/typecheck/stack-check-stage8.test.js +146 -0
- package/test/typecheck/stack-check-stage9.test.js +105 -0
- package/test/typecheck/typecheck-env.test.js +53 -0
- package/test/typecheck/typecheck-pipeline.test.js +37 -0
- package/README.md +0 -37
- package/cli/sail.js +0 -151
- package/cli/typecheck.js +0 -39
- package/docs/ARCHITECTURE.md +0 -50
- package/docs/CHANGELOG.md +0 -18
- package/docs/FFI-GUIDE.md +0 -65
- package/docs/RELEASE.md +0 -36
- package/docs/TESTING.md +0 -86
- package/test/integration.test.js +0 -61
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L3 этап 7: эффекты +Async / +Fail, всплытие (RFC-0.1 §5.7, RFC-typecheck §5.6–5.7).
|
|
3
|
+
*/
|
|
4
|
+
import test from 'brittle'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { typecheckSail } from '../../index.js'
|
|
7
|
+
|
|
8
|
+
const root = path.resolve('/virtual/sail-l3-st7')
|
|
9
|
+
function vfs (files) {
|
|
10
|
+
const norm = Object.fromEntries(
|
|
11
|
+
Object.entries(files).map(([k, v]) => [path.normalize(k), v])
|
|
12
|
+
)
|
|
13
|
+
return (p) => norm[path.normalize(p)] ?? null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
test('L3 stage7: +Async вызывает +Async — ок', function (t) {
|
|
17
|
+
const main = path.join(root, 'async-ok.sail')
|
|
18
|
+
const src = [
|
|
19
|
+
'@asyncCallee ( -> +Async )',
|
|
20
|
+
'',
|
|
21
|
+
'@caller ( -> +Async )',
|
|
22
|
+
' /asyncCallee',
|
|
23
|
+
''
|
|
24
|
+
].join('\n')
|
|
25
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
26
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
27
|
+
const fx = r.env?.definitionEffectsByPath?.get(main)?.get('caller')
|
|
28
|
+
t.ok(fx?.asyncDefinition === true)
|
|
29
|
+
t.end()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('L3 stage7: обёртка с исходящим -Async вызывает +Async — ок', function (t) {
|
|
33
|
+
const main = path.join(root, 'async-wrap.sail')
|
|
34
|
+
const src = [
|
|
35
|
+
'@asyncCallee ( -> +Async )',
|
|
36
|
+
'',
|
|
37
|
+
'@wrap ( -> -Async )',
|
|
38
|
+
' /asyncCallee',
|
|
39
|
+
''
|
|
40
|
+
].join('\n')
|
|
41
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
42
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
43
|
+
t.end()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('L3 stage7: обёртка с исходящим -Fail вызывает +Fail — ок', function (t) {
|
|
47
|
+
const main = path.join(root, 'fail-wrap.sail')
|
|
48
|
+
const src = [
|
|
49
|
+
'@risky ( -> +Fail )',
|
|
50
|
+
'',
|
|
51
|
+
'@wrap ( -> -Fail )',
|
|
52
|
+
' /risky',
|
|
53
|
+
''
|
|
54
|
+
].join('\n')
|
|
55
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
56
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
57
|
+
t.end()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('L3 stage7: синхронное слово вызывает +Async — E1310', function (t) {
|
|
61
|
+
const main = path.join(root, 'e1310.sail')
|
|
62
|
+
const src = [
|
|
63
|
+
'@asyncCallee ( -> +Async )',
|
|
64
|
+
'',
|
|
65
|
+
'@sync ( -> )',
|
|
66
|
+
' /asyncCallee',
|
|
67
|
+
''
|
|
68
|
+
].join('\n')
|
|
69
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
70
|
+
t.ok(r.ok === false)
|
|
71
|
+
t.ok(r.diagnostics.some((d) => d.code === 'E1310'), r.diagnostics.map((d) => d.code).join(','))
|
|
72
|
+
t.end()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('L3 stage7: вызов +Fail без +Fail у вызывающего — E1312', function (t) {
|
|
76
|
+
const main = path.join(root, 'e1312.sail')
|
|
77
|
+
const src = [
|
|
78
|
+
'@mayFail ( -> +Fail )',
|
|
79
|
+
'',
|
|
80
|
+
'@sync ( -> )',
|
|
81
|
+
' /mayFail',
|
|
82
|
+
''
|
|
83
|
+
].join('\n')
|
|
84
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
85
|
+
t.ok(r.ok === false)
|
|
86
|
+
t.ok(r.diagnostics.some((d) => d.code === 'E1312'), r.diagnostics.map((d) => d.code).join(','))
|
|
87
|
+
t.end()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('L3 stage7: quotation + call всплывает +Async — без +Async у слова E1310', function (t) {
|
|
91
|
+
const main = path.join(root, 'q-call-async-bad.sail')
|
|
92
|
+
const src = [
|
|
93
|
+
'@asyncCallee ( -> +Async )',
|
|
94
|
+
'',
|
|
95
|
+
'@bad ( -> )',
|
|
96
|
+
' ( /asyncCallee )',
|
|
97
|
+
' call',
|
|
98
|
+
''
|
|
99
|
+
].join('\n')
|
|
100
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
101
|
+
t.ok(r.ok === false)
|
|
102
|
+
t.ok(r.diagnostics.some((d) => d.code === 'E1310'), r.diagnostics.map((d) => d.code).join(','))
|
|
103
|
+
t.end()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('L3 stage7: quotation + call с +Async у внешнего слова — ок', function (t) {
|
|
107
|
+
const main = path.join(root, 'q-call-async-good.sail')
|
|
108
|
+
const src = [
|
|
109
|
+
'@asyncCallee ( -> +Async )',
|
|
110
|
+
'',
|
|
111
|
+
'@good ( -> +Async )',
|
|
112
|
+
' ( /asyncCallee )',
|
|
113
|
+
' call',
|
|
114
|
+
''
|
|
115
|
+
].join('\n')
|
|
116
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
117
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
118
|
+
t.end()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('L3 stage7: callSiteEffectMarks — calleeAsync на вызове', function (t) {
|
|
122
|
+
const main = path.join(root, 'marks.sail')
|
|
123
|
+
const src = [
|
|
124
|
+
'@asyncCallee ( -> +Async )',
|
|
125
|
+
'',
|
|
126
|
+
'@caller ( -> +Async )',
|
|
127
|
+
' /asyncCallee',
|
|
128
|
+
''
|
|
129
|
+
].join('\n')
|
|
130
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
131
|
+
t.ok(r.ok)
|
|
132
|
+
const marks = r.env?.callSiteEffectMarks ?? []
|
|
133
|
+
const ref = marks.find(
|
|
134
|
+
(m) => m.kind === 'word_ref' && m.name === 'asyncCallee'
|
|
135
|
+
)
|
|
136
|
+
t.ok(ref?.calleeAsync === true)
|
|
137
|
+
t.ok(ref?.callerWord === 'caller')
|
|
138
|
+
t.end()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
test('L3 stage7: callSiteEffectMarks — под -Async calleeAsync не ставится (вариант B)', function (t) {
|
|
142
|
+
const main = path.join(root, 'marks-wrap-async.sail')
|
|
143
|
+
const src = [
|
|
144
|
+
'@asyncCallee ( -> +Async )',
|
|
145
|
+
'',
|
|
146
|
+
'@wrap ( -> -Async )',
|
|
147
|
+
' /asyncCallee',
|
|
148
|
+
''
|
|
149
|
+
].join('\n')
|
|
150
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
151
|
+
t.ok(r.ok)
|
|
152
|
+
const marks = r.env?.callSiteEffectMarks ?? []
|
|
153
|
+
const ref = marks.find(
|
|
154
|
+
(m) => m.kind === 'word_ref' && m.name === 'asyncCallee'
|
|
155
|
+
)
|
|
156
|
+
t.ok(ref)
|
|
157
|
+
t.is(ref.calleeAsync, false)
|
|
158
|
+
t.is(ref.callerWord, 'wrap')
|
|
159
|
+
t.end()
|
|
160
|
+
})
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L3 этап 8: автоген ADT и eliminator-ы (RFC-0.1 §7, RFC-typecheck §5.8).
|
|
3
|
+
*/
|
|
4
|
+
import test from 'brittle'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { typecheckSail } from '../../index.js'
|
|
7
|
+
|
|
8
|
+
const root = path.resolve('/virtual/sail-l3-st8')
|
|
9
|
+
function vfs (files) {
|
|
10
|
+
const norm = Object.fromEntries(
|
|
11
|
+
Object.entries(files).map(([k, v]) => [path.normalize(k), v])
|
|
12
|
+
)
|
|
13
|
+
return (p) => norm[path.normalize(p)] ?? null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
test('L3 stage8: sum /nothing — ( -> (Maybe a) )', function (t) {
|
|
17
|
+
const main = path.join(root, 'sum-nothing.sail')
|
|
18
|
+
const src = [
|
|
19
|
+
'&Maybe a',
|
|
20
|
+
'| Nothing',
|
|
21
|
+
'| Just a',
|
|
22
|
+
'',
|
|
23
|
+
'@w ( -> (Maybe a) )',
|
|
24
|
+
' /nothing',
|
|
25
|
+
''
|
|
26
|
+
].join('\n')
|
|
27
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
28
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
29
|
+
t.end()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('L3 stage8: sum /just — ( a -> (Maybe a) )', function (t) {
|
|
33
|
+
const main = path.join(root, 'sum-just.sail')
|
|
34
|
+
const src = [
|
|
35
|
+
'&Maybe a',
|
|
36
|
+
'| Nothing',
|
|
37
|
+
'| Just a',
|
|
38
|
+
'',
|
|
39
|
+
'@w ( Num -> (Maybe a) )',
|
|
40
|
+
' /just',
|
|
41
|
+
''
|
|
42
|
+
].join('\n')
|
|
43
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
44
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
45
|
+
t.end()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('L3 stage8: eliminator /maybe — согласованные ветки', function (t) {
|
|
49
|
+
const main = path.join(root, 'sum-maybe-ok.sail')
|
|
50
|
+
const src = [
|
|
51
|
+
'&Maybe a',
|
|
52
|
+
'| Nothing',
|
|
53
|
+
'| Just a',
|
|
54
|
+
'',
|
|
55
|
+
'@nothingQuot ( -> q:( ~s -> ~s ) )',
|
|
56
|
+
' ( )',
|
|
57
|
+
'',
|
|
58
|
+
'@justQuot ( -> q:( ~s a -> ~s ) )',
|
|
59
|
+
' ( drop )',
|
|
60
|
+
'',
|
|
61
|
+
'@w ( ~s (Maybe a) -> ~s )',
|
|
62
|
+
' /nothingQuot',
|
|
63
|
+
' /justQuot',
|
|
64
|
+
' /maybe',
|
|
65
|
+
''
|
|
66
|
+
].join('\n')
|
|
67
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
68
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
69
|
+
t.end()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('L3 stage8: eliminator — не хватает quotation — E1308', function (t) {
|
|
73
|
+
const main = path.join(root, 'sum-maybe-e1308.sail')
|
|
74
|
+
const src = [
|
|
75
|
+
'&Maybe a',
|
|
76
|
+
'| Nothing',
|
|
77
|
+
'| Just a',
|
|
78
|
+
'',
|
|
79
|
+
'@w ( ~s (Maybe a) -> ~s )',
|
|
80
|
+
' /maybe',
|
|
81
|
+
''
|
|
82
|
+
].join('\n')
|
|
83
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
84
|
+
t.ok(r.ok === false)
|
|
85
|
+
const codes = r.diagnostics.map((d) => d.code)
|
|
86
|
+
t.ok(codes.includes('E1308'), codes.join(','))
|
|
87
|
+
t.end()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('L3 stage8: eliminator — несовместимые ветки — E1307', function (t) {
|
|
91
|
+
const main = path.join(root, 'sum-maybe-e1307.sail')
|
|
92
|
+
const src = [
|
|
93
|
+
'&Maybe a',
|
|
94
|
+
'| Nothing',
|
|
95
|
+
'| Just a',
|
|
96
|
+
'',
|
|
97
|
+
'@w ( ~s (Maybe a) -> ~s Num )',
|
|
98
|
+
' ( )',
|
|
99
|
+
' ( )',
|
|
100
|
+
' /maybe',
|
|
101
|
+
''
|
|
102
|
+
].join('\n')
|
|
103
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
104
|
+
t.ok(r.ok === false)
|
|
105
|
+
t.ok(r.diagnostics.some((d) => d.code === 'E1307'), r.diagnostics.map((d) => d.code).join(','))
|
|
106
|
+
t.end()
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('L3 stage8: product /point и /withPoint', function (t) {
|
|
110
|
+
const main = path.join(root, 'prod-point.sail')
|
|
111
|
+
const src = [
|
|
112
|
+
'&Point',
|
|
113
|
+
':x Num',
|
|
114
|
+
':y Num',
|
|
115
|
+
'',
|
|
116
|
+
'@q ( -> q:( ~s Num Num -> ~s ) )',
|
|
117
|
+
' ( nip drop )',
|
|
118
|
+
'',
|
|
119
|
+
'@w ( Point -> )',
|
|
120
|
+
' /q',
|
|
121
|
+
' /withPoint',
|
|
122
|
+
'',
|
|
123
|
+
'@t ( -> Point )',
|
|
124
|
+
' 1 2 /point',
|
|
125
|
+
''
|
|
126
|
+
].join('\n')
|
|
127
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
128
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
129
|
+
t.end()
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('L3 stage8: product геттер /xPoint', function (t) {
|
|
133
|
+
const main = path.join(root, 'prod-getter.sail')
|
|
134
|
+
const src = [
|
|
135
|
+
'&Point',
|
|
136
|
+
':x Num',
|
|
137
|
+
':y Num',
|
|
138
|
+
'',
|
|
139
|
+
'@w ( Point -> Num )',
|
|
140
|
+
' /xPoint',
|
|
141
|
+
''
|
|
142
|
+
].join('\n')
|
|
143
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
144
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
145
|
+
t.end()
|
|
146
|
+
})
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L3 этап 9: снимки стека для lowering (RFC-IR-0.1 §3, RFC-typecheck-0.1 §3, §6).
|
|
3
|
+
*/
|
|
4
|
+
import test from 'brittle'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { typecheckSail } from '../../index.js'
|
|
7
|
+
import { verifySnapshotChainStitches } from '../../lib/typecheck/stack-step-snapshots.js'
|
|
8
|
+
|
|
9
|
+
const root = path.resolve('/virtual/sail-l3-st9')
|
|
10
|
+
function vfs (files) {
|
|
11
|
+
const norm = Object.fromEntries(
|
|
12
|
+
Object.entries(files).map(([k, v]) => [path.normalize(k), v])
|
|
13
|
+
)
|
|
14
|
+
return (p) => norm[path.normalize(p)] ?? null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test('L3 stage9: цепочка pre/post для литерал + dup', function (t) {
|
|
18
|
+
const main = path.join(root, 'snap-dup.sail')
|
|
19
|
+
const src = ['@w ( -> Num Num )', '', ' 1', '', ' dup', ''].join('\n')
|
|
20
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
21
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
22
|
+
const rec = r.env.stackSnapshotsByPath.get(main).get('w')
|
|
23
|
+
t.is(rec.steps.length, 2)
|
|
24
|
+
t.ok(verifySnapshotChainStitches(rec))
|
|
25
|
+
t.is(rec.steps[0].pre.length, 0)
|
|
26
|
+
t.is(rec.steps[0].post.length, 1)
|
|
27
|
+
t.is(rec.steps[0].post[0].kind, 'prim')
|
|
28
|
+
t.is(rec.steps[0].post[0].name, 'Num')
|
|
29
|
+
t.is(rec.steps[1].pre.length, 1)
|
|
30
|
+
t.is(rec.steps[1].post.length, 2)
|
|
31
|
+
t.end()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('L3 stage9: вложенная quotation — nestedByParentStep', function (t) {
|
|
35
|
+
const main = path.join(root, 'snap-nested.sail')
|
|
36
|
+
const src = ['@w ( -> ( -> Num ) )', '', ' ( 1 )', ''].join('\n')
|
|
37
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
38
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
39
|
+
const rec = r.env.stackSnapshotsByPath.get(main).get('w')
|
|
40
|
+
t.is(rec.steps.length, 1)
|
|
41
|
+
const nested = rec.nestedByParentStep.get(0)
|
|
42
|
+
t.ok(nested)
|
|
43
|
+
t.is(nested.steps.length, 1)
|
|
44
|
+
t.ok(verifySnapshotChainStitches(rec))
|
|
45
|
+
t.ok(verifySnapshotChainStitches(nested))
|
|
46
|
+
t.end()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('L3 stage9: полиморфный вызов — снимок с Num', function (t) {
|
|
50
|
+
const main = path.join(root, 'snap-poly.sail')
|
|
51
|
+
const src = [
|
|
52
|
+
'@id ( a -> a )',
|
|
53
|
+
'',
|
|
54
|
+
'@w ( Num -> Num )',
|
|
55
|
+
'',
|
|
56
|
+
' /id',
|
|
57
|
+
''
|
|
58
|
+
].join('\n')
|
|
59
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
60
|
+
t.ok(r.ok, r.diagnostics?.map((d) => d.message).join('; '))
|
|
61
|
+
const rec = r.env.stackSnapshotsByPath.get(main).get('w')
|
|
62
|
+
t.is(rec.steps.length, 1)
|
|
63
|
+
t.is(rec.steps[0].pre[0].kind, 'prim')
|
|
64
|
+
t.is(rec.steps[0].pre[0].name, 'Num')
|
|
65
|
+
t.is(rec.steps[0].post[0].kind, 'prim')
|
|
66
|
+
t.is(rec.steps[0].post[0].name, 'Num')
|
|
67
|
+
t.end()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('L3 stage9: при ошибке тела — частичный снимок (pre на шаге сбоя) и второе слово проверяется', function (t) {
|
|
71
|
+
const main = path.join(root, 'snap-fail.sail')
|
|
72
|
+
const src = [
|
|
73
|
+
'@bad ( -> )',
|
|
74
|
+
'',
|
|
75
|
+
' drop',
|
|
76
|
+
'',
|
|
77
|
+
'@ok ( -> )',
|
|
78
|
+
'',
|
|
79
|
+
''
|
|
80
|
+
].join('\n')
|
|
81
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
82
|
+
t.ok(r.ok === false)
|
|
83
|
+
const per = r.env.stackSnapshotsByPath.get(main)
|
|
84
|
+
t.ok(per)
|
|
85
|
+
const badRec = per.get('bad')
|
|
86
|
+
t.ok(badRec, 'RFC-typecheck §3: снимок для слова с ошибкой в теле')
|
|
87
|
+
t.ok(badRec.steps[0].pre)
|
|
88
|
+
t.ok(badRec.steps[0].post === undefined, 'post на ошибочном шаге не обязателен')
|
|
89
|
+
t.ok(per.has('ok'))
|
|
90
|
+
t.is(per.get('ok').steps.length, 0)
|
|
91
|
+
t.end()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('L3 stage9: post[last] согласуется с объявленным выходом слова', function (t) {
|
|
95
|
+
const main = path.join(root, 'snap-out.sail')
|
|
96
|
+
const src = ['@w ( -> Num Num )', '', ' 1', '', ' dup', ''].join('\n')
|
|
97
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
98
|
+
t.ok(r.ok)
|
|
99
|
+
const rec = r.env.stackSnapshotsByPath.get(main).get('w')
|
|
100
|
+
const lastPost = rec.steps[rec.steps.length - 1].post
|
|
101
|
+
t.is(lastPost.length, 2)
|
|
102
|
+
t.is(lastPost[0].name, 'Num')
|
|
103
|
+
t.is(lastPost[1].name, 'Num')
|
|
104
|
+
t.end()
|
|
105
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L3 этап 1: окружение typecheck — wordDeclByPath, scopeByPath, топо-порядок модулей.
|
|
3
|
+
*/
|
|
4
|
+
import test from 'brittle'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { typecheckSail } from '../../index.js'
|
|
7
|
+
import { resolveExportedWord } from '../../lib/typecheck/build-type-env.js'
|
|
8
|
+
|
|
9
|
+
const root = path.resolve('/virtual/sail-l3-env')
|
|
10
|
+
function vfs (files) {
|
|
11
|
+
const norm = Object.fromEntries(
|
|
12
|
+
Object.entries(files).map(([k, v]) => [path.normalize(k), v])
|
|
13
|
+
)
|
|
14
|
+
return (p) => norm[path.normalize(p)] ?? null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test('typecheck env: один модуль — слова и scope.unq', function (t) {
|
|
18
|
+
const main = path.join(root, 'main.sail')
|
|
19
|
+
const src = ['@noop ( -> )', '', '@main ( -> )', '', ' /noop'].join('\n')
|
|
20
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
21
|
+
t.ok(r.ok)
|
|
22
|
+
const env = r.env
|
|
23
|
+
t.ok(env, 'env')
|
|
24
|
+
const words = env.wordDeclByPath.get(main)
|
|
25
|
+
t.ok(words?.get('main')?.signature, 'main signature')
|
|
26
|
+
t.ok(Array.isArray(words?.get('main')?.body), 'main body')
|
|
27
|
+
t.ok(words?.get('noop')?.signature, 'noop signature')
|
|
28
|
+
const sc = env.scopeByPath.get(main)
|
|
29
|
+
t.ok(sc?.unq?.has('main') && sc.unq.has('noop'), 'unq')
|
|
30
|
+
t.end()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('typecheck env: два модуля — dep раньше entry в modulePathsOrdered, importMap', function (t) {
|
|
34
|
+
const main = path.join(root, 'main.sail')
|
|
35
|
+
const lib = path.join(root, 'lib.sail')
|
|
36
|
+
const files = {
|
|
37
|
+
[main]: ['+Lib ./lib.sail', '@main ( -> )', '', ' ~Lib/hello'].join('\n'),
|
|
38
|
+
[lib]: ['@hello ( -> )', '', ''].join('\n')
|
|
39
|
+
}
|
|
40
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs(files) })
|
|
41
|
+
t.ok(r.ok, r.diagnostics.map((d) => d.code + ' ' + d.message).join('; '))
|
|
42
|
+
const env = r.env
|
|
43
|
+
const order = env.modulePathsOrdered
|
|
44
|
+
const iLib = order.indexOf(lib)
|
|
45
|
+
const iMain = order.indexOf(main)
|
|
46
|
+
t.ok(iLib >= 0 && iMain >= 0)
|
|
47
|
+
t.ok(iLib < iMain, 'lib before main in topo order')
|
|
48
|
+
const dep = env.scopeByPath.get(main).importMap.get('Lib')
|
|
49
|
+
t.ok(dep && dep.path === lib, 'importMap Lib -> lib snapshot')
|
|
50
|
+
const w = resolveExportedWord(dep, 'hello')
|
|
51
|
+
t.ok(w && w.name === 'hello', 'resolveExportedWord')
|
|
52
|
+
t.end()
|
|
53
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L3 этап 0: вход после L2, проброс диагностик при падении имён, заглушка при успехе.
|
|
3
|
+
*/
|
|
4
|
+
import test from 'brittle'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { typecheckSail } from '../../index.js'
|
|
7
|
+
|
|
8
|
+
const root = path.resolve('/virtual/sail-l3')
|
|
9
|
+
function vfs (files) {
|
|
10
|
+
const norm = Object.fromEntries(
|
|
11
|
+
Object.entries(files).map(([k, v]) => [path.normalize(k), v])
|
|
12
|
+
)
|
|
13
|
+
return (p) => norm[path.normalize(p)] ?? null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
test('typecheckSail: при ошибке L2 те же diagnostics, без snapshots', function (t) {
|
|
17
|
+
const main = path.join(root, 'main.sail')
|
|
18
|
+
const src = '@main ( -> )\n\n /nope\n'
|
|
19
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
20
|
+
t.ok(r.ok === false)
|
|
21
|
+
t.ok(!('snapshots' in r) || r.snapshots === undefined)
|
|
22
|
+
const d = r.diagnostics.find((x) => x.code === 'E1201')
|
|
23
|
+
t.ok(d, 'E1201')
|
|
24
|
+
t.end()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('typecheckSail: при успехе L2 ok и пустые diagnostics L3, есть snapshots', function (t) {
|
|
28
|
+
const main = path.join(root, 'main.sail')
|
|
29
|
+
const src = ['@noop ( -> )', '', '@main ( -> )', '', ' /noop'].join('\n')
|
|
30
|
+
const r = typecheckSail({ entryPath: main, readFile: vfs({ [main]: src }) })
|
|
31
|
+
t.ok(r.ok, r.diagnostics.map((d) => d.code + ' ' + d.message).join('; '))
|
|
32
|
+
t.is(r.diagnostics.length, 0)
|
|
33
|
+
t.ok(r.snapshots instanceof Map)
|
|
34
|
+
t.ok(r.snapshots.has(main))
|
|
35
|
+
t.ok(r.env && r.env.wordDeclByPath?.get(main)?.has('main'), 'env этап 1')
|
|
36
|
+
t.end()
|
|
37
|
+
})
|
package/README.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# @algosail/lang
|
|
2
|
-
|
|
3
|
-
Sail language tooling. Re-exports parser and typecheck.
|
|
4
|
-
|
|
5
|
-
## CLI
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
sail check <file.sail> # Typecheck
|
|
9
|
-
sail compile <file.sail> [options] # Compile to JS
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
Options for `compile`: `--out <file.js>`, `--no-source-map`, `-` (stdout).
|
|
13
|
-
|
|
14
|
-
## Exports
|
|
15
|
-
|
|
16
|
-
```javascript
|
|
17
|
-
import { createParser, typecheck } from '@algosail/lang'
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Docs
|
|
21
|
-
|
|
22
|
-
- [FFI-GUIDE.md](docs/FFI-GUIDE.md) — JSDoc format for FFI modules
|
|
23
|
-
- [ARCHITECTURE.md](docs/ARCHITECTURE.md) — Pipeline, packages, dependencies
|
|
24
|
-
- [TESTING.md](docs/TESTING.md) — Brittle setup, integration tests
|
|
25
|
-
- [CHANGELOG.md](docs/CHANGELOG.md) — Version history
|
|
26
|
-
- [RELEASE.md](docs/RELEASE.md) — Release checklist
|
|
27
|
-
|
|
28
|
-
## Packages
|
|
29
|
-
|
|
30
|
-
| Package | Description |
|
|
31
|
-
|---------|-------------|
|
|
32
|
-
| @algosail/parser | Parse Sail + JS → symbol table |
|
|
33
|
-
| @algosail/typecheck | Type checking |
|
|
34
|
-
| @algosail/compiler | Sail → JS (CLI: `sail-compile`) |
|
|
35
|
-
| @algosail/builtins | Builtin words |
|
|
36
|
-
| @algosail/tree-sitter | Grammar |
|
|
37
|
-
| @algosail/lsp | LSP server (hover, completion, go-to-definition) |
|
package/cli/sail.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Sail CLI — sail check | sail compile
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const args = process.argv.slice(2)
|
|
8
|
-
const cmd = args[0]
|
|
9
|
-
|
|
10
|
-
if (!cmd || cmd === '-h' || cmd === '--help') {
|
|
11
|
-
console.log(`sail - Sail language tooling
|
|
12
|
-
|
|
13
|
-
Usage:
|
|
14
|
-
sail check <file.sail> Typecheck a Sail file
|
|
15
|
-
sail compile <file.sail> Compile Sail to JavaScript
|
|
16
|
-
|
|
17
|
-
Options for compile:
|
|
18
|
-
--out <file.js> Output path (default: input.js)
|
|
19
|
-
--no-source-map Disable source map generation
|
|
20
|
-
- Write to stdout
|
|
21
|
-
|
|
22
|
-
Examples:
|
|
23
|
-
sail check main.sail
|
|
24
|
-
sail compile main.sail --out dist/main.js
|
|
25
|
-
sail compile main.sail -
|
|
26
|
-
`)
|
|
27
|
-
process.exit(cmd === '-h' || cmd === '--help' ? 0 : 1)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const rest = args.slice(1)
|
|
31
|
-
|
|
32
|
-
if (cmd === 'check') {
|
|
33
|
-
await runCheck(rest)
|
|
34
|
-
} else if (cmd === 'compile') {
|
|
35
|
-
await runCompile(rest)
|
|
36
|
-
} else {
|
|
37
|
-
console.error(`sail: unknown command '${cmd}'. Use 'sail check' or 'sail compile'.`)
|
|
38
|
-
process.exit(1)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function runCheck(args) {
|
|
42
|
-
const filePath = args.find((a) => !a.startsWith('-'))
|
|
43
|
-
if (!filePath) {
|
|
44
|
-
console.error('Usage: sail check <path-to-file.sail>')
|
|
45
|
-
process.exit(1)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const { createParser } = await import('@algosail/parser')
|
|
49
|
-
const { typecheck } = await import('@algosail/typecheck')
|
|
50
|
-
const { readFile } = await import('node:fs/promises')
|
|
51
|
-
const { pathToFileURL } = await import('node:url')
|
|
52
|
-
const { resolve } = await import('node:path')
|
|
53
|
-
const { formatError } = await import('@algosail/compiler/lib/formatError.js')
|
|
54
|
-
const { addErrorHints } = await import('@algosail/compiler/lib/errorHints.js')
|
|
55
|
-
|
|
56
|
-
const absPath = resolve(process.cwd(), filePath)
|
|
57
|
-
const uri = pathToFileURL(absPath).href
|
|
58
|
-
|
|
59
|
-
let text
|
|
60
|
-
try {
|
|
61
|
-
text = await readFile(absPath, 'utf8')
|
|
62
|
-
} catch (err) {
|
|
63
|
-
console.error(`sail check: cannot read ${filePath}: ${err.message}`)
|
|
64
|
-
process.exit(1)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const parser = await createParser()
|
|
68
|
-
const result = await parser.parseSail(uri, text)
|
|
69
|
-
const parseErrors = (result.errors ?? []).map((e) => ({
|
|
70
|
-
message: e.type === 'error' ? `Parse error: ${e.text}` : `Parse error: missing ${e.text}`,
|
|
71
|
-
startPosition: e.startPosition,
|
|
72
|
-
uri,
|
|
73
|
-
}))
|
|
74
|
-
const typeErrors = typecheck(result).map((e) => {
|
|
75
|
-
const hint = addErrorHints(e)
|
|
76
|
-
return { ...e, uri, ...(hint && { hint }) }
|
|
77
|
-
})
|
|
78
|
-
const errors = [...parseErrors, ...typeErrors]
|
|
79
|
-
|
|
80
|
-
if (errors.length > 0) {
|
|
81
|
-
for (const err of errors) {
|
|
82
|
-
console.error(formatError({ ...err, uri }, uri))
|
|
83
|
-
}
|
|
84
|
-
process.exit(1)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function runCompile(args) {
|
|
89
|
-
let inputPath = null
|
|
90
|
-
let outPath = null
|
|
91
|
-
|
|
92
|
-
for (let i = 0; i < args.length; i++) {
|
|
93
|
-
if (args[i] === '--out' && args[i + 1]) {
|
|
94
|
-
outPath = args[i + 1]
|
|
95
|
-
i++
|
|
96
|
-
} else if (!args[i].startsWith('-')) {
|
|
97
|
-
inputPath = args[i]
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (!inputPath) {
|
|
102
|
-
console.error('Usage: sail compile <input.sail> [--out <output.js>] [--no-source-map]')
|
|
103
|
-
process.exit(1)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const { compile } = await import('@algosail/compiler')
|
|
107
|
-
const { formatError } = await import('@algosail/compiler/lib/formatError.js')
|
|
108
|
-
const { readFile, writeFile } = await import('node:fs/promises')
|
|
109
|
-
const { dirname, resolve, basename } = await import('node:path')
|
|
110
|
-
const { pathToFileURL } = await import('node:url')
|
|
111
|
-
|
|
112
|
-
const absPath = resolve(process.cwd(), inputPath)
|
|
113
|
-
const uri = pathToFileURL(absPath).href
|
|
114
|
-
|
|
115
|
-
let source
|
|
116
|
-
try {
|
|
117
|
-
source = await readFile(absPath, 'utf8')
|
|
118
|
-
} catch (err) {
|
|
119
|
-
console.error(`sail compile: cannot read ${inputPath}: ${err.message}`)
|
|
120
|
-
process.exit(1)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const outputPath = outPath ?? inputPath.replace(/\.sail$/, '.js')
|
|
124
|
-
const compileOpts = {
|
|
125
|
-
outPath: outputPath !== '-' ? dirname(resolve(process.cwd(), outputPath)) : dirname(absPath),
|
|
126
|
-
sourceMap: outputPath !== '-' && !args.includes('--no-source-map'),
|
|
127
|
-
outFile: outputPath !== '-' ? resolve(process.cwd(), outputPath) : null,
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const result = await compile(uri, source, compileOpts)
|
|
131
|
-
|
|
132
|
-
if (result.errors.length > 0) {
|
|
133
|
-
for (const err of result.errors) {
|
|
134
|
-
console.error(formatError({ ...err, uri }, uri))
|
|
135
|
-
}
|
|
136
|
-
process.exit(1)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (outputPath === '-') {
|
|
140
|
-
process.stdout.write(result.js)
|
|
141
|
-
} else {
|
|
142
|
-
let js = result.js
|
|
143
|
-
if (result.sourceMap) {
|
|
144
|
-
const mapPath = outputPath + '.map'
|
|
145
|
-
await writeFile(mapPath, result.sourceMap, 'utf8')
|
|
146
|
-
js += `\n//# sourceMappingURL=${basename(mapPath)}`
|
|
147
|
-
}
|
|
148
|
-
await writeFile(outputPath, js, 'utf8')
|
|
149
|
-
console.log(`Compiled to ${outputPath}`)
|
|
150
|
-
}
|
|
151
|
-
}
|