dlintr 0.1.0 → 0.2.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/lib/index.cjs CHANGED
@@ -1,13 +1,29 @@
1
1
  'use strict';
2
2
 
3
- var node_fs = require('node:fs');
4
3
  var promises = require('node:fs/promises');
5
- var node_os = require('node:os');
6
4
  var path = require('node:path');
7
5
  var node_url = require('node:url');
8
- var node_wasi = require('node:wasi');
9
6
 
10
- var __async$1 = (__this, __arguments, generator) => {
7
+ var __defProp = Object.defineProperty;
8
+ var __defProps = Object.defineProperties;
9
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
10
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
13
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
14
+ var __spreadValues = (a, b) => {
15
+ for (var prop in b || (b = {}))
16
+ if (__hasOwnProp.call(b, prop))
17
+ __defNormalProp(a, prop, b[prop]);
18
+ if (__getOwnPropSymbols)
19
+ for (var prop of __getOwnPropSymbols(b)) {
20
+ if (__propIsEnum.call(b, prop))
21
+ __defNormalProp(a, prop, b[prop]);
22
+ }
23
+ return a;
24
+ };
25
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
26
+ var __async$2 = (__this, __arguments, generator) => {
11
27
  return new Promise((resolve, reject) => {
12
28
  var fulfilled = (value) => {
13
29
  try {
@@ -27,63 +43,156 @@ var __async$1 = (__this, __arguments, generator) => {
27
43
  step((generator = generator.apply(__this, __arguments)).next());
28
44
  });
29
45
  };
30
- const createDlinter = (runtime) => {
31
- let version;
46
+ const HOME = "HOME=/";
47
+ const createDlinter = (load) => {
48
+ const reactors = /* @__PURE__ */ new Map();
49
+ const instance = (env) => {
50
+ const pairs = [
51
+ HOME,
52
+ ...Object.entries(env != null ? env : {}).map(([key2, value]) => `${key2}=${value}`)
53
+ ];
54
+ const key = JSON.stringify(pairs);
55
+ let reactor = reactors.get(key);
56
+ if (!reactor) {
57
+ reactor = load(pairs);
58
+ reactors.set(key, reactor);
59
+ reactor.catch(() => reactors.delete(key));
60
+ }
61
+ return reactor;
62
+ };
32
63
  return {
33
- run: runtime.run,
34
- lint: (_0, ..._1) => __async$1(null, [_0, ..._1], function* (dockerfile, options = {}) {
35
- var _a, _b;
36
- const args = ["--format", "json"];
37
- for (const code of (_a = options.ignore) != null ? _a : []) {
38
- args.push("--ignore", code);
39
- }
40
- for (const [code, level] of Object.entries(
41
- (_b = options.severityOverrides) != null ? _b : {}
42
- )) {
43
- if (level) {
44
- args.push(`--${level}`, code);
45
- }
46
- }
47
- args.push("-");
48
- const { stdout, stderr } = yield runtime.run(args, {
49
- stdin: dockerfile,
50
- env: options.env
64
+ lint: (_0, ..._1) => __async$2(null, [_0, ..._1], function* (dockerfile, options = {}) {
65
+ var _a;
66
+ const { lint } = yield instance(options.env);
67
+ const findings = JSON.parse(yield lint(dockerfile));
68
+ const ignored = new Set((_a = options.ignore) != null ? _a : []);
69
+ return findings.filter(({ code }) => !ignored.has(code)).map((finding) => {
70
+ var _a2;
71
+ const level = (_a2 = options.severityOverrides) == null ? void 0 : _a2[finding.code];
72
+ return level ? __spreadProps(__spreadValues({}, finding), { level }) : finding;
51
73
  });
52
- if (!stdout) {
53
- if (stderr) {
54
- throw new Error(stderr.trim());
55
- }
56
- return [];
57
- }
58
- return JSON.parse(stdout);
59
74
  }),
60
- getVersion: () => version != null ? version : version = runtime.run(["--version"]).then(({ stdout }) => stdout.trim()).catch((error) => {
61
- version = void 0;
62
- throw error;
75
+ getVersion: () => __async$2(null, null, function* () {
76
+ const { version } = yield instance();
77
+ return version();
63
78
  })
64
79
  };
65
80
  };
66
81
 
67
- const HADOLINT = "hadolint";
68
- const DEFAULT_ENV = { HOME: "/" };
82
+ const memoize = (load) => {
83
+ let pending;
84
+ return () => {
85
+ if (!pending) {
86
+ const started = load();
87
+ started.catch(() => {
88
+ pending = void 0;
89
+ });
90
+ pending = started;
91
+ }
92
+ return pending;
93
+ };
94
+ };
69
95
 
70
- var __defProp = Object.defineProperty;
71
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
72
- var __hasOwnProp = Object.prototype.hasOwnProperty;
73
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
74
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
75
- var __spreadValues = (a, b) => {
76
- for (var prop in b || (b = {}))
77
- if (__hasOwnProp.call(b, prop))
78
- __defNormalProp(a, prop, b[prop]);
79
- if (__getOwnPropSymbols)
80
- for (var prop of __getOwnPropSymbols(b)) {
81
- if (__propIsEnum.call(b, prop))
82
- __defNormalProp(a, prop, b[prop]);
96
+ const CLOCK_MONOTONIC = 1;
97
+ const POINTER_SIZE = 4;
98
+ const NANOSECONDS_PER_MILLISECOND = 1e6;
99
+ const ERRNO_SUCCESS = 0;
100
+ const NOT_IMPLEMENTED = [
101
+ "fd_close",
102
+ "fd_fdstat_get",
103
+ "fd_fdstat_set_flags",
104
+ "fd_filestat_get",
105
+ "fd_filestat_set_size",
106
+ "fd_prestat_get",
107
+ "fd_prestat_dir_name",
108
+ "fd_read",
109
+ "fd_seek",
110
+ "fd_write",
111
+ "path_create_directory",
112
+ "path_filestat_get",
113
+ "path_open",
114
+ "poll_oneoff",
115
+ "proc_exit"
116
+ ];
117
+ const createWasiHost = (env) => {
118
+ let memory;
119
+ const requireMemory = () => {
120
+ if (!memory) {
121
+ throw new Error("dlintr: WASI called before initialize()");
83
122
  }
84
- return a;
123
+ return memory;
124
+ };
125
+ const view = () => new DataView(requireMemory().buffer);
126
+ const bytes = () => new Uint8Array(requireMemory().buffer);
127
+ const encoder = new TextEncoder();
128
+ const entries = env.map((entry) => encoder.encode(`${entry}\0`));
129
+ const argsSizesGet = (countPointer, sizePointer) => {
130
+ const data = view();
131
+ data.setUint32(countPointer, 0, true);
132
+ data.setUint32(sizePointer, 0, true);
133
+ return ERRNO_SUCCESS;
134
+ };
135
+ const environSizesGet = (countPointer, sizePointer) => {
136
+ const data = view();
137
+ data.setUint32(countPointer, entries.length, true);
138
+ data.setUint32(
139
+ sizePointer,
140
+ entries.reduce((total, entry) => total + entry.length, 0),
141
+ true
142
+ );
143
+ return ERRNO_SUCCESS;
144
+ };
145
+ const environGet = (pointerTable, buffer) => {
146
+ const data = view();
147
+ const target = bytes();
148
+ let offset = buffer;
149
+ for (const [index, entry] of entries.entries()) {
150
+ data.setUint32(pointerTable + index * POINTER_SIZE, offset, true);
151
+ target.set(entry, offset);
152
+ offset += entry.length;
153
+ }
154
+ return ERRNO_SUCCESS;
155
+ };
156
+ const clockTimeGet = (id, _precision, timePointer) => {
157
+ if (id !== CLOCK_MONOTONIC) {
158
+ throw new Error(
159
+ `dlintr: clock_time_get(${id}) is not the monotonic clock`
160
+ );
161
+ }
162
+ const nanoseconds = BigInt(
163
+ Math.round(performance.now() * NANOSECONDS_PER_MILLISECOND)
164
+ );
165
+ view().setBigUint64(timePointer, nanoseconds, true);
166
+ return ERRNO_SUCCESS;
167
+ };
168
+ const wasiImport = {
169
+ args_get: () => ERRNO_SUCCESS,
170
+ args_sizes_get: argsSizesGet,
171
+ clock_time_get: clockTimeGet,
172
+ environ_get: environGet,
173
+ environ_sizes_get: environSizesGet
174
+ };
175
+ for (const name of NOT_IMPLEMENTED) {
176
+ wasiImport[name] = () => {
177
+ throw new Error(
178
+ `dlintr: the reactor called \`${name}\`, which this WASI shim does not implement.
179
+ The imports actually used are measured, not assumed \u2014 run \`node wasm/calls.mjs\`
180
+ to see the current set. Seeing this means the module now reaches further than
181
+ when that measurement was taken, so implement \`${name}\` rather than retrying.`
182
+ );
183
+ };
184
+ }
185
+ return {
186
+ wasiImport,
187
+ initialize: (instance) => {
188
+ var _a, _b;
189
+ memory = instance.exports.memory;
190
+ (_b = (_a = instance.exports)._initialize) == null ? void 0 : _b.call(_a);
191
+ }
192
+ };
85
193
  };
86
- var __async = (__this, __arguments, generator) => {
194
+
195
+ var __async$1 = (__this, __arguments, generator) => {
87
196
  return new Promise((resolve, reject) => {
88
197
  var fulfilled = (value) => {
89
198
  try {
@@ -103,135 +212,66 @@ var __async = (__this, __arguments, generator) => {
103
212
  step((generator = generator.apply(__this, __arguments)).next());
104
213
  });
105
214
  };
106
- const import_meta = {};
107
- const packageRoot = () => typeof __dirname === "string" ? path.join(__dirname, "..") : node_url.fileURLToPath(new URL("..", import_meta.url));
108
- const wasmPath = () => path.join(packageRoot(), "hadolint.wasm");
109
- const encoder = new TextEncoder();
110
- const decoder = new TextDecoder();
111
- let compiled;
112
- const load = () => {
113
- compiled != null ? compiled : compiled = promises.readFile(wasmPath()).then((bytes) => WebAssembly.compile(bytes)).catch((error) => {
114
- compiled = void 0;
115
- throw error;
215
+ const createReactor = (module, glue, env) => __async$1(null, null, function* () {
216
+ const names = WebAssembly.Module.exports(module).map(({ name }) => name);
217
+ if (!names.includes("_initialize") || names.includes("_start")) {
218
+ throw new Error(
219
+ `dlintr: expected a reactor exporting \`_initialize\` and no \`_start\`, got: ${names.join(", ")}`
220
+ );
221
+ }
222
+ const wasi = createWasiHost(env);
223
+ const deferred = {};
224
+ const instance = yield WebAssembly.instantiate(module, {
225
+ ghc_wasm_jsffi: glue(deferred),
226
+ wasi_snapshot_preview1: wasi.wasiImport
116
227
  });
117
- return compiled;
118
- };
119
- const IOVEC_SIZE = 8;
120
- const IOVEC_LENGTH_OFFSET = 4;
121
- const intercept = (imports, memory, stdin) => {
122
- const state = { stdout: "", stderr: "" };
123
- let offset = 0;
124
- const views = () => {
125
- const buffer = memory();
126
- return { view: new DataView(buffer), bytes: new Uint8Array(buffer) };
127
- };
128
- const iovecs = (iovs, iovsLen) => {
129
- const { view } = views();
130
- const result = [];
131
- for (let index = 0; index < iovsLen; index++) {
132
- result.push({
133
- offset: view.getUint32(iovs + index * IOVEC_SIZE, true),
134
- length: view.getUint32(
135
- iovs + index * IOVEC_SIZE + IOVEC_LENGTH_OFFSET,
136
- true
137
- )
138
- });
139
- }
140
- return result;
228
+ Object.assign(deferred, instance.exports);
229
+ const reactor = instance.exports;
230
+ wasi.initialize({ exports: reactor });
231
+ return {
232
+ lint: (dockerfile) => reactor.lint(dockerfile),
233
+ version: () => reactor.version()
141
234
  };
142
- imports.fd_read = (_fd, iovs, iovsLen, nreadPtr) => {
143
- const { view, bytes } = views();
144
- let read = 0;
145
- for (const { offset: target, length } of iovecs(iovs, iovsLen)) {
146
- const size = Math.min(length, stdin.length - offset);
147
- if (size <= 0) {
148
- break;
235
+ });
236
+
237
+ var __async = (__this, __arguments, generator) => {
238
+ return new Promise((resolve, reject) => {
239
+ var fulfilled = (value) => {
240
+ try {
241
+ step(generator.next(value));
242
+ } catch (e) {
243
+ reject(e);
149
244
  }
150
- bytes.set(stdin.subarray(offset, offset + size), target);
151
- offset += size;
152
- read += size;
153
- }
154
- view.setUint32(nreadPtr, read, true);
155
- return 0;
156
- };
157
- imports.fd_write = (fd, iovs, iovsLen, nwrittenPtr) => {
158
- const { view, bytes } = views();
159
- let written = 0;
160
- for (const { offset: source, length } of iovecs(iovs, iovsLen)) {
161
- const chunk = decoder.decode(bytes.subarray(source, source + length));
162
- if (fd === 1) {
163
- state.stdout += chunk;
164
- } else if (fd === 2) {
165
- state.stderr += chunk;
245
+ };
246
+ var rejected = (value) => {
247
+ try {
248
+ step(generator.throw(value));
249
+ } catch (e) {
250
+ reject(e);
166
251
  }
167
- written += length;
168
- }
169
- view.setUint32(nwrittenPtr, written, true);
170
- return 0;
171
- };
172
- return state;
173
- };
174
- const memoryOf = (instance) => instance.exports.memory.buffer;
175
- const openStdio = () => ({
176
- stdin: node_fs.openSync(node_os.devNull, "r"),
177
- stdout: node_fs.openSync(node_os.devNull, "w"),
178
- stderr: node_fs.openSync(node_os.devNull, "w")
179
- });
180
- const closeQuietly = (fd) => {
181
- try {
182
- node_fs.closeSync(fd);
183
- } catch (e) {
184
- }
185
- };
186
- const closeStdio = (stdio) => {
187
- closeQuietly(stdio.stdin);
188
- closeQuietly(stdio.stdout);
189
- closeQuietly(stdio.stderr);
252
+ };
253
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
254
+ step((generator = generator.apply(__this, __arguments)).next());
255
+ });
190
256
  };
191
- const createNodeRuntime = () => ({
192
- run: (_0, ..._1) => __async(null, [_0, ..._1], function* (args, options = {}) {
193
- var _a;
194
- const module = yield load();
195
- const stdio = openStdio();
196
- let instance;
197
- try {
198
- const wasi = new node_wasi.WASI({
199
- version: "preview1",
200
- args: [HADOLINT, ...args],
201
- env: __spreadValues(__spreadValues({}, DEFAULT_ENV), options.env),
202
- stdin: stdio.stdin,
203
- stdout: stdio.stdout,
204
- stderr: stdio.stderr
205
- });
206
- const nodeWasi = wasi;
207
- const intercepted = intercept(
208
- nodeWasi.wasiImport,
209
- () => {
210
- if (!instance) {
211
- throw new Error(
212
- "dlintr: hadolint touched stdio before it was instantiated"
213
- );
214
- }
215
- return memoryOf(instance);
216
- },
217
- encoder.encode((_a = options.stdin) != null ? _a : "")
218
- );
219
- instance = yield WebAssembly.instantiate(module, {
220
- wasi_snapshot_preview1: nodeWasi.wasiImport
221
- });
222
- const exitCode = nodeWasi.start(instance);
223
- return __spreadValues({ exitCode: exitCode != null ? exitCode : 0 }, intercepted);
224
- } finally {
225
- closeStdio(stdio);
226
- }
257
+ const import_meta = {};
258
+ const packageRoot = () => typeof __dirname === "string" ? path.join(__dirname, "..") : node_url.fileURLToPath(new URL("..", import_meta.url));
259
+ const loadAssets = memoize(
260
+ () => __async(null, null, function* () {
261
+ const root = packageRoot();
262
+ const [bytes, glue] = yield Promise.all([
263
+ promises.readFile(path.join(root, "hadolint.wasm")),
264
+ import(node_url.pathToFileURL(path.join(root, "ghc_wasm_jsffi.js")).href)
265
+ ]);
266
+ return [yield WebAssembly.compile(bytes), glue.default];
227
267
  })
228
- });
229
-
230
- const dlinter = createDlinter(createNodeRuntime());
231
- const run = dlinter.run;
268
+ );
269
+ const dlinter = createDlinter((env) => __async(null, null, function* () {
270
+ const [module, glue] = yield loadAssets();
271
+ return createReactor(module, glue, env);
272
+ }));
232
273
  const lint = dlinter.lint;
233
274
  const getVersion = dlinter.getVersion;
234
275
 
235
276
  exports.getVersion = getVersion;
236
277
  exports.lint = lint;
237
- exports.run = run;
package/lib/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- export declare const run: (args: string[], options?: import("./types.js").RunOptions) => Promise<import("./types.js").RunResult>;
2
1
  export declare const lint: (dockerfile: string, options?: import("./types.js").LintOptions) => Promise<import("./types.js").LintResult[]>;
3
2
  export declare const getVersion: () => Promise<string>;
4
- export type { Dlinter } from './api.js';
5
- export type { WasiRuntime } from './runtime.js';
6
- export type { LintOptions, LintResult, OutputFormat, RunOptions, RunResult, Severity, } from './types.js';
3
+ export type { Dlinter, LoadReactor } from './api.js';
4
+ export type { Reactor } from './reactor.js';
5
+ export type { LintOptions, LintResult, Severity } from './types.js';
package/lib/index.js CHANGED
@@ -1,7 +1,25 @@
1
+ import { __rewriteRelativeImportExtension } from "tslib";
2
+ import { readFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
1
5
  import { createDlinter } from './api.js';
2
- import { createNodeRuntime } from './wasi-node.js';
3
- const dlinter = createDlinter(createNodeRuntime());
4
- export const run = dlinter.run;
6
+ import { memoize } from './memoize.js';
7
+ import { createReactor } from './reactor.js';
8
+ const packageRoot = () => typeof __dirname === 'string'
9
+ ? path.join(__dirname, '..')
10
+ : fileURLToPath(new URL('..', import.meta.url));
11
+ const loadAssets = memoize(async () => {
12
+ const root = packageRoot();
13
+ const [bytes, glue] = await Promise.all([
14
+ readFile(path.join(root, 'hadolint.wasm')),
15
+ import(__rewriteRelativeImportExtension(pathToFileURL(path.join(root, 'ghc_wasm_jsffi.js')).href, true)),
16
+ ]);
17
+ return [await WebAssembly.compile(bytes), glue.default];
18
+ });
19
+ const dlinter = createDlinter(async (env) => {
20
+ const [module, glue] = await loadAssets();
21
+ return createReactor(module, glue, env);
22
+ });
5
23
  export const lint = dlinter.lint;
6
24
  export const getVersion = dlinter.getVersion;
7
25
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAElD,MAAM,OAAO,GAAG,aAAa,CAAC,iBAAiB,EAAE,CAAC,CAAA;AAElD,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAA;AAC9B,MAAM,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;AAChC,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AASA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAU5C,MAAM,WAAW,GAAG,GAAW,EAAE,CAC/B,OAAO,SAAS,KAAK,QAAQ;IAC3B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC;IAC5B,CAAC,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAMnD,MAAM,UAAU,GAAG,OAAO,CACxB,KAAK,IAA0D,EAAE;IAC/D,MAAM,IAAI,GAAG,WAAW,EAAE,CAAA;IAC1B,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACtC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QAC1C,MAAM,kCACJ,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,QAGxD;KACH,CAAC,CAAA;IACF,OAAO,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,CAAU,CAAA;AAClE,CAAC,CACF,CAAA;AAED,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAC,GAAG,EAAC,EAAE;IACxC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,MAAM,UAAU,EAAE,CAAA;IACzC,OAAO,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;AACzC,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;AAChC,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAA"}
@@ -0,0 +1 @@
1
+ export declare const memoize: <T>(load: () => Promise<T>) => (() => Promise<T>);
package/lib/memoize.js ADDED
@@ -0,0 +1,14 @@
1
+ export const memoize = (load) => {
2
+ let pending;
3
+ return () => {
4
+ if (!pending) {
5
+ const started = load();
6
+ started.catch(() => {
7
+ pending = undefined;
8
+ });
9
+ pending = started;
10
+ }
11
+ return pending;
12
+ };
13
+ };
14
+ //# sourceMappingURL=memoize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize.js","sourceRoot":"","sources":["../src/memoize.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,MAAM,OAAO,GAAG,CAAI,IAAsB,EAAsB,EAAE;IACvE,IAAI,OAA+B,CAAA;IAEnC,OAAO,GAAG,EAAE;QACV,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,IAAI,EAAE,CAAA;YAMtB,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE;gBACjB,OAAO,GAAG,SAAS,CAAA;YACrB,CAAC,CAAC,CAAA;YAEF,OAAO,GAAG,OAAO,CAAA;QACnB,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,6 @@
1
+ export type GhcWasmJsffi = (exports: WebAssembly.Exports) => Record<string, WebAssembly.ExportValue>;
2
+ export interface Reactor {
3
+ lint: (dockerfile: string) => Promise<string>;
4
+ version: () => Promise<string>;
5
+ }
6
+ export declare const createReactor: (module: WebAssembly.Module, glue: GhcWasmJsffi, env: readonly string[]) => Promise<Reactor>;
package/lib/reactor.js ADDED
@@ -0,0 +1,21 @@
1
+ import { createWasiHost } from './wasi.js';
2
+ export const createReactor = async (module, glue, env) => {
3
+ const names = WebAssembly.Module.exports(module).map(({ name }) => name);
4
+ if (!names.includes('_initialize') || names.includes('_start')) {
5
+ throw new Error(`dlintr: expected a reactor exporting \`_initialize\` and no \`_start\`, got: ${names.join(', ')}`);
6
+ }
7
+ const wasi = createWasiHost(env);
8
+ const deferred = {};
9
+ const instance = await WebAssembly.instantiate(module, {
10
+ ghc_wasm_jsffi: glue(deferred),
11
+ wasi_snapshot_preview1: wasi.wasiImport,
12
+ });
13
+ Object.assign(deferred, instance.exports);
14
+ const reactor = instance.exports;
15
+ wasi.initialize({ exports: reactor });
16
+ return {
17
+ lint: dockerfile => reactor.lint(dockerfile),
18
+ version: () => reactor.version(),
19
+ };
20
+ };
21
+ //# sourceMappingURL=reactor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactor.js","sourceRoot":"","sources":["../src/reactor.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAuC1C,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAChC,MAA0B,EAC1B,IAAkB,EAClB,GAAsB,EACJ,EAAE;IACpB,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAA;IAKxE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,KAAK,CACb,gFAAgF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACnG,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;IAShC,MAAM,QAAQ,GAAwB,EAAE,CAAA;IAExC,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC,MAAM,EAAE;QACrD,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC;QAC9B,sBAAsB,EAAE,IAAI,CAAC,UAAU;KACxC,CAAC,CAAA;IAEF,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;IAKzC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAyB,CAAA;IAUlD,IAAI,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;IAErC,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;QAC5C,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;KACjC,CAAA;AACH,CAAC,CAAA"}
package/lib/types.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export type Severity = 'error' | 'info' | 'style' | 'warning';
2
- export type OutputFormat = 'checkstyle' | 'codacy' | 'codeclimate' | 'gitlab_codeclimate' | 'gnu' | 'json' | 'sarif' | 'sonarqube' | 'tty';
3
2
  export interface LintResult {
4
3
  code: string;
5
4
  line: number;
@@ -8,15 +7,6 @@ export interface LintResult {
8
7
  message: string;
9
8
  file: string;
10
9
  }
11
- export interface RunResult {
12
- exitCode: number;
13
- stdout: string;
14
- stderr: string;
15
- }
16
- export interface RunOptions {
17
- stdin?: string;
18
- env?: Record<string, string>;
19
- }
20
10
  export interface LintOptions {
21
11
  ignore?: string[];
22
12
  severityOverrides?: Partial<Record<string, Severity>>;
package/lib/wasi.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export interface WasiInstance {
2
+ exports: {
3
+ memory: WebAssembly.Memory;
4
+ _initialize?: () => unknown;
5
+ };
6
+ }
7
+ export interface WasiHost {
8
+ wasiImport: Record<string, WebAssembly.ExportValue>;
9
+ initialize: (instance: WasiInstance) => void;
10
+ }
11
+ export declare const createWasiHost: (env: readonly string[]) => WasiHost;
package/lib/wasi.js ADDED
@@ -0,0 +1,88 @@
1
+ const CLOCK_MONOTONIC = 1;
2
+ const POINTER_SIZE = 4;
3
+ const NANOSECONDS_PER_MILLISECOND = 1e6;
4
+ const ERRNO_SUCCESS = 0;
5
+ const NOT_IMPLEMENTED = [
6
+ 'fd_close',
7
+ 'fd_fdstat_get',
8
+ 'fd_fdstat_set_flags',
9
+ 'fd_filestat_get',
10
+ 'fd_filestat_set_size',
11
+ 'fd_prestat_get',
12
+ 'fd_prestat_dir_name',
13
+ 'fd_read',
14
+ 'fd_seek',
15
+ 'fd_write',
16
+ 'path_create_directory',
17
+ 'path_filestat_get',
18
+ 'path_open',
19
+ 'poll_oneoff',
20
+ 'proc_exit',
21
+ ];
22
+ export const createWasiHost = (env) => {
23
+ let memory;
24
+ const requireMemory = () => {
25
+ if (!memory) {
26
+ throw new Error('dlintr: WASI called before initialize()');
27
+ }
28
+ return memory;
29
+ };
30
+ const view = () => new DataView(requireMemory().buffer);
31
+ const bytes = () => new Uint8Array(requireMemory().buffer);
32
+ const encoder = new TextEncoder();
33
+ const entries = env.map(entry => encoder.encode(`${entry}\0`));
34
+ const argsSizesGet = (countPointer, sizePointer) => {
35
+ const data = view();
36
+ data.setUint32(countPointer, 0, true);
37
+ data.setUint32(sizePointer, 0, true);
38
+ return ERRNO_SUCCESS;
39
+ };
40
+ const environSizesGet = (countPointer, sizePointer) => {
41
+ const data = view();
42
+ data.setUint32(countPointer, entries.length, true);
43
+ data.setUint32(sizePointer, entries.reduce((total, entry) => total + entry.length, 0), true);
44
+ return ERRNO_SUCCESS;
45
+ };
46
+ const environGet = (pointerTable, buffer) => {
47
+ const data = view();
48
+ const target = bytes();
49
+ let offset = buffer;
50
+ for (const [index, entry] of entries.entries()) {
51
+ data.setUint32(pointerTable + index * POINTER_SIZE, offset, true);
52
+ target.set(entry, offset);
53
+ offset += entry.length;
54
+ }
55
+ return ERRNO_SUCCESS;
56
+ };
57
+ const clockTimeGet = (id, _precision, timePointer) => {
58
+ if (id !== CLOCK_MONOTONIC) {
59
+ throw new Error(`dlintr: clock_time_get(${id}) is not the monotonic clock`);
60
+ }
61
+ const nanoseconds = BigInt(Math.round(performance.now() * NANOSECONDS_PER_MILLISECOND));
62
+ view().setBigUint64(timePointer, nanoseconds, true);
63
+ return ERRNO_SUCCESS;
64
+ };
65
+ const wasiImport = {
66
+ args_get: () => ERRNO_SUCCESS,
67
+ args_sizes_get: argsSizesGet,
68
+ clock_time_get: clockTimeGet,
69
+ environ_get: environGet,
70
+ environ_sizes_get: environSizesGet,
71
+ };
72
+ for (const name of NOT_IMPLEMENTED) {
73
+ wasiImport[name] = () => {
74
+ throw new Error(`dlintr: the reactor called \`${name}\`, which this WASI shim does not implement.\n` +
75
+ `The imports actually used are measured, not assumed — run \`node wasm/calls.mjs\`\n` +
76
+ `to see the current set. Seeing this means the module now reaches further than\n` +
77
+ `when that measurement was taken, so implement \`${name}\` rather than retrying.`);
78
+ };
79
+ }
80
+ return {
81
+ wasiImport,
82
+ initialize: instance => {
83
+ memory = instance.exports.memory;
84
+ instance.exports._initialize?.();
85
+ },
86
+ };
87
+ };
88
+ //# sourceMappingURL=wasi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wasi.js","sourceRoot":"","sources":["../src/wasi.ts"],"names":[],"mappings":"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"}