@smoothbricks/cli 0.5.0 → 0.7.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.
@@ -1 +1 @@
1
- {"version":3,"file":"git-config.d.ts","sourceRoot":"","sources":["../../src/monorepo/git-config.ts"],"names":[],"mappings":"AAKA,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYzE"}
1
+ {"version":3,"file":"git-config.d.ts","sourceRoot":"","sources":["../../src/monorepo/git-config.ts"],"names":[],"mappings":"AAKA,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBzE"}
@@ -10,6 +10,13 @@ export async function applyWorkspaceGitConfig(root) {
10
10
  const gitDir = resolve(root, decode(gitDirResult.stdout).trim());
11
11
  const tooling = join(root, 'tooling');
12
12
  await $ `git config --local include.path ${join(tooling, 'workspace.gitconfig')}`.cwd(root);
13
+ // Keep the newer runtime version pins on any merge (nvfetcher overlay +
14
+ // devenv.lock) so a mirror sync's `git am --3way` never stalls on a version
15
+ // conflict. Mapped by the managed .gitattributes (merge=smoo-newer-pins);
16
+ // implemented in tooling/direnv/merge-newer-pins.sh. The next `devenv shell`
17
+ // regenerates package.json fields from the resulting runtime.
18
+ await $ `git config --local merge.smoo-newer-pins.name ${'keep the newer devenv/nvfetcher runtime pins'}`.cwd(root);
19
+ await $ `git config --local merge.smoo-newer-pins.driver ${'bash tooling/direnv/merge-newer-pins.sh %O %A %B %P'}`.cwd(root);
13
20
  linkHook(gitDir, tooling, 'pre-commit');
14
21
  linkHook(gitDir, tooling, 'commit-msg');
15
22
  }
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Repos may append their own content to a managed file below this marker —
3
+ * e.g. extra merge drivers in .gitattributes. Everything from the marker
4
+ * line onward is preserved verbatim across updates and ignored by the
5
+ * drift check; the managed section above it stays byte-exact.
6
+ */
7
+ export declare const LOCAL_SECTION_MARKER = "# smoo-local: everything below this line is repo-owned and preserved";
8
+ /** Split a managed target's content into the managed part and the repo-owned tail. */
9
+ declare function splitLocalSection(current: string): {
10
+ managed: string;
11
+ localTail: string;
12
+ };
13
+ /** Test seam for the pure splitter. */
14
+ export declare const splitLocalSectionForTest: typeof splitLocalSection;
1
15
  export interface FileResult {
2
16
  target: string;
3
17
  action: 'created' | 'updated' | 'unchanged' | 'skipped' | 'skipped-symlink' | 'drifted' | 'ok-symlink';
@@ -5,4 +19,5 @@ export interface FileResult {
5
19
  export declare function applyManagedFiles(root: string, mode: 'update' | 'check' | 'diff'): FileResult[];
6
20
  export declare function printResults(results: FileResult[]): void;
7
21
  export declare function validateManagedFiles(root: string): number;
22
+ export {};
8
23
  //# sourceMappingURL=managed-files.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,GAAG,YAAY,CAAC;CACxG;AAmGD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,EAAE,CAG/F;AA4KD,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAIxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzD"}
1
+ {"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,yEAAyE,CAAC;AAU3G,sFAAsF;AACtF,iBAAS,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAIlF;AAED,uCAAuC;AACvC,eAAO,MAAM,wBAAwB,0BAAoB,CAAC;AAE1D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,GAAG,YAAY,CAAC;CACxG;AA8GD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,EAAE,CAG/F;AA8KD,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAIxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzD"}
@@ -5,6 +5,22 @@ import { fileURLToPath } from 'node:url';
5
5
  import { listReleasePackages, readPackageJson } from '../lib/workspace.js';
6
6
  import { renderCiWorkflowYaml } from './ci-workflow.js';
7
7
  import { renderPublishWorkflowYaml } from './publish-workflow.js';
8
+ /**
9
+ * Repos may append their own content to a managed file below this marker —
10
+ * e.g. extra merge drivers in .gitattributes. Everything from the marker
11
+ * line onward is preserved verbatim across updates and ignored by the
12
+ * drift check; the managed section above it stays byte-exact.
13
+ */
14
+ export const LOCAL_SECTION_MARKER = '# smoo-local: everything below this line is repo-owned and preserved';
15
+ /** Split a managed target's content into the managed part and the repo-owned tail. */
16
+ function splitLocalSection(current) {
17
+ const index = current.indexOf(LOCAL_SECTION_MARKER);
18
+ if (index === -1)
19
+ return { managed: current, localTail: '' };
20
+ return { managed: current.slice(0, index), localTail: current.slice(index) };
21
+ }
22
+ /** Test seam for the pure splitter. */
23
+ export const splitLocalSectionForTest = splitLocalSection;
8
24
  const managedFiles = [
9
25
  {
10
26
  kind: 'raw',
@@ -46,6 +62,17 @@ const managedFiles = [
46
62
  source: 'git-format-staged.yml',
47
63
  target: '.git-format-staged.yml',
48
64
  },
65
+ {
66
+ kind: 'raw',
67
+ source: 'gitattributes',
68
+ target: '.gitattributes',
69
+ },
70
+ {
71
+ kind: 'raw',
72
+ source: 'tooling/direnv/merge-newer-pins.sh',
73
+ target: 'tooling/direnv/merge-newer-pins.sh',
74
+ executable: true,
75
+ },
49
76
  {
50
77
  kind: 'generated',
51
78
  source: 'ci-workflow',
@@ -103,13 +130,15 @@ function applyManagedFile(root, file, mode, context) {
103
130
  throw new Error(`${file.target} exists but is not a regular file or symlink`);
104
131
  }
105
132
  const current = readFileSync(target, 'utf8');
106
- if (current === content) {
133
+ const { managed, localTail } = splitLocalSection(current);
134
+ if (managed === content || (localTail !== '' && managed === `${content}\n`)) {
107
135
  return { target: file.target, action: 'unchanged' };
108
136
  }
109
137
  if (mode === 'check' || mode === 'diff') {
110
138
  return { target: file.target, action: 'drifted' };
111
139
  }
112
- writeManagedFile(target, content, file.executable === true);
140
+ const next = localTail === '' ? content : `${content}\n${localTail}`;
141
+ writeManagedFile(target, next, file.executable === true);
113
142
  return { target: file.target, action: 'updated' };
114
143
  }
115
144
  if (mode === 'check' || mode === 'diff') {
@@ -69,7 +69,7 @@ async function validatePackedManifest(root, pkg, packed) {
69
69
  async function validateAttw(root, pkg, packed) {
70
70
  const attw = await loadAttwCore();
71
71
  const analysis = await attw.checkPackage(await createAttwPackageFromTarball(attw, root, packed.path), {
72
- excludeEntrypoints: wasmExportEntrypoints(pkg.json.exports),
72
+ excludeEntrypoints: nonJsExportEntrypoints(pkg.json.exports),
73
73
  });
74
74
  if (!isRecord(analysis) || analysis.types === false) {
75
75
  return 0;
@@ -205,19 +205,21 @@ function formatResolutionKind(kind) {
205
205
  }
206
206
  return kind;
207
207
  }
208
- function wasmExportEntrypoints(exports) {
208
+ function nonJsExportEntrypoints(exports) {
209
209
  if (!isRecord(exports)) {
210
210
  return [];
211
211
  }
212
212
  return Object.entries(exports)
213
- .filter(([key, value]) => key.startsWith('.') && exportPointsToWasm(value))
213
+ .filter(([key, value]) => key.startsWith('.') && exportPointsToNonJs(value))
214
214
  .map(([key]) => key);
215
215
  }
216
- function exportPointsToWasm(value) {
216
+ function exportPointsToNonJs(value) {
217
217
  if (typeof value === 'string') {
218
- return value.endsWith('.wasm');
218
+ // attw resolves every entrypoint as a module; assets can never have types.
219
+ // Existence of the target files is still validated by publint.
220
+ return value.endsWith('.wasm') || value.endsWith('.css');
219
221
  }
220
- return isRecord(value) && Object.values(value).some(exportPointsToWasm);
222
+ return isRecord(value) && Object.values(value).some(exportPointsToNonJs);
221
223
  }
222
224
  async function packPackage(root, pkg) {
223
225
  const packageDir = join(root, pkg.path);
@@ -0,0 +1,8 @@
1
+ # smoo-managed. Keep the newer runtime version pins on any merge (especially a
2
+ # mirror sync's `git am --3way`) instead of stalling on a conflict. The driver
3
+ # `smoo-newer-pins` is defined by `smoo` (git config, installed at devenv setup)
4
+ # and implemented in tooling/direnv/merge-newer-pins.sh. Files not present in a
5
+ # given repo are simply ignored.
6
+ tooling/direnv/nixpkgs-overlay/_sources/generated.json merge=smoo-newer-pins
7
+ tooling/direnv/nixpkgs-overlay/_sources/generated.nix merge=smoo-newer-pins
8
+ tooling/direnv/devenv.lock merge=smoo-newer-pins
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+ # smoo-managed git merge driver: keep whichever side pins the NEWER version.
3
+ #
4
+ # Runtime version pins (nvfetcher overlay _sources/generated.{json,nix}) and the
5
+ # devenv/flake lock (devenv.lock) frequently differ across branches or across a
6
+ # mirror sync's `git am --3way`. Instead of stalling on a conflict, keep the
7
+ # higher pin wholesale so the tree always converges to the newest runtime; the
8
+ # next `devenv shell` then regenerates package.json engine/packageManager from
9
+ # it (see monorepo/runtime.ts syncRootRuntimeVersions).
10
+ #
11
+ # Wired by `smoo` (managed .gitattributes -> merge=smoo-newer-pins, and
12
+ # `git config merge.smoo-newer-pins.driver` installed by applyWorkspaceGitConfig).
13
+ # git invokes it as: merge-newer-pins.sh %O %A %B %P
14
+ # %O base, %A ours (result written here), %B theirs, %P pathname.
15
+ set -euo pipefail
16
+
17
+ ours="$2"
18
+ theirs="$3"
19
+
20
+ # Flake locks are ordered by lastModified (unix seconds); nvfetcher outputs by
21
+ # the semver in their version fields. A given file is one kind or the other.
22
+ max_last_modified() {
23
+ grep -oE '"lastModified"[[:space:]]*:[[:space:]]*[0-9]+' "$1" 2>/dev/null \
24
+ | grep -oE '[0-9]+' | sort -n | tail -1
25
+ }
26
+ max_semver() {
27
+ grep -oE 'version[[:space:]":=]*[0-9]+\.[0-9]+\.[0-9]+' "$1" 2>/dev/null \
28
+ | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -1
29
+ }
30
+
31
+ ours_lm="$(max_last_modified "$ours" || true)"
32
+ theirs_lm="$(max_last_modified "$theirs" || true)"
33
+
34
+ if [ -n "${ours_lm}${theirs_lm}" ]; then
35
+ # Flake lock: numeric lastModified comparison.
36
+ if [ -n "$theirs_lm" ] && [ "${theirs_lm:-0}" -gt "${ours_lm:-0}" ]; then
37
+ cp "$theirs" "$ours"
38
+ fi
39
+ else
40
+ # nvfetcher / version-pinned file: semver comparison.
41
+ ov="$(max_semver "$ours" || true)"
42
+ tv="$(max_semver "$theirs" || true)"
43
+ newest="$(printf '%s\n%s\n' "${ov:-0.0.0}" "${tv:-0.0.0}" | sort -V | tail -1)"
44
+ if [ -n "$tv" ] && [ "$newest" = "$tv" ] && [ "$tv" != "${ov:-}" ]; then
45
+ cp "$theirs" "$ours"
46
+ fi
47
+ fi
48
+ exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smoothbricks/cli",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "description": "SmoothBricks monorepo automation CLI",
6
6
  "bin": {
@@ -13,6 +13,16 @@ export async function applyWorkspaceGitConfig(root: string): Promise<void> {
13
13
  const tooling = join(root, 'tooling');
14
14
 
15
15
  await $`git config --local include.path ${join(tooling, 'workspace.gitconfig')}`.cwd(root);
16
+
17
+ // Keep the newer runtime version pins on any merge (nvfetcher overlay +
18
+ // devenv.lock) so a mirror sync's `git am --3way` never stalls on a version
19
+ // conflict. Mapped by the managed .gitattributes (merge=smoo-newer-pins);
20
+ // implemented in tooling/direnv/merge-newer-pins.sh. The next `devenv shell`
21
+ // regenerates package.json fields from the resulting runtime.
22
+ await $`git config --local merge.smoo-newer-pins.name ${'keep the newer devenv/nvfetcher runtime pins'}`.cwd(root);
23
+ await $`git config --local merge.smoo-newer-pins.driver ${'bash tooling/direnv/merge-newer-pins.sh %O %A %B %P'}`.cwd(
24
+ root,
25
+ );
16
26
  linkHook(gitDir, tooling, 'pre-commit');
17
27
  linkHook(gitDir, tooling, 'commit-msg');
18
28
  }
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { LOCAL_SECTION_MARKER, splitLocalSectionForTest } from './managed-files.js';
3
+
4
+ const MANAGED = '# managed content\npath merge=driver\n';
5
+
6
+ describe('managed-file local sections', () => {
7
+ it('content without a marker is entirely managed', () => {
8
+ const { managed, localTail } = splitLocalSectionForTest(MANAGED);
9
+ expect(managed).toBe(MANAGED);
10
+ expect(localTail).toBe('');
11
+ });
12
+
13
+ it('everything from the marker onward is the repo-owned tail', () => {
14
+ const tail = `${LOCAL_SECTION_MARKER}\ncustom/*.jsonl merge=custom-log\n`;
15
+ const { managed, localTail } = splitLocalSectionForTest(`${MANAGED}\n${tail}`);
16
+ expect(managed).toBe(`${MANAGED}\n`);
17
+ expect(localTail).toBe(tail);
18
+ });
19
+
20
+ it('a tail directly after the managed content tolerates the separating newline', () => {
21
+ // The compare rule accepts `managed === content + '\n'` when a tail exists,
22
+ // so update → check round-trips as unchanged.
23
+ const written = `${MANAGED}\n${LOCAL_SECTION_MARKER}\nextra\n`;
24
+ const { managed, localTail } = splitLocalSectionForTest(written);
25
+ expect(managed).toBe(`${MANAGED}\n`);
26
+ expect(localTail.startsWith(LOCAL_SECTION_MARKER)).toBe(true);
27
+ });
28
+ });
@@ -8,6 +8,14 @@ import { renderPublishWorkflowYaml } from './publish-workflow.js';
8
8
 
9
9
  type ManagedKind = 'raw' | 'template' | 'generated';
10
10
 
11
+ /**
12
+ * Repos may append their own content to a managed file below this marker —
13
+ * e.g. extra merge drivers in .gitattributes. Everything from the marker
14
+ * line onward is preserved verbatim across updates and ignored by the
15
+ * drift check; the managed section above it stays byte-exact.
16
+ */
17
+ export const LOCAL_SECTION_MARKER = '# smoo-local: everything below this line is repo-owned and preserved';
18
+
11
19
  interface ManagedFile {
12
20
  kind: ManagedKind;
13
21
  source: string;
@@ -16,6 +24,16 @@ interface ManagedFile {
16
24
  releasePackagesOnly?: boolean;
17
25
  }
18
26
 
27
+ /** Split a managed target's content into the managed part and the repo-owned tail. */
28
+ function splitLocalSection(current: string): { managed: string; localTail: string } {
29
+ const index = current.indexOf(LOCAL_SECTION_MARKER);
30
+ if (index === -1) return { managed: current, localTail: '' };
31
+ return { managed: current.slice(0, index), localTail: current.slice(index) };
32
+ }
33
+
34
+ /** Test seam for the pure splitter. */
35
+ export const splitLocalSectionForTest = splitLocalSection;
36
+
19
37
  export interface FileResult {
20
38
  target: string;
21
39
  action: 'created' | 'updated' | 'unchanged' | 'skipped' | 'skipped-symlink' | 'drifted' | 'ok-symlink';
@@ -78,6 +96,17 @@ const managedFiles: ManagedFile[] = [
78
96
  source: 'git-format-staged.yml',
79
97
  target: '.git-format-staged.yml',
80
98
  },
99
+ {
100
+ kind: 'raw',
101
+ source: 'gitattributes',
102
+ target: '.gitattributes',
103
+ },
104
+ {
105
+ kind: 'raw',
106
+ source: 'tooling/direnv/merge-newer-pins.sh',
107
+ target: 'tooling/direnv/merge-newer-pins.sh',
108
+ executable: true,
109
+ },
81
110
  {
82
111
  kind: 'generated',
83
112
  source: 'ci-workflow',
@@ -143,13 +172,15 @@ function applyManagedFile(
143
172
  throw new Error(`${file.target} exists but is not a regular file or symlink`);
144
173
  }
145
174
  const current = readFileSync(target, 'utf8');
146
- if (current === content) {
175
+ const { managed, localTail } = splitLocalSection(current);
176
+ if (managed === content || (localTail !== '' && managed === `${content}\n`)) {
147
177
  return { target: file.target, action: 'unchanged' };
148
178
  }
149
179
  if (mode === 'check' || mode === 'diff') {
150
180
  return { target: file.target, action: 'drifted' };
151
181
  }
152
- writeManagedFile(target, content, file.executable === true);
182
+ const next = localTail === '' ? content : `${content}\n${localTail}`;
183
+ writeManagedFile(target, next, file.executable === true);
153
184
  return { target: file.target, action: 'updated' };
154
185
  }
155
186
  if (mode === 'check' || mode === 'diff') {
@@ -0,0 +1,59 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { dirname, join, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const script = resolve(
9
+ dirname(fileURLToPath(import.meta.url)),
10
+ '..',
11
+ '..',
12
+ 'managed/raw/tooling/direnv/merge-newer-pins.sh',
13
+ );
14
+
15
+ // Invoke the merge driver as git does: `driver %O %A %B %P`, result written to %A (ours).
16
+ function mergedOurs(oursContent: string, theirsContent: string): string {
17
+ const dir = mkdtempSync(join(tmpdir(), 'mnp-'));
18
+ try {
19
+ const base = join(dir, 'base');
20
+ const ours = join(dir, 'ours');
21
+ const theirs = join(dir, 'theirs');
22
+ writeFileSync(base, '');
23
+ writeFileSync(ours, oursContent);
24
+ writeFileSync(theirs, theirsContent);
25
+ const r = spawnSync('bash', [script, base, ours, theirs, 'pins'], { encoding: 'utf8' });
26
+ expect(r.status).toBe(0);
27
+ return readFileSync(ours, 'utf8');
28
+ } finally {
29
+ rmSync(dir, { recursive: true, force: true });
30
+ }
31
+ }
32
+
33
+ describe('smoo-newer-pins merge driver', () => {
34
+ it('nvfetcher JSON: takes theirs when it pins a newer semver', () => {
35
+ expect(mergedOurs('{"bun":{"version": "1.3.13"}}', '{"bun":{"version": "1.3.14"}}')).toContain('1.3.14');
36
+ });
37
+
38
+ it('nvfetcher JSON: keeps ours when ours pins the newer semver', () => {
39
+ expect(mergedOurs('{"bun":{"version": "1.3.14"}}', '{"bun":{"version": "1.3.13"}}')).toContain('1.3.14');
40
+ });
41
+
42
+ it('nvfetcher nix form: takes the newer semver', () => {
43
+ expect(mergedOurs('version = "1.3.13";', 'version = "1.3.14";')).toContain('1.3.14');
44
+ });
45
+
46
+ it('flake lock: takes theirs when lastModified is newer', () => {
47
+ const out = mergedOurs('{"nixpkgs":{"lastModified": 1777573317}}', '{"nixpkgs":{"lastModified": 1779717400}}');
48
+ expect(out).toContain('1779717400');
49
+ });
50
+
51
+ it('flake lock: keeps ours when ours lastModified is newer', () => {
52
+ const out = mergedOurs('{"nixpkgs":{"lastModified": 1779717400}}', '{"nixpkgs":{"lastModified": 1777573317}}');
53
+ expect(out).toContain('1779717400');
54
+ });
55
+
56
+ it('equal versions: keeps ours unchanged (idempotent)', () => {
57
+ expect(mergedOurs('{"v": "1.3.14", "note": "ours"}', '{"v": "1.3.14", "note": "theirs"}')).toContain('ours');
58
+ });
59
+ });
@@ -90,7 +90,7 @@ async function validatePackedManifest(
90
90
  async function validateAttw(root: string, pkg: PackageInfo, packed: { path: string }): Promise<number> {
91
91
  const attw = await loadAttwCore();
92
92
  const analysis = await attw.checkPackage(await createAttwPackageFromTarball(attw, root, packed.path), {
93
- excludeEntrypoints: wasmExportEntrypoints(pkg.json.exports),
93
+ excludeEntrypoints: nonJsExportEntrypoints(pkg.json.exports),
94
94
  });
95
95
  if (!isRecord(analysis) || analysis.types === false) {
96
96
  return 0;
@@ -242,20 +242,22 @@ function formatResolutionKind(kind: string): string {
242
242
  return kind;
243
243
  }
244
244
 
245
- function wasmExportEntrypoints(exports: unknown): string[] {
245
+ function nonJsExportEntrypoints(exports: unknown): string[] {
246
246
  if (!isRecord(exports)) {
247
247
  return [];
248
248
  }
249
249
  return Object.entries(exports)
250
- .filter(([key, value]) => key.startsWith('.') && exportPointsToWasm(value))
250
+ .filter(([key, value]) => key.startsWith('.') && exportPointsToNonJs(value))
251
251
  .map(([key]) => key);
252
252
  }
253
253
 
254
- function exportPointsToWasm(value: unknown): boolean {
254
+ function exportPointsToNonJs(value: unknown): boolean {
255
255
  if (typeof value === 'string') {
256
- return value.endsWith('.wasm');
256
+ // attw resolves every entrypoint as a module; assets can never have types.
257
+ // Existence of the target files is still validated by publint.
258
+ return value.endsWith('.wasm') || value.endsWith('.css');
257
259
  }
258
- return isRecord(value) && Object.values(value).some(exportPointsToWasm);
260
+ return isRecord(value) && Object.values(value).some(exportPointsToNonJs);
259
261
  }
260
262
 
261
263
  async function packPackage(root: string, pkg: PackageInfo): Promise<{ path: string; arrayBuffer: ArrayBuffer }> {
@@ -18,7 +18,7 @@ afterEach(() => {
18
18
 
19
19
  const PR_JSON = JSON.stringify({
20
20
  number: 40,
21
- url: 'https://github.com/conloca/private/pull/40',
21
+ url: 'https://github.com/acme/private/pull/40',
22
22
  headRefName: 'gar-sync/private-to-public',
23
23
  baseRefName: 'public-mirror',
24
24
  isCrossRepository: false,
@@ -145,7 +145,7 @@ function writeState(overrides: Record<string, unknown>): void {
145
145
  mkdirSync(join(gitDir, 'smoo'), { recursive: true });
146
146
  const state = {
147
147
  pr: 40,
148
- url: 'https://github.com/conloca/private/pull/40',
148
+ url: 'https://github.com/acme/private/pull/40',
149
149
  headBranch: 'gar-sync/private-to-public',
150
150
  baseBranch: 'public-mirror',
151
151
  remote: 'origin',