From 7b1eb131bc092a70ec810cdf63c96814f91d9009 Mon Sep 17 00:00:00 2001 From: GENTILHOMME Thomas Date: Thu, 9 Jul 2026 13:49:51 +0200 Subject: [PATCH 1/2] fix(scanner): ensure extractors are working in a web env --- .changeset/real-clowns-stare.md | 7 ++ workspaces/contact/README.md | 54 +-------- workspaces/contact/docs/utils.md | 113 ++++++++++++++++++ workspaces/contact/package.json | 13 +- .../contact/src/ContactExtractor.class.ts | 24 +--- workspaces/contact/src/index.ts | 10 +- .../src/utils/extractMetadataContacts.ts | 22 ++++ workspaces/contact/src/utils/index.ts | 1 + workspaces/contact/src/web.ts | 12 ++ .../contact/test/ContactExtractor.spec.ts | 2 +- workspaces/contact/test/tsconfig.json | 12 ++ .../contact/test/utils/compareContact.spec.ts | 2 +- .../utils/extractMetadataContacts.spec.ts | 86 +++++++++++++ .../contact/test/utils/parseRegexp.spec.ts | 2 +- .../test/utils/toContactWithMetadata.spec.ts | 6 +- workspaces/mama/package.json | 10 +- workspaces/mama/test/tsconfig.json | 12 ++ workspaces/scanner/package.json | 6 +- .../HighlightedContactsExtractor.class.ts | 15 ++- .../HighlightedPackagesExtractor.class.ts | 18 ++- .../test/extractors/browser-compat.spec.ts | 47 ++++++++ workspaces/scanner/test/tsconfig.json | 12 ++ 22 files changed, 387 insertions(+), 99 deletions(-) create mode 100644 .changeset/real-clowns-stare.md create mode 100644 workspaces/contact/docs/utils.md create mode 100644 workspaces/contact/src/utils/extractMetadataContacts.ts create mode 100644 workspaces/contact/src/web.ts create mode 100644 workspaces/contact/test/tsconfig.json create mode 100644 workspaces/contact/test/utils/extractMetadataContacts.spec.ts create mode 100644 workspaces/mama/test/tsconfig.json create mode 100644 workspaces/scanner/test/extractors/browser-compat.spec.ts create mode 100644 workspaces/scanner/test/tsconfig.json diff --git a/.changeset/real-clowns-stare.md b/.changeset/real-clowns-stare.md new file mode 100644 index 00000000..76a75116 --- /dev/null +++ b/.changeset/real-clowns-stare.md @@ -0,0 +1,7 @@ +--- +"@nodesecure/contact": minor +"@nodesecure/mama": minor +"@nodesecure/scanner": patch +--- + +Refactor highlight extractors to make them work in a web env. Add unit-test to scanner to ensure compatibility and CI break. diff --git a/workspaces/contact/README.md b/workspaces/contact/README.md index e214c334..490a0480 100644 --- a/workspaces/contact/README.md +++ b/workspaces/contact/README.md @@ -55,6 +55,7 @@ console.log({ illuminated, expired }); ## API - [NsResolver](./docs/NsResolver.md) +- [Utilities](./docs/utils.md) (compareContact, toContactWithMetadata, extractMetadataContacts, parseRegExp) Contact is defined by the following TypeScript interface: ```ts @@ -104,59 +105,6 @@ export interface ContactWithMetadata extends Contact { flags: ContactFlag[]; } -``` -### compareContact(contactA: Partial< Contact > | ContactWithMetadata, contactB: Partial< Contact > | ContactWithMetadata, options?: CompareOptions): boolean - -Compare two contacts and return `true` if they are the same person - -```ts -import { - compareContact -} from "@nodesecure/contact"; -import assert from "node:assert"; - -assert.ok( - compareContact( - { name: "john doe" }, - { name: "John Doe" } - ) -); -``` - -Each string is trimmed, converted to lowercase, and any multiple spaces are reduced to a single space. - -#### Options - -```ts -interface CompareOptions { - /** - * @default true - */ - compareName?: boolean; -} -``` - -### toContactWithMetadata>(contact: T): T & {flags: ContactFlag[] } - -Apply some transformation on a contact such as adding a flag when the contact use a free email service - -```ts -import { - toContactWithMetadata -} from "@nodesecure/contact"; -import assert from "node:assert"; - -assert.deepEqual( -toContactWithMetadata({ - name:"john doe", - email: "johndoe@gmail.com" - }), - { - name:"john doe", - email: "johndoe@gmail.com", - flags: ["free-email-service"] - } -); ``` ## License diff --git a/workspaces/contact/docs/utils.md b/workspaces/contact/docs/utils.md new file mode 100644 index 00000000..f4bc0ed3 --- /dev/null +++ b/workspaces/contact/docs/utils.md @@ -0,0 +1,113 @@ +# Utilities + +Contact is defined by the following TypeScript interface: +```ts +interface Contact { + email?: string; + url?: string; + name: string; +} +``` + +## compareContact(contactA: Partial< Contact > | ContactWithMetadata, contactB: Partial< Contact > | ContactWithMetadata, options?: CompareOptions): boolean + +Compare two contacts and return `true` if they are the same person + +```ts +import { + compareContact +} from "@nodesecure/contact"; +import assert from "node:assert"; + +assert.ok( + compareContact( + { name: "john doe" }, + { name: "John Doe" } + ) +); +``` + +Each string is trimmed, converted to lowercase, and any multiple spaces are reduced to a single space. + +### Options + +```ts +interface CompareOptions { + /** + * @default true + */ + compareName?: boolean; +} +``` + +## toContactWithMetadata>(contact: T): T & { flags: ContactFlag[] } + +Apply some transformation on a contact such as adding a flag when the contact use a free email service + +```ts +import { + toContactWithMetadata +} from "@nodesecure/contact"; +import assert from "node:assert"; + +assert.deepEqual( +toContactWithMetadata({ + name:"john doe", + email: "johndoe@gmail.com" + }), + { + name:"john doe", + email: "johndoe@gmail.com", + flags: ["free-email-service"] + } +); +``` + +## extractMetadataContacts(metadata: ContactPackageMetaData): ContactWithMetadata[] + +Extract the author (if any) and maintainers of a package metadata object as a flat array of `ContactWithMetadata`, applying `toContactWithMetadata` on each of them. The author (when present) is always returned first, followed by the maintainers. + +```ts +import { + extractMetadataContacts +} from "@nodesecure/contact"; +import assert from "node:assert"; + +assert.deepEqual( + extractMetadataContacts({ + author: { name: "john doe", email: "john@gmail.com" }, + maintainers: [{ name: "jane doe" }] + }), + [ + { name: "john doe", email: "john@gmail.com", flags: ["free-email-service"] }, + { name: "jane doe", flags: [] } + ] +); +``` + +```ts +interface ContactExtractorPackageMetadata { + author?: Contact | null; + maintainers: Contact[]; +} + +type ContactPackageMetaData = Partial; +``` + +## parseRegExp(input: string): RegExp | null + +Parse a string containing a literal RegExp (e.g. `"/^hello/i"`) into a `RegExp` instance. Returns `null` when the input doesn't match the literal RegExp syntax. Only the `gimsuy` flags are kept, any other character found after the closing slash is ignored. + +```ts +import { + parseRegExp +} from "@nodesecure/contact"; +import assert from "node:assert"; + +const regexp = parseRegExp("/^hello/i"); + +assert.ok(regexp instanceof RegExp); +assert.ok(regexp.test("Hello World")); + +assert.strictEqual(parseRegExp("hello"), null); +``` diff --git a/workspaces/contact/package.json b/workspaces/contact/package.json index af88cce0..084b4258 100644 --- a/workspaces/contact/package.json +++ b/workspaces/contact/package.json @@ -3,8 +3,15 @@ "version": "3.2.0", "description": "Utilities to extract/fetch data on NPM contacts (author, maintainers ..)", "type": "module", - "exports": "./dist/index.js", - "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js" + }, + "./web": { + "types": "./dist/web.d.ts", + "import": "./dist/web.js" + } + }, "engines": { "node": ">=20" }, @@ -12,7 +19,7 @@ "build": "tsc -b", "prepublishOnly": "npm run build", "test-only": "node --test \"./test/**/*.spec.ts\"", - "test-types": "npm run build && tsd && attw --pack . --profile esm-only", + "test-types": "npm run build && attw --pack . --profile esm-only", "test": "c8 -r html npm run test-only && npm run test-types" }, "publishConfig": { diff --git a/workspaces/contact/src/ContactExtractor.class.ts b/workspaces/contact/src/ContactExtractor.class.ts index 26270246..997fbdbc 100644 --- a/workspaces/contact/src/ContactExtractor.class.ts +++ b/workspaces/contact/src/ContactExtractor.class.ts @@ -1,5 +1,5 @@ // Import Third-party Dependencies -import type { Contact, PackumentVersion, Packument } from "@nodesecure/npm-types"; +import type { PackumentVersion, Packument } from "@nodesecure/npm-types"; // Import Internal Dependencies import { @@ -8,7 +8,10 @@ import { type IlluminatedContact } from "./UnlitContact.class.ts"; import { NsResolver } from "./NsResolver.class.ts"; -import { toContactWithMetadata } from "./utils/index.ts"; +import { + extractMetadataContacts, + type ContactExtractorPackageMetadata +} from "./utils/index.ts"; import type { ContactWithMetadata } from "./types.ts"; export type { @@ -16,13 +19,6 @@ export type { EnforcedContact }; -export interface ContactExtractorPackageMetadata { - author?: Contact | null; - maintainers: Contact[]; -} - -type ContactPackageMetaData = Partial; - export interface ContactExtractorFromDependenciesResult { illuminated: IlluminatedContact[]; /** @@ -126,13 +122,3 @@ export class ContactExtractor { }; } } - -export function extractMetadataContacts( - metadata: ContactPackageMetaData -): ContactWithMetadata[] { - return [ - ...(metadata.author ? [toContactWithMetadata(metadata.author)] : []), - ...(metadata.maintainers ? metadata.maintainers.map(toContactWithMetadata) : []) - ]; -} - diff --git a/workspaces/contact/src/index.ts b/workspaces/contact/src/index.ts index 9669a6e6..755e9d96 100644 --- a/workspaces/contact/src/index.ts +++ b/workspaces/contact/src/index.ts @@ -1,8 +1,8 @@ export * from "./ContactExtractor.class.ts"; -export { - compareContact, - toContactWithMetadata -} from "./utils/index.ts"; +export * from "./utils/index.ts"; export { NsResolver } from "./NsResolver.class.ts"; export { UnlitContact } from "./UnlitContact.class.ts"; -export { type ContactWithMetadata } from "./types.ts"; +export type { + ContactWithMetadata, + ContactFlag +} from "./types.ts"; diff --git a/workspaces/contact/src/utils/extractMetadataContacts.ts b/workspaces/contact/src/utils/extractMetadataContacts.ts new file mode 100644 index 00000000..84eac6e3 --- /dev/null +++ b/workspaces/contact/src/utils/extractMetadataContacts.ts @@ -0,0 +1,22 @@ +// Import Third-party Dependencies +import type { Contact } from "@nodesecure/npm-types"; + +// Import Internal Dependencies +import { toContactWithMetadata } from "./toContactWithMetadata.ts"; +import type { ContactWithMetadata } from "../types.ts"; + +export interface ContactExtractorPackageMetadata { + author?: Contact | null; + maintainers: Contact[]; +} + +export type ContactPackageMetaData = Partial; + +export function extractMetadataContacts( + metadata: ContactPackageMetaData +): ContactWithMetadata[] { + return [ + ...(metadata.author ? [toContactWithMetadata(metadata.author)] : []), + ...(metadata.maintainers ? metadata.maintainers.map(toContactWithMetadata) : []) + ]; +} diff --git a/workspaces/contact/src/utils/index.ts b/workspaces/contact/src/utils/index.ts index 8909c75a..f62bf12d 100644 --- a/workspaces/contact/src/utils/index.ts +++ b/workspaces/contact/src/utils/index.ts @@ -1,3 +1,4 @@ export * from "./compareContact.ts"; export * from "./parseRegexp.ts"; export * from "./toContactWithMetadata.ts"; +export * from "./extractMetadataContacts.ts"; diff --git a/workspaces/contact/src/web.ts b/workspaces/contact/src/web.ts new file mode 100644 index 00000000..274a9486 --- /dev/null +++ b/workspaces/contact/src/web.ts @@ -0,0 +1,12 @@ +export * from "./utils/index.ts"; +export { + UnlitContact +} from "./UnlitContact.class.ts"; +export type { + EnforcedContact, + IlluminatedContact +} from "./ContactExtractor.class.ts"; +export type { + ContactFlag, + ContactWithMetadata +} from "./types.ts"; diff --git a/workspaces/contact/test/ContactExtractor.spec.ts b/workspaces/contact/test/ContactExtractor.spec.ts index c2ecfba3..0720fbed 100644 --- a/workspaces/contact/test/ContactExtractor.spec.ts +++ b/workspaces/contact/test/ContactExtractor.spec.ts @@ -11,9 +11,9 @@ import { faker } from "@faker-js/faker"; // Import Internal Dependencies import { ContactExtractor, + NsResolver, type ContactExtractorPackageMetadata } from "../src/index.ts"; -import { NsResolver } from "../src/NsResolver.class.ts"; // CONSTANTS const kManifestFixturePath = join(import.meta.dirname, "fixtures", "manifest"); diff --git a/workspaces/contact/test/tsconfig.json b/workspaces/contact/test/tsconfig.json new file mode 100644 index 00000000..c095016c --- /dev/null +++ b/workspaces/contact/test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "..", + "noEmit": true, + "composite": false + }, + "include": [ + "../src", + "." + ] +} diff --git a/workspaces/contact/test/utils/compareContact.spec.ts b/workspaces/contact/test/utils/compareContact.spec.ts index 8a935256..e6e0dc6c 100644 --- a/workspaces/contact/test/utils/compareContact.spec.ts +++ b/workspaces/contact/test/utils/compareContact.spec.ts @@ -6,7 +6,7 @@ import { describe, test } from "node:test"; import type { Contact } from "@nodesecure/npm-types"; // Import Internal Dependencies -import { compareContact } from "../../src/utils/index.ts"; +import { compareContact } from "../../src/index.ts"; describe("compareContact", () => { test("Given Contacts with the same name, it must return true", () => { diff --git a/workspaces/contact/test/utils/extractMetadataContacts.spec.ts b/workspaces/contact/test/utils/extractMetadataContacts.spec.ts new file mode 100644 index 00000000..7a4217ea --- /dev/null +++ b/workspaces/contact/test/utils/extractMetadataContacts.spec.ts @@ -0,0 +1,86 @@ +// Import Node.js Dependencies +import assert from "node:assert"; +import { describe, test } from "node:test"; + +// Import Third-party Dependencies +import type { Contact } from "@nodesecure/npm-types"; + +// Import Internal Dependencies +import { extractMetadataContacts } from "../../src/index.ts"; + +describe("extractMetadataContacts", () => { + test("Given an empty metadata object, it must return an empty array", () => { + assert.deepEqual( + extractMetadataContacts({}), + [] + ); + }); + + test("Given only an author, it must return it as a ContactWithMetadata", () => { + const author: Contact = { + name: "john doe", + email: "john@something.com" + }; + + assert.deepEqual( + extractMetadataContacts({ author }), + [ + { name: "john doe", email: "john@something.com", flags: [] } + ] + ); + }); + + test("Given only maintainers, it must return them as ContactWithMetadata", () => { + const maintainers: Contact[] = [ + { name: "john doe" }, + { name: "jane doe", email: "jane@gmail.com" } + ]; + + assert.deepEqual( + extractMetadataContacts({ maintainers }), + [ + { name: "john doe", flags: [] }, + { name: "jane doe", email: "jane@gmail.com", flags: ["free-email-service"] } + ] + ); + }); + + test("Given both an author and maintainers, it must return the author first followed by the maintainers", () => { + const author: Contact = { name: "john doe" }; + const maintainers: Contact[] = [ + { name: "jane doe" }, + { name: "jack doe" } + ]; + + assert.deepEqual( + extractMetadataContacts({ author, maintainers }), + [ + { name: "john doe", flags: [] }, + { name: "jane doe", flags: [] }, + { name: "jack doe", flags: [] } + ] + ); + }); + + test("Given a null author, it must be ignored", () => { + assert.deepEqual( + extractMetadataContacts({ author: null, maintainers: [{ name: "jane doe" }] }), + [ + { name: "jane doe", flags: [] } + ] + ); + }); + + test("Given an author already flagged as ContactWithMetadata, it must be returned as-is", () => { + const author = { + name: "john doe", + email: "john@gmail.com", + flags: ["free-email-service"] as const + }; + + assert.deepEqual( + extractMetadataContacts({ author }), + [author] + ); + }); +}); diff --git a/workspaces/contact/test/utils/parseRegexp.spec.ts b/workspaces/contact/test/utils/parseRegexp.spec.ts index 3427e57c..c3c4104e 100644 --- a/workspaces/contact/test/utils/parseRegexp.spec.ts +++ b/workspaces/contact/test/utils/parseRegexp.spec.ts @@ -3,7 +3,7 @@ import assert from "node:assert"; import { describe, test } from "node:test"; // Import Internal Dependencies -import { parseRegExp } from "../../src/utils/index.ts"; +import { parseRegExp } from "../../src/index.ts"; describe("parseRegExp", () => { test("Given a literal with no slash then it must return null", () => { diff --git a/workspaces/contact/test/utils/toContactWithMetadata.spec.ts b/workspaces/contact/test/utils/toContactWithMetadata.spec.ts index f4a35d71..9ea77de5 100644 --- a/workspaces/contact/test/utils/toContactWithMetadata.spec.ts +++ b/workspaces/contact/test/utils/toContactWithMetadata.spec.ts @@ -6,8 +6,10 @@ import { describe, test } from "node:test"; import type { Contact } from "@nodesecure/npm-types"; // Import Internal Dependencies -import { type ContactWithMetadata } from "../../src/types.ts"; -import { toContactWithMetadata } from "../../src/utils/index.ts"; +import { + toContactWithMetadata, + type ContactWithMetadata +} from "../../src/index.ts"; describe("toContactWithMetadata", () => { test("should transform to a contact without the free-domain flag", () => { diff --git a/workspaces/mama/package.json b/workspaces/mama/package.json index 0e777d9f..46999b97 100644 --- a/workspaces/mama/package.json +++ b/workspaces/mama/package.json @@ -3,13 +3,17 @@ "version": "2.3.1", "description": "Manifest Manager", "type": "module", - "exports": "./dist/index.js", - "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js" + }, + "./utils/*": "./dist/utils/*" + }, "scripts": { "build": "tsc -b", "prepublishOnly": "npm run build", "test-only": "node --test \"./test/**/*.spec.ts\"", - "test-types": "npm run build && tsd && attw --pack . --profile esm-only", + "test-types": "npm run build && attw --pack . --profile esm-only", "test": "c8 -r html npm run test-only && npm run test-types" }, "publishConfig": { diff --git a/workspaces/mama/test/tsconfig.json b/workspaces/mama/test/tsconfig.json new file mode 100644 index 00000000..c095016c --- /dev/null +++ b/workspaces/mama/test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "..", + "noEmit": true, + "composite": false + }, + "include": [ + "../src", + "." + ] +} diff --git a/workspaces/scanner/package.json b/workspaces/scanner/package.json index 4019a1eb..6ccd7d84 100644 --- a/workspaces/scanner/package.json +++ b/workspaces/scanner/package.json @@ -23,7 +23,8 @@ "prepublishOnly": "npm run build && pkg-ok", "test": "c8 -r html npm run test-only && npm run test-types", "test-only": "node --test \"./test/**/*.spec.ts\"", - "test-types": "attw --pack . --profile esm-only" + "test-types": "attw --pack . --profile esm-only", + "test-extractors-browser-compat": "node --test \"./test/extractors/browser-compat.spec.ts\"" }, "publishConfig": { "registry": "https://registry.npmjs.org", @@ -88,6 +89,7 @@ "@npmcli/config": "^10.4.2", "@types/node": "^26.0.0", "@types/npmcli__config": "^6.0.3", - "c8": "^11.0.0" + "c8": "^11.0.0", + "esbuild": "0.28.1" } } diff --git a/workspaces/scanner/src/extractors/probes/HighlightedContactsExtractor.class.ts b/workspaces/scanner/src/extractors/probes/HighlightedContactsExtractor.class.ts index 1347d7e2..6babcf3b 100644 --- a/workspaces/scanner/src/extractors/probes/HighlightedContactsExtractor.class.ts +++ b/workspaces/scanner/src/extractors/probes/HighlightedContactsExtractor.class.ts @@ -5,7 +5,7 @@ import { UnlitContact, extractMetadataContacts, type ContactWithMetadata -} from "@nodesecure/contact"; +} from "@nodesecure/contact/web"; // Import Internal Dependencies import type { @@ -17,16 +17,23 @@ export type HighlightedContactsResult = { illuminated: IlluminatedContact[]; }; -export class HighlightedContacts implements PackumentProbeExtractor { +export class HighlightedContacts implements PackumentProbeExtractor< + HighlightedContactsResult +> { level = "packument" as const; #unlitContacts: UnlitContact[]; - constructor(contacts: EnforcedContact[]) { + constructor( + contacts: EnforcedContact[] + ) { this.#unlitContacts = contacts.map((contact) => new UnlitContact(contact)); } - next(packageName: string, dependency: Dependency) { + next( + packageName: string, + dependency: Dependency + ) { const extractedContacts = extractMetadataContacts(dependency.metadata); this.addDependencyToUnlitContacts(extractedContacts, packageName); } diff --git a/workspaces/scanner/src/extractors/probes/HighlightedPackagesExtractor.class.ts b/workspaces/scanner/src/extractors/probes/HighlightedPackagesExtractor.class.ts index fa3a5eb5..90fe82ef 100644 --- a/workspaces/scanner/src/extractors/probes/HighlightedPackagesExtractor.class.ts +++ b/workspaces/scanner/src/extractors/probes/HighlightedPackagesExtractor.class.ts @@ -1,5 +1,5 @@ // Import Third-party Dependencies -import { parseNpmSpec } from "@nodesecure/mama"; +import { parseNpmSpec } from "@nodesecure/mama/utils/parseNpmSpec.js"; import semver from "semver"; // Import Internal Dependencies @@ -13,16 +13,22 @@ export type HighlightedPackagesResult = { highlightedPackages: string[]; }; -export class HighlightedPackages implements ManifestProbeExtractor { +export class HighlightedPackages implements ManifestProbeExtractor< + HighlightedPackagesResult +> { level = "manifest" as const; #semverRanges: Record; #highlightedPackages = new Set(); - constructor(packages: HighlightPackages) { + constructor( + packages: HighlightPackages + ) { this.#semverRanges = this.#parseSemverRange(packages); } - #parseSemverRange(packages: HighlightPackages) { + #parseSemverRange( + packages: HighlightPackages + ) { const pkgs = Array.isArray(packages) ? this.#parseSpecs(packages) : packages; return Object.entries(pkgs).reduce((acc, [name, semverRange]) => { @@ -37,7 +43,9 @@ export class HighlightedPackages implements ManifestProbeExtractor { // Handle scope-only entries like "@fastify", matching all packages under that scope if (/^@[^/@]+$/.test(spec)) { diff --git a/workspaces/scanner/test/extractors/browser-compat.spec.ts b/workspaces/scanner/test/extractors/browser-compat.spec.ts new file mode 100644 index 00000000..841c6835 --- /dev/null +++ b/workspaces/scanner/test/extractors/browser-compat.spec.ts @@ -0,0 +1,47 @@ +// Import Node.js Dependencies +import assert from "node:assert"; +import { readdirSync } from "node:fs"; +import path from "node:path"; +import { describe, it } from "node:test"; + +// Import Third-party Dependencies +import * as esbuild from "esbuild"; + +// CONSTANTS +const kExtractorsDir = path.join(import.meta.dirname, "..", "..", "src", "extractors"); +const kProbesDir = path.join(kExtractorsDir, "probes"); +const kEntryPoints = [ + path.join(kExtractorsDir, "index.ts"), + path.join(kExtractorsDir, "payload.ts"), + ...readdirSync(kProbesDir) + .filter((file) => file.endsWith(".ts")) + .map((file) => path.join(kProbesDir, file)) +]; + +describe("Extractors browser compatibility", () => { + for (const entryPoint of kEntryPoints) { + const entryName = path.relative(kExtractorsDir, entryPoint); + + it(`should bundle '${entryName}' for a browser target with no Node.js builtins`, async() => { + try { + await esbuild.build({ + entryPoints: [entryPoint], + bundle: true, + write: false, + platform: "browser", + external: [], + logLevel: "silent" + }); + } + catch (error: any) { + const reasons = (error.errors ?? []) + .map((buildError: esbuild.Message) => buildError.text) + .join("\n"); + + assert.fail( + `'${entryName}' is not browser-compatible (it cannot be bundled for a "browser" platform):\n${reasons || error.message}` + ); + } + }); + } +}); diff --git a/workspaces/scanner/test/tsconfig.json b/workspaces/scanner/test/tsconfig.json new file mode 100644 index 00000000..c095016c --- /dev/null +++ b/workspaces/scanner/test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "..", + "noEmit": true, + "composite": false + }, + "include": [ + "../src", + "." + ] +} From c1bdf3f14a9ec13bd2ccb658ff2ed0805990865a Mon Sep 17 00:00:00 2001 From: "Thomas.G" Date: Thu, 9 Jul 2026 14:29:08 +0200 Subject: [PATCH 2/2] Update workspaces/contact/docs/utils.md Co-authored-by: PierreDemailly <39910767+PierreDemailly@users.noreply.github.com> --- workspaces/contact/docs/utils.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/workspaces/contact/docs/utils.md b/workspaces/contact/docs/utils.md index f4bc0ed3..ac58ef26 100644 --- a/workspaces/contact/docs/utils.md +++ b/workspaces/contact/docs/utils.md @@ -51,14 +51,14 @@ import { import assert from "node:assert"; assert.deepEqual( -toContactWithMetadata({ + toContactWithMetadata({ name:"john doe", email: "johndoe@gmail.com" }), { - name:"john doe", - email: "johndoe@gmail.com", - flags: ["free-email-service"] + name:"john doe", + email: "johndoe@gmail.com", + flags: ["free-email-service"] } ); ```