@pulse-compute/wasm-build-support 0.0.0 → 1.0.0-beta.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 +26 -1
- package/package.json +30 -6
- package/src/artifacts-dir.js +41 -0
- package/src/assemblyscript-compile.js +279 -0
- package/src/files.js +91 -0
- package/src/index.js +7 -0
- package/src/native-optimization.js +50 -0
package/README.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
1
|
# @pulse-compute/wasm-build-support
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<!-- pulse-package-status:start -->
|
|
4
|
+
> **Support tier:** Implementation/transitive surface<br>
|
|
5
|
+
> **Audience:** Pulse release-set and compiler maintainers.<br>
|
|
6
|
+
> **Install directly:** No for application projects; it is installed transitively where required.<br>
|
|
7
|
+
> **Supported entry points:** `None for application authors.`<br>
|
|
8
|
+
> **Stability:** Internal release-set interface; exported modules may change with compiler implementation needs.<br>
|
|
9
|
+
> **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.1/packages/implementation-packages/)
|
|
10
|
+
>
|
|
11
|
+
> This release-status block is generated from the synchronized `Pulse 1.0.0-beta.1` package policy.
|
|
12
|
+
<!-- pulse-package-status:end -->
|
|
13
|
+
|
|
14
|
+
Shared build-time helpers for PulseWasm package-owned builders.
|
|
15
|
+
|
|
16
|
+
This package is intentionally build/tooling scoped. It owns utilities that future package-owned compiler surfaces need without importing `@pulse-compute/wasm-compiler` internals.
|
|
17
|
+
|
|
18
|
+
## Exposed modules
|
|
19
|
+
|
|
20
|
+
- `artifactsDir` → canonical `wasm/artifacts` directory resolution helpers.
|
|
21
|
+
- `assemblyscriptCompile` → AssemblyScript compiler resolution and compile-smoke helper.
|
|
22
|
+
- `files` → shared slash normalization, SHA-256, truncation, ANSI stripping, and file-record helpers.
|
|
23
|
+
|
|
24
|
+
## Boundary rules
|
|
25
|
+
|
|
26
|
+
- May depend on `@pulse-compute/wasm-contracts` for diagnostics/artifact normalization.
|
|
27
|
+
- Must not depend on `@pulse-compute/wasm-compiler`.
|
|
28
|
+
- Must not own the `assemblyscript` dependency; AS is resolved from the caller workspace/package.
|
package/package.json
CHANGED
|
@@ -1,15 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pulse-compute/wasm-build-support",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Inert namespace bootstrap for Pulse.",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
5
4
|
"license": "Apache-2.0",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": "^22.14.0 || ^24.0.0"
|
|
7
|
+
},
|
|
8
|
+
"description": "Shared build-time helpers for PulseWasm package-owned builders.",
|
|
9
|
+
"type": "commonjs",
|
|
10
|
+
"main": "./src/index.js",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./src/index.js",
|
|
13
|
+
"./artifacts-dir": "./src/artifacts-dir.js",
|
|
14
|
+
"./assemblyscript-compile": "./src/assemblyscript-compile.js",
|
|
15
|
+
"./native-optimization": "./src/native-optimization.js",
|
|
16
|
+
"./files": "./src/files.js"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@pulse-compute/wasm-contracts": "1.0.0-beta.1"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
6
24
|
"files": [
|
|
25
|
+
"src",
|
|
7
26
|
"README.md",
|
|
8
27
|
"LICENSE",
|
|
9
28
|
"NOTICE"
|
|
10
29
|
],
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/pulse-compute/pulse.git",
|
|
33
|
+
"directory": "wasm/packages/build-support"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://pulsecompute.io/v1.0.0-beta.1/packages/implementation-packages/",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/pulse-compute/pulse/issues"
|
|
14
38
|
}
|
|
15
|
-
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
function getBuildSupportPackageRoot() {
|
|
7
|
+
return path.resolve(__dirname, '..');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getWasmRoot() {
|
|
11
|
+
const packageRoot = getBuildSupportPackageRoot();
|
|
12
|
+
const candidate = path.resolve(packageRoot, '..', '..');
|
|
13
|
+
const manifestPath = path.join(candidate, 'package.json');
|
|
14
|
+
if (path.basename(path.dirname(packageRoot)) === 'packages' && fs.existsSync(manifestPath)) {
|
|
15
|
+
return candidate;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getDefaultArtifactsDir(cwd = process.cwd()) {
|
|
21
|
+
if (process.env.PULSEWASM_ARTIFACTS_DIR) {
|
|
22
|
+
return path.resolve(cwd, process.env.PULSEWASM_ARTIFACTS_DIR);
|
|
23
|
+
}
|
|
24
|
+
const wasmRoot = getWasmRoot();
|
|
25
|
+
if (wasmRoot) {
|
|
26
|
+
return path.join(wasmRoot, 'artifacts');
|
|
27
|
+
}
|
|
28
|
+
return path.join(cwd, 'artifacts');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function resolveArtifactsDir(value, cwd = process.cwd()) {
|
|
32
|
+
if (!value) return getDefaultArtifactsDir(cwd);
|
|
33
|
+
return path.resolve(cwd, value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
getBuildSupportPackageRoot,
|
|
38
|
+
getWasmRoot,
|
|
39
|
+
getDefaultArtifactsDir,
|
|
40
|
+
resolveArtifactsDir
|
|
41
|
+
};
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { getDefaultArtifactsDir } = require('./artifacts-dir.js');
|
|
7
|
+
const { spawnSync } = require('node:child_process');
|
|
8
|
+
|
|
9
|
+
function loadContractsDiagnostics() {
|
|
10
|
+
try {
|
|
11
|
+
return require('@pulse-compute/wasm-contracts/diagnostics');
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
|
|
14
|
+
return require('../../contracts/src/diagnostics.js');
|
|
15
|
+
}
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { PACKAGE_VERSION, normalizeArtifact } = loadContractsDiagnostics();
|
|
21
|
+
|
|
22
|
+
const ASSEMBLYSCRIPT_COMPILE_VERSION = 'pulsewasm.assemblyscript-compile.v1';
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
fileRecord,
|
|
26
|
+
sha256Text,
|
|
27
|
+
stripAnsi,
|
|
28
|
+
truncate
|
|
29
|
+
} = require('./files.js');
|
|
30
|
+
|
|
31
|
+
function parseAscWarnings(stderr) {
|
|
32
|
+
const text = stripAnsi(stderr);
|
|
33
|
+
const warnings = [];
|
|
34
|
+
const regex = /WARNING\s+(AS\d+):\s+([^\n]+)(?:.|\n)*?in ([^\n]+)\((\d+),(\d+)\)/g;
|
|
35
|
+
let match;
|
|
36
|
+
while ((match = regex.exec(text))) {
|
|
37
|
+
warnings.push({
|
|
38
|
+
code: match[1],
|
|
39
|
+
message: match[2].trim(),
|
|
40
|
+
file: match[3],
|
|
41
|
+
line: Number(match[4]),
|
|
42
|
+
column: Number(match[5])
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (warnings.length === 0 && /WARNING\s+AS\d+:/m.test(text)) {
|
|
46
|
+
warnings.push({
|
|
47
|
+
code: 'AS_WARNING',
|
|
48
|
+
message: 'AssemblyScript emitted one or more warnings. Inspect result.stderr for details.'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return warnings;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveAssemblyScriptPackageRoot(cwd) {
|
|
55
|
+
try {
|
|
56
|
+
const packageJson = require.resolve('assemblyscript/package.json', { paths: [cwd] });
|
|
57
|
+
return path.dirname(packageJson);
|
|
58
|
+
} catch (_) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function resolveAsc(cwd) {
|
|
64
|
+
const packageRoot = resolveAssemblyScriptPackageRoot(cwd);
|
|
65
|
+
if (!packageRoot) return undefined;
|
|
66
|
+
const ascJs = path.join(packageRoot, 'bin', 'asc.js');
|
|
67
|
+
if (!fs.existsSync(ascJs)) return undefined;
|
|
68
|
+
return {
|
|
69
|
+
packageRoot,
|
|
70
|
+
executable: process.execPath,
|
|
71
|
+
script: ascJs,
|
|
72
|
+
display: `node ${path.relative(cwd, ascJs).replace(/\\/g, '/')}`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function assemblyScriptVersion(cwd) {
|
|
77
|
+
const packageRoot = resolveAssemblyScriptPackageRoot(cwd);
|
|
78
|
+
if (!packageRoot) return undefined;
|
|
79
|
+
try {
|
|
80
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
81
|
+
return pkg.version;
|
|
82
|
+
} catch (_) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function writeStagedFiles(stagingDir, shape, core) {
|
|
88
|
+
const allFiles = [];
|
|
89
|
+
if (shape && Array.isArray(shape.files)) allFiles.push(...shape.files);
|
|
90
|
+
if (core && Array.isArray(core.files)) allFiles.push(...core.files);
|
|
91
|
+
|
|
92
|
+
// De-duplicate by output path, keeping later/core files when index.as.ts is upgraded by the core generator.
|
|
93
|
+
const byFile = new Map();
|
|
94
|
+
for (const file of allFiles) byFile.set(file.file, file.text);
|
|
95
|
+
|
|
96
|
+
for (const [file, text] of byFile.entries()) {
|
|
97
|
+
const abs = path.join(stagingDir, file);
|
|
98
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
99
|
+
fs.writeFileSync(abs, text, 'utf8');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return Array.from(byFile.entries()).map(([file, text]) => ({
|
|
103
|
+
file,
|
|
104
|
+
bytes: Buffer.byteLength(text, 'utf8'),
|
|
105
|
+
sha256: sha256Text(text)
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function runAscCompile(options) {
|
|
110
|
+
const cwd = options.cwd || process.cwd();
|
|
111
|
+
const outDir = options.outDir || getDefaultArtifactsDir(cwd);
|
|
112
|
+
const shape = options.assemblyScriptShape;
|
|
113
|
+
const core = options.assemblyScriptCore;
|
|
114
|
+
const asc = resolveAsc(cwd);
|
|
115
|
+
|
|
116
|
+
const diagnostics = [];
|
|
117
|
+
if (!asc) {
|
|
118
|
+
diagnostics.push({
|
|
119
|
+
pass: 'assemblyscript-compile',
|
|
120
|
+
code: 'PULSEWASM_ASSEMBLYSCRIPT_ASC_MISSING',
|
|
121
|
+
severity: 'error',
|
|
122
|
+
message: 'AssemblyScript compiler dependency was not found.',
|
|
123
|
+
hint: 'Run corepack pnpm install --frozen-lockfile so the assemblyscript devDependency is available, then rerun the compile smoke test.',
|
|
124
|
+
loc: { file: '<assemblyscript-compile>' }
|
|
125
|
+
});
|
|
126
|
+
return { artifact: buildArtifact({ cwd, outDir, status: 'error', diagnostics, shape, core }), diagnostics };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const stagingDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulsewasm-as-compile-'));
|
|
130
|
+
const outputDir = path.join(outDir, 'generated', 'as-smoke');
|
|
131
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
132
|
+
|
|
133
|
+
const stagedFiles = writeStagedFiles(stagingDir, shape, core);
|
|
134
|
+
const entry = path.join(stagingDir, 'generated/as/index.as.ts');
|
|
135
|
+
const wasmFile = path.join(outputDir, 'pulsewasm-smoke.wasm');
|
|
136
|
+
const watFile = path.join(outputDir, 'pulsewasm-smoke.wat');
|
|
137
|
+
|
|
138
|
+
const ascArgs = [
|
|
139
|
+
asc.script,
|
|
140
|
+
entry,
|
|
141
|
+
'--outFile', wasmFile,
|
|
142
|
+
'--textFile', watFile,
|
|
143
|
+
'--runtime', 'stub',
|
|
144
|
+
'--noAssert'
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
const startedAt = Date.now();
|
|
148
|
+
const proc = spawnSync(asc.executable, ascArgs, {
|
|
149
|
+
cwd,
|
|
150
|
+
encoding: 'utf8',
|
|
151
|
+
maxBuffer: 1024 * 1024 * 12
|
|
152
|
+
});
|
|
153
|
+
const durationMs = Date.now() - startedAt;
|
|
154
|
+
|
|
155
|
+
const warnings = parseAscWarnings(proc.stderr);
|
|
156
|
+
|
|
157
|
+
if (proc.error) {
|
|
158
|
+
diagnostics.push({
|
|
159
|
+
pass: 'assemblyscript-compile',
|
|
160
|
+
code: 'PULSEWASM_ASSEMBLYSCRIPT_COMPILE_SPAWN_FAILED',
|
|
161
|
+
severity: 'error',
|
|
162
|
+
message: `Failed to start AssemblyScript compiler: ${proc.error.message}`,
|
|
163
|
+
hint: 'Verify Node can execute node_modules/assemblyscript/bin/asc.js.',
|
|
164
|
+
loc: { file: '<assemblyscript-compile>' }
|
|
165
|
+
});
|
|
166
|
+
} else if (proc.status !== 0) {
|
|
167
|
+
diagnostics.push({
|
|
168
|
+
pass: 'assemblyscript-compile',
|
|
169
|
+
code: 'PULSEWASM_ASSEMBLYSCRIPT_COMPILE_FAILED',
|
|
170
|
+
severity: 'error',
|
|
171
|
+
message: `AssemblyScript compile smoke test failed with exit code ${proc.status}.`,
|
|
172
|
+
hint: 'Inspect assemblyscript-compile.json stderr/stdout for asc diagnostics. Generated AS must compile before host binding work.',
|
|
173
|
+
loc: { file: '<assemblyscript-compile>' },
|
|
174
|
+
details: {
|
|
175
|
+
exitCode: proc.status,
|
|
176
|
+
stderr: truncate(proc.stderr, 8000),
|
|
177
|
+
stdout: truncate(proc.stdout, 8000)
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const outputs = [];
|
|
183
|
+
if (fs.existsSync(wasmFile)) outputs.push(fileRecord(wasmFile, cwd, 'wasm-smoke-output'));
|
|
184
|
+
if (fs.existsSync(watFile)) outputs.push(fileRecord(watFile, cwd, 'wat-smoke-output'));
|
|
185
|
+
|
|
186
|
+
const artifact = buildArtifact({
|
|
187
|
+
cwd,
|
|
188
|
+
outDir,
|
|
189
|
+
status: diagnostics.length === 0 ? 'ok' : 'error',
|
|
190
|
+
diagnostics,
|
|
191
|
+
warnings,
|
|
192
|
+
shape,
|
|
193
|
+
core,
|
|
194
|
+
asc,
|
|
195
|
+
assemblyScriptVersion: assemblyScriptVersion(cwd),
|
|
196
|
+
stagedFiles,
|
|
197
|
+
outputs,
|
|
198
|
+
command: {
|
|
199
|
+
executable: asc.display,
|
|
200
|
+
args: ascArgs.slice(1).map((arg) => path.isAbsolute(arg) ? path.relative(cwd, arg).replace(/\\/g, '/') : arg)
|
|
201
|
+
},
|
|
202
|
+
result: {
|
|
203
|
+
warnings,
|
|
204
|
+
exitCode: proc.status,
|
|
205
|
+
signal: proc.signal,
|
|
206
|
+
durationMs,
|
|
207
|
+
stdout: truncate(proc.stdout),
|
|
208
|
+
stderr: truncate(proc.stderr)
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return { artifact, diagnostics, outputFiles: outputs };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function buildArtifact(options = {}) {
|
|
216
|
+
const cwd = options.cwd || process.cwd();
|
|
217
|
+
return normalizeArtifact({
|
|
218
|
+
version: ASSEMBLYSCRIPT_COMPILE_VERSION,
|
|
219
|
+
generatedBy: options.generatedBy || PACKAGE_VERSION,
|
|
220
|
+
status: options.status || 'ok',
|
|
221
|
+
sourceAssemblyScriptShapeVersion: options.shape?.artifact?.version,
|
|
222
|
+
sourceAssemblyScriptCoreVersion: options.core?.artifact?.version,
|
|
223
|
+
sourceAssemblyScriptHandlersVersion: options.core?.handlersArtifact?.version,
|
|
224
|
+
policy: {
|
|
225
|
+
phase: '9E',
|
|
226
|
+
sourceOfTruth: 'generated/as/*.as.ts files emitted from validated artifacts',
|
|
227
|
+
reReadsSource: false,
|
|
228
|
+
reInfersRouteSemantics: false,
|
|
229
|
+
compiler: 'assemblyscript asc',
|
|
230
|
+
compilationOnly: true,
|
|
231
|
+
wasmExecution: false,
|
|
232
|
+
hostBindings: false,
|
|
233
|
+
witBindings: false,
|
|
234
|
+
purpose: 'prove generated AssemblyScript is syntactically and type valid before local Wasm execution work'
|
|
235
|
+
},
|
|
236
|
+
compiler: {
|
|
237
|
+
package: 'assemblyscript',
|
|
238
|
+
version: options.assemblyScriptVersion,
|
|
239
|
+
executable: options.asc?.display
|
|
240
|
+
},
|
|
241
|
+
command: options.command,
|
|
242
|
+
result: options.result,
|
|
243
|
+
generated: {
|
|
244
|
+
inputFiles: options.stagedFiles || [],
|
|
245
|
+
outputFiles: options.outputs || []
|
|
246
|
+
},
|
|
247
|
+
diagnostics: options.diagnostics || [],
|
|
248
|
+
warnings: options.warnings || [],
|
|
249
|
+
summary: {
|
|
250
|
+
compiled: options.status === 'ok',
|
|
251
|
+
inputFiles: (options.stagedFiles || []).length,
|
|
252
|
+
outputFiles: (options.outputs || []).length,
|
|
253
|
+
wasmBytes: (options.outputs || []).find((file) => file.kind === 'wasm-smoke-output')?.bytes || 0,
|
|
254
|
+
watBytes: (options.outputs || []).find((file) => file.kind === 'wat-smoke-output')?.bytes || 0,
|
|
255
|
+
diagnostics: (options.diagnostics || []).length,
|
|
256
|
+
warnings: (options.warnings || []).length,
|
|
257
|
+
wasmExecuted: false,
|
|
258
|
+
hostBindings: false
|
|
259
|
+
}
|
|
260
|
+
}, cwd);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function buildAssemblyScriptCompileSmoke(assemblyScriptShape, assemblyScriptCore, options = {}) {
|
|
264
|
+
return {
|
|
265
|
+
version: ASSEMBLYSCRIPT_COMPILE_VERSION,
|
|
266
|
+
...runAscCompile({
|
|
267
|
+
cwd: options.cwd || process.cwd(),
|
|
268
|
+
outDir: options.outDir,
|
|
269
|
+
assemblyScriptShape,
|
|
270
|
+
assemblyScriptCore
|
|
271
|
+
})
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
module.exports = {
|
|
276
|
+
ASSEMBLYSCRIPT_COMPILE_VERSION,
|
|
277
|
+
buildAssemblyScriptCompileSmoke,
|
|
278
|
+
resolveAsc
|
|
279
|
+
};
|
package/src/files.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
|
|
7
|
+
function normalizeSlash(value) {
|
|
8
|
+
return String(value || '').replace(/\\/g, '/');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function sha256Buffer(buffer) {
|
|
12
|
+
return crypto.createHash('sha256').update(buffer).digest('hex');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function sha256Text(text) {
|
|
16
|
+
return crypto.createHash('sha256').update(String(text), 'utf8').digest('hex');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function truncate(value, max = 20000) {
|
|
20
|
+
const text = String(value || '');
|
|
21
|
+
if (text.length <= max) return text;
|
|
22
|
+
return `${text.slice(0, max)}\n... <truncated ${text.length - max} chars>`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function stripAnsi(value) {
|
|
26
|
+
return String(value || '').replace(/\u001b\[[0-9;]*m/g, '');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const TEXT_ARTIFACT_EXTENSIONS = new Set([
|
|
30
|
+
'.cjs', '.js', '.json', '.md', '.mjs', '.toml', '.ts', '.txt', '.wat', '.yaml', '.yml'
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
// AssemblyScript and several proof builders use mkdtemp staging directories.
|
|
34
|
+
// Those paths are operational details, not part of the generated program. Keep
|
|
35
|
+
// checked-in/compiler artifacts stable without weakening semantic hash checks.
|
|
36
|
+
function canonicalizeEphemeralPaths(value) {
|
|
37
|
+
const stagingNames = String(value || '').replace(
|
|
38
|
+
/pulsewasm-([A-Za-z0-9._-]+?)-[A-Za-z0-9]{6}(?=[^A-Za-z0-9]|$)/g,
|
|
39
|
+
'pulsewasm-$1-STAGING'
|
|
40
|
+
);
|
|
41
|
+
// AssemblyScript writes source names relative to the output directory. That
|
|
42
|
+
// relative prefix changes when the repository is unpacked at a different
|
|
43
|
+
// filesystem depth even though the generated program is identical. Remove
|
|
44
|
+
// only the path leading to a known Pulse staging directory; retain the
|
|
45
|
+
// staging family and all source-relative suffixes for semantic hashing.
|
|
46
|
+
return stagingNames
|
|
47
|
+
.replace(/(?:\.\.\/)+tmp\/(pulsewasm-[A-Za-z0-9._-]+-STAGING)(?=\/)/g, 'tmp/$1')
|
|
48
|
+
.replace(/\/tmp\/(pulsewasm-[A-Za-z0-9._-]+-STAGING)(?=\/)/g, 'tmp/$1');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isTextArtifact(filePath) {
|
|
52
|
+
return TEXT_ARTIFACT_EXTENSIONS.has(path.extname(String(filePath || '')).toLowerCase());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function canonicalizeArtifactBuffer(filePath, buffer) {
|
|
56
|
+
const source = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer || '');
|
|
57
|
+
if (!isTextArtifact(filePath)) return source;
|
|
58
|
+
return Buffer.from(canonicalizeEphemeralPaths(source.toString('utf8')), 'utf8');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function normalizeArtifactFile(filePath) {
|
|
62
|
+
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) return false;
|
|
63
|
+
const before = fs.readFileSync(filePath);
|
|
64
|
+
const after = canonicalizeArtifactBuffer(filePath, before);
|
|
65
|
+
if (before.equals(after)) return false;
|
|
66
|
+
fs.writeFileSync(filePath, after);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function fileRecord(filePath, cwd, kind) {
|
|
71
|
+
const buffer = fs.readFileSync(filePath);
|
|
72
|
+
return {
|
|
73
|
+
file: normalizeSlash(path.relative(cwd, filePath)),
|
|
74
|
+
kind,
|
|
75
|
+
bytes: buffer.length,
|
|
76
|
+
sha256: sha256Buffer(buffer)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
normalizeSlash,
|
|
82
|
+
sha256Buffer,
|
|
83
|
+
sha256Text,
|
|
84
|
+
truncate,
|
|
85
|
+
stripAnsi,
|
|
86
|
+
fileRecord,
|
|
87
|
+
canonicalizeEphemeralPaths,
|
|
88
|
+
canonicalizeArtifactBuffer,
|
|
89
|
+
normalizeArtifactFile,
|
|
90
|
+
isTextArtifact
|
|
91
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
NATIVE_OPTIMIZATION_VERSION,
|
|
5
|
+
EXPERIMENTAL_NATIVE_SIZE_MODE
|
|
6
|
+
} = require('@pulse-compute/wasm-contracts/project/native-optimization');
|
|
7
|
+
const EXPERIMENTAL_NATIVE_SIZE_OPTIMIZATION = Object.freeze({
|
|
8
|
+
version: NATIVE_OPTIMIZATION_VERSION,
|
|
9
|
+
mode: EXPERIMENTAL_NATIVE_SIZE_MODE,
|
|
10
|
+
experimental: true,
|
|
11
|
+
goal: 'size',
|
|
12
|
+
assemblyScript: Object.freeze({
|
|
13
|
+
optimize: true,
|
|
14
|
+
optimizeLevel: 3,
|
|
15
|
+
shrinkLevel: 2,
|
|
16
|
+
converge: true
|
|
17
|
+
})
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function resolveNativeOptimization(value) {
|
|
21
|
+
if (value === undefined || value === null || value === false) return undefined;
|
|
22
|
+
if (
|
|
23
|
+
value === true
|
|
24
|
+
|| value === EXPERIMENTAL_NATIVE_SIZE_MODE
|
|
25
|
+
|| (typeof value === 'object' && value.mode === EXPERIMENTAL_NATIVE_SIZE_MODE)
|
|
26
|
+
) {
|
|
27
|
+
return EXPERIMENTAL_NATIVE_SIZE_OPTIMIZATION;
|
|
28
|
+
}
|
|
29
|
+
throw new TypeError(`Unsupported Pulse Native optimization mode ${String(value && value.mode || value)}.`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function appendAssemblyScriptOptimizationArgs(args, value) {
|
|
33
|
+
if (!Array.isArray(args)) throw new TypeError('AssemblyScript optimization arguments require a mutable argument array.');
|
|
34
|
+
const optimization = resolveNativeOptimization(value);
|
|
35
|
+
if (!optimization) return undefined;
|
|
36
|
+
args.push(
|
|
37
|
+
'--optimizeLevel', String(optimization.assemblyScript.optimizeLevel),
|
|
38
|
+
'--shrinkLevel', String(optimization.assemblyScript.shrinkLevel)
|
|
39
|
+
);
|
|
40
|
+
if (optimization.assemblyScript.converge) args.push('--converge');
|
|
41
|
+
return optimization;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = Object.freeze({
|
|
45
|
+
NATIVE_OPTIMIZATION_VERSION,
|
|
46
|
+
EXPERIMENTAL_NATIVE_SIZE_MODE,
|
|
47
|
+
EXPERIMENTAL_NATIVE_SIZE_OPTIMIZATION,
|
|
48
|
+
resolveNativeOptimization,
|
|
49
|
+
appendAssemblyScriptOptimizationArgs
|
|
50
|
+
});
|