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/README.md CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  [![GitHub Actions](https://github.com/wasmio/dlintr/workflows/CI/badge.svg)](https://github.com/wasmio/dlintr/actions/workflows/ci.yml)
4
4
  [![Codecov](https://img.shields.io/codecov/c/github/wasmio/dlintr.svg)](https://codecov.io/gh/wasmio/dlintr)
5
- [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/wasmio/dlintr.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/wasmio/dlintr/context:javascript)
6
5
  [![type-coverage](https://img.shields.io/badge/dynamic/json.svg?label=type-coverage&prefix=%E2%89%A5&suffix=%&query=$.typeCoverage.atLeast&uri=https%3A%2F%2Fraw.githubusercontent.com%2Fwasmio%2Fdlintr%2Fmain%2Fpackage.json)](https://github.com/plantain-00/type-coverage)
7
6
  [![npm](https://img.shields.io/npm/v/dlintr.svg)](https://www.npmjs.com/package/dlintr)
8
7
  [![GitHub Release](https://img.shields.io/github/release/wasmio/dlintr)](https://github.com/wasmio/dlintr/releases)
@@ -21,6 +20,8 @@ Dockerfile linter, validate inline bash, powered by [`hadolint`](https://github.
21
20
  - [Install](#install)
22
21
  - [API](#api)
23
22
  - [How it works](#how-it-works)
23
+ - [The WASI surface, and its residual risk](#the-wasi-surface-and-its-residual-risk)
24
+ - [The digest, and what it does not promise](#the-digest-and-what-it-does-not-promise)
24
25
  - [Sponsors](#sponsors)
25
26
  - [Backers](#backers)
26
27
  - [Changelog](#changelog)
@@ -41,12 +42,13 @@ yarn add dlintr
41
42
  npm i dlintr
42
43
  ```
43
44
 
44
- Requires Node.js 20.1+ for the built-in [`node:wasi`][node-wasi].
45
+ The only runtime dependency is `tslib`. The WASI surface hadolint needs is
46
+ implemented in `src/wasi.ts` rather than taken from a library.
45
47
 
46
48
  ### API
47
49
 
48
50
  ```js
49
- import { getVersion, lint, run } from 'dlintr'
51
+ import { getVersion, lint } from 'dlintr'
50
52
 
51
53
  // Lint Dockerfile content and get the findings back.
52
54
  const results = await lint('FROM ubuntu:latest\nRUN apt-get update\n')
@@ -56,34 +58,93 @@ const results = await lint('FROM ubuntu:latest\nRUN apt-get update\n')
56
58
  // ]
57
59
 
58
60
  await lint('FROM alpine:3.20\n', {
59
- ignore: ['DL3009'], // rule codes to suppress
61
+ ignore: ['DL3009'], // rule codes to suppress, applied to the findings
60
62
  severityOverrides: { DL3007: 'error' }, // re-level a single rule
61
63
  env: { HADOLINT_TRUSTED_REGISTRIES: 'docker.io' }, // anything hadolint reads
62
64
  })
63
65
 
64
- await getVersion() // 'Haskell Dockerfile Linter 3.0.0'
65
-
66
- // Or drive the CLI directly, for options `lint` does not model.
67
- const { exitCode, stdout, stderr } = await run(
68
- ['--no-color', '--failure-threshold', 'error', '-'],
69
- {
70
- stdin: 'FROM ubuntu:latest\n',
71
- },
72
- )
66
+ await getVersion() // 'Haskell Dockerfile Linter 2.15.1'
73
67
  ```
74
68
 
75
- An unparseable Dockerfile is reported, not thrown: hadolint returns it as a `DL1000` finding with `level: 'error'`. Anything that stops hadolint before linting — a rejected option, for instance — is thrown, since it would otherwise be indistinguishable from a clean Dockerfile.
69
+ An unparseable Dockerfile is reported, not thrown: hadolint returns it as a
70
+ `DL1000` finding with `level: 'error'`.
76
71
 
77
- `lint` returns findings and drops the exit code. To gate CI, either look at `level`, or use `run` with `--failure-threshold` and check `exitCode`.
78
-
79
- ### How it works
72
+ To gate CI, look at `level`:
80
73
 
81
- [`hadolint`][hadolint] is compiled to WASI with the GHC backend and executed in-process; there is no native binary and no Docker.
74
+ ```js
75
+ const problems = (await lint(dockerfile)).filter(
76
+ ({ level }) => level === 'error',
77
+ )
78
+ ```
82
79
 
83
- - **Node** drives it with the built-in `node:wasi`, so there is nothing to install.
84
- - **Browsers** get [`@bjorn3/browser_wasi_shim`][bjorn3] (~29 kB minified) through the `browser` export condition. It is an optional peer dependency — install it if you target the browser, and Node users never pay for it.
80
+ ### How it works
85
81
 
86
- The Dockerfile is fed over stdin, which is why no filesystem has to be mounted: only what you pass to `lint`/`run` is ever visible to the linter, and it cannot read files from the host.
82
+ [`hadolint`][hadolint] is compiled to a WebAssembly _reactor_ with the GHC
83
+ backend and called in-process; there is no native binary and no Docker. The
84
+ module is instantiated once and `lint` is called on it.
85
+
86
+ **This is a breaking change from 0.1.x.** The module previously exposed
87
+ hadolint's command-line interface, and now exports `lint()` and
88
+ `getVersion():`
89
+
90
+ - **`run()` is gone**, and with it the whole CLI surface. `--failure-threshold`,
91
+ `--config`, `--format`, `--no-color` and the rest are no longer reachable
92
+ from JavaScript. `lint` returns the findings as JSON, and the output format is
93
+ no longer configurable.
94
+ - `ignore` and `severityOverrides` survive, applied to the returned findings —
95
+ they only ever filtered and relabelled, so they never needed the Haskell side.
96
+ - `env` survives too, and reaches hadolint through WASI as before.
97
+ - **`node:wasi` cannot host this module at all.** Its `initialize` ends in
98
+ `proc_exit(71)` and the RTS never starts, so every call answers "RTS is not
99
+ initialised". That is true of upstream's published reactor as well, so it is a
100
+ limitation of `node:wasi` rather than of this build. Node and the browser
101
+ therefore share one WASI implementation, from `src/wasi.ts`.
102
+
103
+ Nothing is fed over stdin any more: the Dockerfile is passed straight to `lint`,
104
+ so the module never sees a file descriptor that could read the host.
105
+
106
+ ### The WASI surface, and its residual risk
107
+
108
+ hadolint needs five WASI calls — `args_get`, `args_sizes_get`, `environ_get`,
109
+ `environ_sizes_get` and `clock_time_get` — and never touches the other fifteen
110
+ it declares, including all of `fd_*`, `path_*` and `proc_exit`. That is
111
+ **measured, not assumed**: `node wasm/calls.mjs` instruments every import and
112
+ reports what is actually called on each path. Run it when the hadolint submodule
113
+ or the wasm recipe moves.
114
+
115
+ `src/wasi.ts` implements those five and makes the rest **throw by name**, so a
116
+ module that reaches further fails at `fd_write` with an explanation rather than
117
+ silently losing output.
118
+
119
+ Two things follow, stated plainly:
120
+
121
+ - **The implemented surface is version-bound.** It is correct for the build that
122
+ was measured. That is exactly why the unimplemented imports throw rather than
123
+ doing nothing, and why `wasm/calls.mjs` is committed.
124
+ - **This is not a tested third-party WASI implementation.** The stubs protect the
125
+ paths the test suite exercises. A path it does not exercise could reach an
126
+ unimplemented import in your hands, and the failure there is an exception
127
+ rather than a wrong answer — better than silent corruption, but not the same
128
+ guarantee a maintained WASI library gives.
129
+
130
+ ### The digest, and what it does not promise
131
+
132
+ The build is attested and the npm tarball is content-addressed, so what you
133
+ install is what was published. **A digest identifies a build; it does not
134
+ guarantee one.**
135
+
136
+ This module is **not reproducible**. Two from-scratch builds of the same source,
137
+ with the same toolchain and no build cache, produce different bytes — eight
138
+ distinct digests are on record for one pinned revision of hadolint. So the
139
+ digest is useful for telling one published artifact from another and for tying an
140
+ artifact to its attestation, and for nothing stronger than that.
141
+
142
+ An earlier version of this repository used the digest as a drift gate, comparing
143
+ each build against a recorded value and failing on a mismatch. It was retired,
144
+ because it failed on every run: a gate that presupposes a property the build does
145
+ not have is not a gate. The measurements are written up in
146
+ [`wasm/upstream-report.md`](./wasm/upstream-report.md) and filed upstream as
147
+ [haskell-wasm/ghc-wasm-bindists#49](https://github.com/haskell-wasm/ghc-wasm-bindists/issues/49).
87
148
 
88
149
  ## Sponsors
89
150
 
@@ -108,8 +169,6 @@ Detailed changes for each release are documented in [CHANGELOG.md](./CHANGELOG.m
108
169
  [MIT][] © [JounQin][]@[1stG.me][]
109
170
 
110
171
  [1stg.me]: https://www.1stg.me
111
- [bjorn3]: https://github.com/bjorn3/browser_wasi_shim
112
172
  [hadolint]: https://github.com/hadolint/hadolint
113
173
  [jounqin]: https://GitHub.com/JounQin
114
174
  [mit]: http://opensource.org/licenses/MIT
115
- [node-wasi]: https://nodejs.org/api/wasi.html
@@ -0,0 +1,114 @@
1
+ // This file implements the JavaScript runtime logic for Haskell
2
+ // modules that use JSFFI. It is not an ESM module, but the template
3
+ // of one; the post-linker script will copy all contents into a new
4
+ // ESM module.
5
+
6
+ // Manage a mapping from 32-bit ids to actual JavaScript values.
7
+ class JSValManager {
8
+ #lastk = 0;
9
+ #kv = new Map();
10
+
11
+ newJSVal(v) {
12
+ const k = ++this.#lastk;
13
+ this.#kv.set(k, v);
14
+ return k;
15
+ }
16
+
17
+ // A separate has() call to ensure we can store undefined as a value
18
+ // too. Also, unconditionally check this since the check is cheap
19
+ // anyway, if the check fails then there's a use-after-free to be
20
+ // fixed.
21
+ getJSVal(k) {
22
+ if (!this.#kv.has(k)) {
23
+ throw new WebAssembly.RuntimeError(`getJSVal(${k})`);
24
+ }
25
+ return this.#kv.get(k);
26
+ }
27
+
28
+ // Check for double free as well.
29
+ freeJSVal(k) {
30
+ if (!this.#kv.delete(k)) {
31
+ throw new WebAssembly.RuntimeError(`freeJSVal(${k})`);
32
+ }
33
+ }
34
+ }
35
+
36
+ // The actual setImmediate() to be used. This is a ESM module top
37
+ // level binding and doesn't pollute the globalThis namespace.
38
+ //
39
+ // To benchmark different setImmediate() implementations in the
40
+ // browser, use https://github.com/jphpsf/setImmediate-shim-demo as a
41
+ // starting point.
42
+ const setImmediate = await (async () => {
43
+ // node, bun, or other scripts might have set this up in the browser
44
+ if (globalThis.setImmediate) {
45
+ return globalThis.setImmediate;
46
+ }
47
+
48
+ // deno
49
+ if (globalThis.Deno) {
50
+ try {
51
+ return (await import("node:timers")).setImmediate;
52
+ } catch {}
53
+ }
54
+
55
+ // https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/postTask
56
+ if (globalThis.scheduler) {
57
+ return (cb, ...args) => scheduler.postTask(() => cb(...args));
58
+ }
59
+
60
+ // Cloudflare workers doesn't support MessageChannel
61
+ if (globalThis.MessageChannel) {
62
+ // A simple & fast setImmediate() implementation for browsers. It's
63
+ // not a drop-in replacement for node.js setImmediate() because:
64
+ // 1. There's no clearImmediate(), and setImmediate() doesn't return
65
+ // anything
66
+ // 2. There's no guarantee that callbacks scheduled by setImmediate()
67
+ // are executed in the same order (in fact it's the opposite lol),
68
+ // but you are never supposed to rely on this assumption anyway
69
+ class SetImmediate {
70
+ #fs = [];
71
+ #mc = new MessageChannel();
72
+
73
+ constructor() {
74
+ this.#mc.port1.addEventListener("message", () => {
75
+ this.#fs.pop()();
76
+ });
77
+ this.#mc.port1.start();
78
+ }
79
+
80
+ setImmediate(cb, ...args) {
81
+ this.#fs.push(() => cb(...args));
82
+ this.#mc.port2.postMessage(undefined);
83
+ }
84
+ }
85
+
86
+ const sm = new SetImmediate();
87
+ return (cb, ...args) => sm.setImmediate(cb, ...args);
88
+ }
89
+
90
+ return (cb, ...args) => setTimeout(cb, 0, ...args);
91
+ })();
92
+
93
+ export default (__exports) => {
94
+ const __ghc_wasm_jsffi_jsval_manager = new JSValManager();
95
+ const __ghc_wasm_jsffi_finalization_registry = globalThis.FinalizationRegistry ? new FinalizationRegistry(sp => __exports.rts_freeStablePtr(sp)) : { register: () => {}, unregister: () => true };
96
+ return {
97
+ newJSVal: (v) => __ghc_wasm_jsffi_jsval_manager.newJSVal(v),
98
+ getJSVal: (k) => __ghc_wasm_jsffi_jsval_manager.getJSVal(k),
99
+ freeJSVal: (k) => __ghc_wasm_jsffi_jsval_manager.freeJSVal(k),
100
+ scheduleWork: () => setImmediate(__exports.rts_schedulerLoop),
101
+ ZC0ZCghczminternalZCGHCziInternalziWasmziPrimziExportsZC: ($1,$2) => ($1.reject(new WebAssembly.RuntimeError($2))),
102
+ ZC18ZCghczminternalZCGHCziInternalziWasmziPrimziExportsZC: ($1,$2) => ($1.resolve($2)),
103
+ ZC20ZCghczminternalZCGHCziInternalziWasmziPrimziExportsZC: ($1) => {$1.throwTo = () => {};},
104
+ ZC21ZCghczminternalZCGHCziInternalziWasmziPrimziExportsZC: ($1,$2) => {$1.throwTo = (err) => __exports.rts_promiseThrowTo($2, err);},
105
+ ZC22ZCghczminternalZCGHCziInternalziWasmziPrimziExportsZC: () => {let res, rej; const p = new Promise((resolve, reject) => { res = resolve; rej = reject; }); p.resolve = res; p.reject = rej; return p;},
106
+ ZC0ZCghczminternalZCGHCziInternalziWasmziPrimziTypesZC: ($1) => (`${$1.stack ? $1.stack : $1}`),
107
+ ZC1ZCghczminternalZCGHCziInternalziWasmziPrimziTypesZC: ($1,$2) => ((new TextDecoder('utf-8', {fatal: true})).decode(new Uint8Array(__exports.memory.buffer, $1, $2))),
108
+ ZC2ZCghczminternalZCGHCziInternalziWasmziPrimziTypesZC: ($1,$2,$3) => ((new TextEncoder()).encodeInto($1, new Uint8Array(__exports.memory.buffer, $2, $3)).written),
109
+ ZC3ZCghczminternalZCGHCziInternalziWasmziPrimziTypesZC: ($1) => ($1.length),
110
+ ZC4ZCghczminternalZCGHCziInternalziWasmziPrimziTypesZC: ($1) => {try { __ghc_wasm_jsffi_finalization_registry.unregister($1); } catch {}},
111
+ ZC18ZCghczminternalZCGHCziInternalziWasmziPrimziImportsZC: ($1,$2) => ($1.then(() => __exports.rts_promiseResolveUnit($2), err => __exports.rts_promiseReject($2, err))),
112
+ ZC0ZCghczminternalZCGHCziInternalziWasmziPrimziConcziInternalZC: async ($1) => (new Promise(res => setTimeout(res, $1 / 1000))),
113
+ };
114
+ };
package/hadolint.wasm CHANGED
Binary file
package/lib/api.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import type { WasiRuntime } from './runtime.js';
2
- import type { LintOptions, LintResult, RunOptions, RunResult } from './types.js';
1
+ import type { Reactor } from './reactor.js';
2
+ import type { LintOptions, LintResult } from './types.js';
3
3
  export interface Dlinter {
4
- run: (args: string[], options?: RunOptions) => Promise<RunResult>;
5
4
  lint: (dockerfile: string, options?: LintOptions) => Promise<LintResult[]>;
6
5
  getVersion: () => Promise<string>;
7
6
  }
8
- export declare const createDlinter: (runtime: WasiRuntime) => Dlinter;
7
+ export type LoadReactor = (env: readonly string[]) => Promise<Reactor>;
8
+ export declare const createDlinter: (load: LoadReactor) => Dlinter;
package/lib/api.js CHANGED
@@ -1,37 +1,36 @@
1
- export const createDlinter = (runtime) => {
2
- let version;
1
+ const HOME = 'HOME=/';
2
+ export const createDlinter = (load) => {
3
+ const reactors = new Map();
4
+ const instance = (env) => {
5
+ const pairs = [
6
+ HOME,
7
+ ...Object.entries(env ?? {}).map(([key, value]) => `${key}=${value}`),
8
+ ];
9
+ const key = JSON.stringify(pairs);
10
+ let reactor = reactors.get(key);
11
+ if (!reactor) {
12
+ reactor = load(pairs);
13
+ reactors.set(key, reactor);
14
+ reactor.catch(() => reactors.delete(key));
15
+ }
16
+ return reactor;
17
+ };
3
18
  return {
4
- run: runtime.run,
5
19
  lint: async (dockerfile, options = {}) => {
6
- const args = ['--format', 'json'];
7
- for (const code of options.ignore ?? []) {
8
- args.push('--ignore', code);
9
- }
10
- for (const [code, level] of Object.entries(options.severityOverrides ?? {})) {
11
- if (level) {
12
- args.push(`--${level}`, code);
13
- }
14
- }
15
- args.push('-');
16
- const { stdout, stderr } = await runtime.run(args, {
17
- stdin: dockerfile,
18
- env: options.env,
20
+ const { lint } = await instance(options.env);
21
+ const findings = JSON.parse(await lint(dockerfile));
22
+ const ignored = new Set(options.ignore ?? []);
23
+ return findings
24
+ .filter(({ code }) => !ignored.has(code))
25
+ .map(finding => {
26
+ const level = options.severityOverrides?.[finding.code];
27
+ return level ? { ...finding, level } : finding;
19
28
  });
20
- if (!stdout) {
21
- if (stderr) {
22
- throw new Error(stderr.trim());
23
- }
24
- return [];
25
- }
26
- return JSON.parse(stdout);
27
29
  },
28
- getVersion: () => (version ??= runtime
29
- .run(['--version'])
30
- .then(({ stdout }) => stdout.trim())
31
- .catch((error) => {
32
- version = undefined;
33
- throw error;
34
- })),
30
+ getVersion: async () => {
31
+ const { version } = await instance();
32
+ return version();
33
+ },
35
34
  };
36
35
  };
37
36
  //# sourceMappingURL=api.js.map
package/lib/api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAwBA,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAoB,EAAW,EAAE;IAC7D,IAAI,OAAoC,CAAA;IAExC,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG;QAEhB,IAAI,EAAE,KAAK,EACT,UAAkB,EAClB,OAAO,GAAgB,EAAE,EACF,EAAE;YACzB,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;YACjC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;gBACxC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;YAC7B,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CACxC,OAAO,CAAC,iBAAiB,IAAI,EAAE,CAChC,EAAE,CAAC;gBACF,IAAI,KAAK,EAAE,CAAC;oBACV,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,EAAE,IAAI,CAAC,CAAA;gBAC/B,CAAC;YACH,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YAEd,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE;gBACjD,KAAK,EAAE,UAAU;gBACjB,GAAG,EAAE,OAAO,CAAC,GAAG;aACjB,CAAC,CAAA;YAKF,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,IAAI,MAAM,EAAE,CAAC;oBACX,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;gBAChC,CAAC;gBACD,OAAO,EAAE,CAAA;YACX,CAAC;YACD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAiB,CAAA;QAC3C,CAAC;QAED,UAAU,EAAE,GAAG,EAAE,CACf,CAAC,OAAO,KAAK,OAAO;aACjB,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC;aAClB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;aACnC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YAExB,OAAO,GAAG,SAAS,CAAA;YACnB,MAAM,KAAK,CAAA;QACb,CAAC,CAAC,CAAC;KACR,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AA+BA,MAAM,IAAI,GAAG,QAAQ,CAAA;AAGrB,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAiB,EAAW,EAAE;IAC1D,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA4B,CAAA;IAEpD,MAAM,QAAQ,GAAG,CAAC,GAA4B,EAAoB,EAAE;QAClE,MAAM,KAAK,GAAG;YACZ,IAAI;YACJ,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC;SACtE,CAAA;QAGD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAEjC,IAAI,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;YACrB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;YAE1B,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QAC3C,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;IAED,OAAO;QACL,IAAI,EAAE,KAAK,EACT,UAAkB,EAClB,OAAO,GAAgB,EAAE,EACF,EAAE;YACzB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,CAAiB,CAAA;YAMnE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAA;YAC7C,OAAO,QAAQ;iBACZ,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;iBACxC,GAAG,CAAC,OAAO,CAAC,EAAE;gBACb,MAAM,KAAK,GACT,OAAO,CAAC,iBAAiB,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;gBAG3C,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAA;YAChD,CAAC,CAAC,CAAA;QACN,CAAC;QAED,UAAU,EAAE,KAAK,IAAqB,EAAE;YACtC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,QAAQ,EAAE,CAAA;YACpC,OAAO,OAAO,EAAE,CAAA;QAClB,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
package/lib/browser.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/browser.js CHANGED
@@ -1,7 +1,26 @@
1
+ import { __rewriteRelativeImportExtension } from "tslib";
1
2
  import { createDlinter } from './api.js';
2
- import { createBrowserRuntime } from './wasi-browser.js';
3
- const dlinter = createDlinter(createBrowserRuntime());
4
- export const run = dlinter.run;
3
+ import { memoize } from './memoize.js';
4
+ import { createReactor } from './reactor.js';
5
+ const loadAssets = memoize(async () => {
6
+ const wasmUrl = new URL('../hadolint.wasm', import.meta.url);
7
+ const glueUrl = new URL('../ghc_wasm_jsffi.js', import.meta.url);
8
+ const [response, glue] = await Promise.all([
9
+ fetch(wasmUrl),
10
+ import(__rewriteRelativeImportExtension(glueUrl.href, true)),
11
+ ]);
12
+ if (!response.ok) {
13
+ throw new Error(`dlintr: could not fetch ${wasmUrl.pathname}: ${response.status} ${response.statusText}`);
14
+ }
15
+ return [
16
+ await WebAssembly.compile(await response.arrayBuffer()),
17
+ glue.default,
18
+ ];
19
+ });
20
+ const dlinter = createDlinter(async (env) => {
21
+ const [module, glue] = await loadAssets();
22
+ return createReactor(module, glue, env);
23
+ });
5
24
  export const lint = dlinter.lint;
6
25
  export const getVersion = dlinter.getVersion;
7
26
  //# sourceMappingURL=browser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAExD,MAAM,OAAO,GAAG,aAAa,CAAC,oBAAoB,EAAE,CAAC,CAAA;AAErD,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":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":";AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAO5C,MAAM,UAAU,GAAG,OAAO,CACxB,KAAK,IAA0D,EAAE;IAC/D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,kBAAkB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAA;IAC5D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,sBAAsB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAA;IAEhE,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzC,KAAK,CAAC,OAAO,CAAC;QACd,MAAM,kCAAC,OAAO,CAAC,IAAI,QAAuC;KAC3D,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,2BAA2B,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CACzF,CAAA;IACH,CAAC;IAED,OAAO;QACL,MAAM,WAAW,CAAC,OAAO,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;QACvD,IAAI,CAAC,OAAO;KACJ,CAAA;AACZ,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"}