diff --git a/.editorconfig b/.editorconfig index 77b164ecf2b..ab809d52996 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,5 +6,5 @@ trim_trailing_whitespace = true insert_final_newline = true [*.js] -indent_style = space +indent_style = tab indent_size = 2 diff --git a/completion b/completion deleted file mode 100644 index 3fc6dfe2977..00000000000 --- a/completion +++ /dev/null @@ -1 +0,0 @@ -mple diff --git a/packages/@romejs-integration/vscode/src/extension.ts b/packages/@romejs-integration/vscode/src/extension.ts index cc71a66c042..f7be921ccd8 100644 --- a/packages/@romejs-integration/vscode/src/extension.ts +++ b/packages/@romejs-integration/vscode/src/extension.ts @@ -6,10 +6,10 @@ */ import { - LanguageClient, - LanguageClientOptions, - ServerOptions, - TransportKind, + LanguageClient, + LanguageClientOptions, + ServerOptions, + TransportKind, } from 'vscode-languageclient'; // rome-ignore resolver/notFound import * as vscode from 'vscode'; @@ -22,151 +22,151 @@ import os = require('os'); let client: LanguageClient; function crawl(root: string): Iterable { - return { - [Symbol.iterator]() { - return { - next() { - const value = root; - root = path.dirname(value); - - return { - value, - done: root === '.' || root === '/', - }; - }, - }; - }, - }; + return { + [Symbol.iterator]() { + return { + next() { + const value = root; + root = path.dirname(value); + + return { + value, + done: root === '.' || root === '/', + }; + }, + }; + }, + }; } async function tryChain( - root: string, - suffix: string, + root: string, + suffix: string, ): Promise { - for (const dir of crawl(root)) { - const possible = path.join(dir, suffix); - - try { - await fs.promises.access(possible); - return possible; - } catch (err) { - } - } - return undefined; + for (const dir of crawl(root)) { + const possible = path.join(dir, suffix); + + try { + await fs.promises.access(possible); + return possible; + } catch (err) { + } + } + return undefined; } async function tryManifest(root: string): Promise { - for (const dir of crawl(root)) { - const manifestLoc = path.join(dir, 'package.json'); - - try { - const content = await fs.promises.readFile(manifestLoc, 'utf8'); - const json = JSON.parse(content); - if (json.romeLSPBin) { - return String(path.resolve(dir, json.romeLSPBin)); - } - } catch (err) { - if (err instanceof SyntaxError || err.code === 'ENOENT') { - continue; - } else { - throw err; - } - } - } - return undefined; + for (const dir of crawl(root)) { + const manifestLoc = path.join(dir, 'package.json'); + + try { + const content = await fs.promises.readFile(manifestLoc, 'utf8'); + const json = JSON.parse(content); + if (json.romeLSPBin) { + return String(path.resolve(dir, json.romeLSPBin)); + } + } catch (err) { + if (err instanceof SyntaxError || err.code === 'ENOENT') { + continue; + } else { + throw err; + } + } + } + return undefined; } async function getRomeLocation(): Promise { - const {workspaceFolders} = vscode.workspace; - if (workspaceFolders === undefined) { - return undefined; - } - - // Find relative to workspace folders - for (const {uri} of workspaceFolders) { - if (uri.scheme === 'file') { - const manifest = await tryManifest(uri.path); - if (manifest !== undefined) { - return manifest; - } - - const nodeModules = await tryChain(uri.path, `node_modules/rome/index.js`); - if (nodeModules !== undefined) { - return nodeModules; - } - } - } - - // Find development build - try { - const possible = path.join(os.tmpdir(), 'rome-dev', 'index.js'); - await fs.promises.access(possible); - return possible; - } catch (err) { - } - - return undefined; + const {workspaceFolders} = vscode.workspace; + if (workspaceFolders === undefined) { + return undefined; + } + + // Find relative to workspace folders + for (const {uri} of workspaceFolders) { + if (uri.scheme === 'file') { + const manifest = await tryManifest(uri.path); + if (manifest !== undefined) { + return manifest; + } + + const nodeModules = await tryChain(uri.path, `node_modules/rome/index.js`); + if (nodeModules !== undefined) { + return nodeModules; + } + } + } + + // Find development build + try { + const possible = path.join(os.tmpdir(), 'rome-dev', 'index.js'); + await fs.promises.access(possible); + return possible; + } catch (err) { + } + + return undefined; } export async function activate() { - const outputChannel = vscode.window.createOutputChannel('Rome'); - - function log(message: string) { - outputChannel.appendLine(message); - } - - let romePath: undefined | string = await getRomeLocation(); - - // If no romePath was found then watch workspace folders until we find one - if (romePath === undefined) { - log( - 'No Rome path found. Waiting for workspace folder changes before trying again', - ); - - await new Promise((resolve) => { - const event = vscode.workspace.onDidChangeWorkspaceFolders(() => { - getRomeLocation().then((filename) => { - if (filename !== undefined) { - romePath = filename; - event.dispose(); - resolve(); - } - }); - }); - }); - } - - if (romePath === undefined) { - throw new Error('Should have been defined'); - } - - log(`Discovered Rome path ${romePath}`); - - let serverOptions: ServerOptions = { - module: romePath, - args: ['lsp'], - transport: TransportKind.stdio, - }; - - let clientOptions: LanguageClientOptions = { - outputChannel, - documentSelector: [ - {scheme: 'file', language: 'javascript'}, - {scheme: 'file', language: 'javascriptreact'}, - {scheme: 'file', language: 'typescript'}, - {scheme: 'file', language: 'typescriptreact'}, - {scheme: 'file', language: 'json'}, - ], - }; - - client = new LanguageClient('rome', 'Rome', serverOptions, clientOptions); - - client.start(); + const outputChannel = vscode.window.createOutputChannel('Rome'); + + function log(message: string) { + outputChannel.appendLine(message); + } + + let romePath: undefined | string = await getRomeLocation(); + + // If no romePath was found then watch workspace folders until we find one + if (romePath === undefined) { + log( + 'No Rome path found. Waiting for workspace folder changes before trying again', + ); + + await new Promise((resolve) => { + const event = vscode.workspace.onDidChangeWorkspaceFolders(() => { + getRomeLocation().then((filename) => { + if (filename !== undefined) { + romePath = filename; + event.dispose(); + resolve(); + } + }); + }); + }); + } + + if (romePath === undefined) { + throw new Error('Should have been defined'); + } + + log(`Discovered Rome path ${romePath}`); + + let serverOptions: ServerOptions = { + module: romePath, + args: ['lsp'], + transport: TransportKind.stdio, + }; + + let clientOptions: LanguageClientOptions = { + outputChannel, + documentSelector: [ + {scheme: 'file', language: 'javascript'}, + {scheme: 'file', language: 'javascriptreact'}, + {scheme: 'file', language: 'typescript'}, + {scheme: 'file', language: 'typescriptreact'}, + {scheme: 'file', language: 'json'}, + ], + }; + + client = new LanguageClient('rome', 'Rome', serverOptions, clientOptions); + + client.start(); } export function deactivate(): Thenable | undefined { - if (!client) { - return undefined; - } - return client.stop(); + if (!client) { + return undefined; + } + return client.stop(); } diff --git a/packages/@romejs-runtime/rome/test.ts b/packages/@romejs-runtime/rome/test.ts index 1604c52d87c..b18ab3e35a6 100644 --- a/packages/@romejs-runtime/rome/test.ts +++ b/packages/@romejs-runtime/rome/test.ts @@ -14,8 +14,8 @@ export type SyncThrower = () => void; export type ExpectedError = undefined | string | RegExp | Function; export type TestSnapshotOptions = { - filename?: string; - language?: string; + filename?: string; + language?: string; }; // These diagnostics are subsets of the official diagnostics @@ -24,74 +24,70 @@ export type TestSnapshotOptions = { export type TestDiagnosticLogCategory = 'none' | 'info' | 'warn' | 'error'; export type TestDiagnosticAdviceInspect = { - type: 'inspect'; - data: JSONPropertyValue; + type: 'inspect'; + data: JSONPropertyValue; }; export type TestDiagnosticAdviceList = { - type: 'list'; - list: Array; + type: 'list'; + list: Array; }; export type TestDiagnosticAdviceCode = { - type: 'code'; - code: string; + type: 'code'; + code: string; }; export type TestDiagnosticAdviceLog = { - type: 'log'; - category: TestDiagnosticLogCategory; - text: string; + type: 'log'; + category: TestDiagnosticLogCategory; + text: string; }; export type TestDiagnosticAdviceItem = - | TestDiagnosticAdviceInspect - | TestDiagnosticAdviceCode - | TestDiagnosticAdviceLog - | TestDiagnosticAdviceList; + | TestDiagnosticAdviceInspect + | TestDiagnosticAdviceCode + | TestDiagnosticAdviceLog + | TestDiagnosticAdviceList; export interface TestHelper { - addToAdvice(item: TestDiagnosticAdviceItem): void; - clearAdvice(): void; - onTeardown(callback: AsyncFunc): void; - clearTimeout(): void; - extendTimeout(time: number): void; - setTimeout(time: number): void; - checkTimeout(): void; - truthy(value: unknown, message?: string): void; - falsy(value: unknown, message?: string): void; - true(value: unknown, message?: string): void; - false(value: unknown, message?: string): void; - is(received: unknown, expected: unknown, message?: string): void; - not(received: unknown, expected: unknown, message?: string): void; - looksLike(received: unknown, expected: unknown, message?: string): void; - notLooksLike(received: unknown, expected: unknown, message?: string): void; - throws( - thrower: SyncThrower, - expected?: ExpectedError, - message?: string, - ): void; - throwsAsync( - thrower: AsyncFunc, - expected?: ExpectedError, - message?: string, - ): Promise; - notThrows(nonThrower: SyncThrower, message?: string): void; - notThrowsAsync(nonThrower: AsyncFunc, message?: string): Promise; - regex(contents: string, regex: RegExp, message?: string): void; - notRegex(contents: string, regex: RegExp, message?: string): void; - snapshot( - expected: unknown, - message?: string, - opts?: TestSnapshotOptions, - ): string; - inlineSnapshot(received: unknown, expected?: string | boolean | number): void; - namedSnapshot( - name: string, - expected: unknown, - message?: string, - opts?: TestSnapshotOptions, - ): string; + addToAdvice(item: TestDiagnosticAdviceItem): void; + clearAdvice(): void; + onTeardown(callback: AsyncFunc): void; + clearTimeout(): void; + extendTimeout(time: number): void; + setTimeout(time: number): void; + checkTimeout(): void; + truthy(value: unknown, message?: string): void; + falsy(value: unknown, message?: string): void; + true(value: unknown, message?: string): void; + false(value: unknown, message?: string): void; + is(received: unknown, expected: unknown, message?: string): void; + not(received: unknown, expected: unknown, message?: string): void; + looksLike(received: unknown, expected: unknown, message?: string): void; + notLooksLike(received: unknown, expected: unknown, message?: string): void; + throws(thrower: SyncThrower, expected?: ExpectedError, message?: string): void; + throwsAsync( + thrower: AsyncFunc, + expected?: ExpectedError, + message?: string, + ): Promise; + notThrows(nonThrower: SyncThrower, message?: string): void; + notThrowsAsync(nonThrower: AsyncFunc, message?: string): Promise; + regex(contents: string, regex: RegExp, message?: string): void; + notRegex(contents: string, regex: RegExp, message?: string): void; + snapshot( + expected: unknown, + message?: string, + opts?: TestSnapshotOptions, + ): string; + inlineSnapshot(received: unknown, expected?: string | boolean | number): void; + namedSnapshot( + name: string, + expected: unknown, + message?: string, + opts?: TestSnapshotOptions, + ): string; } export type TestName = string | Array; @@ -99,101 +95,101 @@ export type TestName = string | Array; declare const __ROME__TEST_OPTIONS__: GlobalTestOptions; export type GlobalTestOptions = - | undefined - | { - dirname?: string; - register?: (err: Error, opts: TestOptions, callback: TestCallback) => void; - }; + | undefined + | { + dirname?: string; + register?: (err: Error, opts: TestOptions, callback: TestCallback) => void; + }; type NamelessTestOptions = { - timeout?: number; - only?: boolean; + timeout?: number; + only?: boolean; }; export type TestCallback = (t: TestHelper) => void | undefined | Promise; export type TestOptions = NamelessTestOptions & { - name: TestName; + name: TestName; }; type TestArg = TestName | NamelessTestOptions | TestCallback | undefined; export const testOptions: NonNullable = - __ROME__TEST_OPTIONS__ === undefined ? {} : __ROME__TEST_OPTIONS__; + __ROME__TEST_OPTIONS__ === undefined ? {} : __ROME__TEST_OPTIONS__; function registerTest( - callsiteError: Error, - opts: TestOptions, - callback: TestCallback, + callsiteError: Error, + opts: TestOptions, + callback: TestCallback, ) { - const register = testOptions.register; + const register = testOptions.register; - if (typeof register !== 'function') { - throw new Error('Test harness does not exist'); - } + if (typeof register !== 'function') { + throw new Error('Test harness does not exist'); + } - register(callsiteError, opts, callback); + register(callsiteError, opts, callback); } function isOptionsObject(arg: TestArg): arg is NamelessTestOptions { - return typeof arg === 'object' && arg != null && !Array.isArray(arg); + return typeof arg === 'object' && arg != null && !Array.isArray(arg); } function splitArgs( - args: TestRegisterFunctionArgs, + args: TestRegisterFunctionArgs, ): { - options: TestOptions; - callback: TestCallback; + options: TestOptions; + callback: TestCallback; } { - const name = args.shift(); - if (typeof name !== 'string' && !Array.isArray(name)) { - throw new Error('Expected test name to be a string or an array of strings'); - } - - const callback = args.pop(); - if (typeof callback !== 'function') { - throw new Error('Expected options callback'); - } - - const options = args.pop(); - if (options !== undefined && !isOptionsObject(options)) { - throw new Error('Expected options object'); - } - - if (args.length > 0) { - throw new Error('Expected to have exhausted test register arguments'); - } - - return { - options: { - ...options, - name, - }, - callback, - }; + const name = args.shift(); + if (typeof name !== 'string' && !Array.isArray(name)) { + throw new Error('Expected test name to be a string or an array of strings'); + } + + const callback = args.pop(); + if (typeof callback !== 'function') { + throw new Error('Expected options callback'); + } + + const options = args.pop(); + if (options !== undefined && !isOptionsObject(options)) { + throw new Error('Expected options object'); + } + + if (args.length > 0) { + throw new Error('Expected to have exhausted test register arguments'); + } + + return { + options: { + ...options, + name, + }, + callback, + }; } type TestRegisterFunctionArgs = - | [TestName, TestCallback] - | [TestName, NamelessTestOptions, TestCallback]; + | [TestName, TestCallback] + | [TestName, NamelessTestOptions, TestCallback]; type TestRegisterFunction = (...args: TestRegisterFunctionArgs) => void; export const test: TestRegisterFunction & { - only: TestRegisterFunction; + only: TestRegisterFunction; } = function(...args: TestRegisterFunctionArgs) { - const {options, callback} = splitArgs(args); - registerTest(new Error(), options, callback); + const {options, callback} = splitArgs(args); + registerTest(new Error(), options, callback); }; test.only = function(...args: TestRegisterFunctionArgs) { - const {options, callback} = splitArgs(args); - registerTest( - new Error(), - { - ...options, - only: true, - }, - callback, - ); + const {options, callback} = splitArgs(args); + registerTest( + new Error(), + { + ...options, + only: true, + }, + callback, + ); }; diff --git a/packages/@romejs-runtime/rome/types.ts b/packages/@romejs-runtime/rome/types.ts index 91e85fc50fd..4fd8b8a3a83 100644 --- a/packages/@romejs-runtime/rome/types.ts +++ b/packages/@romejs-runtime/rome/types.ts @@ -7,17 +7,17 @@ // These are copied from packages/@romejs/codec-json/types.ts export type JSONValue = - | null - | string - | number - | boolean - | JSONObject - | JSONArray; + | null + | string + | number + | boolean + | JSONObject + | JSONArray; export type JSONPropertyValue = undefined | void | JSONValue; export type JSONObject = { - [x: string]: JSONPropertyValue; + [x: string]: JSONPropertyValue; }; export type JSONArray = Array; diff --git a/packages/@romejs/cli-diagnostics/DiagnosticsPrinter.ts b/packages/@romejs/cli-diagnostics/DiagnosticsPrinter.ts index 8b72de20fb4..4e82ae69690 100644 --- a/packages/@romejs/cli-diagnostics/DiagnosticsPrinter.ts +++ b/packages/@romejs/cli-diagnostics/DiagnosticsPrinter.ts @@ -6,626 +6,621 @@ */ import { - Diagnostic, - DiagnosticAdvice, - DiagnosticLanguage, - DiagnosticSourceType, - Diagnostics, - DiagnosticsProcessor, - deriveRootAdviceFromDiagnostic, + Diagnostic, + DiagnosticAdvice, + DiagnosticLanguage, + DiagnosticSourceType, + Diagnostics, + DiagnosticsProcessor, + deriveRootAdviceFromDiagnostic, } from '@romejs/diagnostics'; import {Reporter} from '@romejs/cli-reporter'; import { - DiagnosticsFileReader, - DiagnosticsFileReaderStats, - DiagnosticsPrinterFlags, - DiagnosticsPrinterOptions, + DiagnosticsFileReader, + DiagnosticsFileReaderStats, + DiagnosticsPrinterFlags, + DiagnosticsPrinterOptions, } from './types'; import { - formatAnsi, - markup, - markupToPlainTextString, + formatAnsi, + markup, + markupToPlainTextString, } from '@romejs/string-markup'; import {ToLines, toLines} from './utils'; import printAdvice from './printAdvice'; import {default as successBanner} from './banners/success.json'; import {default as errorBanner} from './banners/error.json'; import { - AbsoluteFilePath, - AbsoluteFilePathSet, - UnknownFilePath, - UnknownFilePathMap, - UnknownFilePathSet, - createAbsoluteFilePath, - createUnknownFilePath, + AbsoluteFilePath, + AbsoluteFilePathSet, + UnknownFilePath, + UnknownFilePathMap, + UnknownFilePathSet, + createAbsoluteFilePath, + createUnknownFilePath, } from '@romejs/path'; import {Number0, Number1} from '@romejs/ob1'; import {existsSync, lstatSync, readFileTextSync} from '@romejs/fs'; type Banner = { - // Array should really be [number, number, number], but TypeScript widens the imported types - palettes: Array>; - // Array should really be [number, number], same reason as above - rows: Array>>; + // Array should really be [number, number, number], but TypeScript widens the imported types + palettes: Array>; + // Array should really be [number, number], same reason as above + rows: Array>>; }; type PositionLike = { - line?: undefined | Number1; - column?: undefined | Number0; + line?: undefined | Number1; + column?: undefined | Number0; }; export function readDiagnosticsFileLocal( - path: AbsoluteFilePath, + path: AbsoluteFilePath, ): ReturnType { - if (!existsSync(path)) { - return; - } + if (!existsSync(path)) { + return; + } - const src = readFileTextSync(path); - const mtime = lstatSync(path).mtimeMs; - return {content: src, mtime}; + const src = readFileTextSync(path); + const mtime = lstatSync(path).mtimeMs; + return {content: src, mtime}; } function equalPosition( - a: undefined | PositionLike, - b: undefined | PositionLike, + a: undefined | PositionLike, + b: undefined | PositionLike, ): boolean { - if (a === undefined || b === undefined) { - return false; - } + if (a === undefined || b === undefined) { + return false; + } - if (a.line !== b.line || a.column !== b.column) { - return false; - } + if (a.line !== b.line || a.column !== b.column) { + return false; + } - return true; + return true; } type FooterPrintCallback = ( - reporter: Reporter, - error: boolean, + reporter: Reporter, + error: boolean, ) => void | boolean; export const DEFAULT_PRINTER_FLAGS: DiagnosticsPrinterFlags = { - grep: '', - inverseGrep: false, - showAllDiagnostics: true, - fieri: false, - verboseDiagnostics: false, - maxDiagnostics: 100, + grep: '', + inverseGrep: false, + showAllDiagnostics: true, + fieri: false, + verboseDiagnostics: false, + maxDiagnostics: 100, }; // Dependency that may not be included in the output diagnostic but whose changes may effect the validity of this one type ChangeFileDependency = { - type: 'change'; - path: UnknownFilePath; - mtime: number; + type: 'change'; + path: UnknownFilePath; + mtime: number; }; // Dependency that will have a code frame in the output diagnostic type ReferenceFileDependency = { - type: 'reference'; - path: UnknownFilePath; - mtime: undefined | number; - sourceType: undefined | DiagnosticSourceType; - language: undefined | DiagnosticLanguage; + type: 'reference'; + path: UnknownFilePath; + mtime: undefined | number; + sourceType: undefined | DiagnosticSourceType; + language: undefined | DiagnosticLanguage; }; type FileDependency = ChangeFileDependency | ReferenceFileDependency; export type DiagnosticsPrinterFileSources = UnknownFilePathMap<{ - sourceText: string; - lines: ToLines; + sourceText: string; + lines: ToLines; }>; export type DiagnosticsPrinterFileMtimes = UnknownFilePathMap; export default class DiagnosticsPrinter extends Error { - constructor(opts: DiagnosticsPrinterOptions) { - super( - "Diagnostics printer. If you're seeing this then it wasn't caught and printed correctly.", - ); - const {cwd, reporter, flags = DEFAULT_PRINTER_FLAGS} = opts; - - this.reporter = reporter; - this.flags = flags; - this.readFile = - opts.readFile === undefined ? readDiagnosticsFileLocal : opts.readFile; - this.cwd = cwd === undefined ? createAbsoluteFilePath(process.cwd()) : cwd; - this.processor = - opts.processor === undefined ? new DiagnosticsProcessor() : opts.processor; - - this.displayedCount = 0; - this.problemCount = 0; - this.filteredCount = 0; - this.truncatedCount = 0; - - this.hasTruncatedDiagnostics = false; - this.missingFileSources = new AbsoluteFilePathSet(); - this.fileSources = new UnknownFilePathMap(); - this.fileMtimes = new UnknownFilePathMap(); - this.onFooterPrintCallbacks = []; - } - - reporter: Reporter; - processor: DiagnosticsProcessor; - onFooterPrintCallbacks: Array; - flags: DiagnosticsPrinterFlags; - cwd: AbsoluteFilePath; - readFile: DiagnosticsFileReader; - - hasTruncatedDiagnostics: boolean; - missingFileSources: AbsoluteFilePathSet; - fileSources: DiagnosticsPrinterFileSources; - fileMtimes: DiagnosticsPrinterFileMtimes; - - displayedCount: number; - problemCount: number; - filteredCount: number; - truncatedCount: number; - - createFilePath(filename: undefined | string): UnknownFilePath { - if (filename === undefined) { - filename = 'unknown'; - } - - const {normalizeFilename} = this.reporter.markupOptions; - - if (normalizeFilename === undefined) { - return createUnknownFilePath(filename); - } else { - return createUnknownFilePath(normalizeFilename(filename)); - } - } - - throwIfAny() { - if (this.hasDiagnostics()) { - throw this; - } - } - - hasDiagnostics(): boolean { - return this.processor.hasDiagnostics(); - } - - getDisplayedProblemsCount() { - return this.problemCount - this.filteredCount; - } - - shouldTruncate(): boolean { - if ( - !this.flags.showAllDiagnostics && - this.displayedCount > this.flags.maxDiagnostics - ) { - return true; - } else { - return false; - } - } - - getDiagnostics(): Diagnostics { - return this.processor.getSortedDiagnostics(); - } - - shouldIgnore(diag: Diagnostic): boolean { - const {grep, inverseGrep} = this.flags; - - // An empty grep pattern means show everything - if (grep === undefined || grep === '') { - return false; - } - - // Match against the supplied grep pattern - let ignored = - markupToPlainTextString(diag.description.message.value).toLowerCase().includes( - grep, - ) === false; - if (inverseGrep) { - ignored = !ignored; - } - return ignored; - } - - addFileSource( - info: ChangeFileDependency | ReferenceFileDependency, - stats: DiagnosticsFileReaderStats, - ) { - this.fileMtimes.set(info.path, stats.mtime); - - if (info.type === 'reference') { - this.fileSources.set( - info.path, - { - sourceText: stats.content, - lines: toLines({ - path: info.path, - input: stats.content, - sourceType: info.sourceType, - language: info.language, - }), - }, - ); - } - } - - getDependenciesFromDiagnostics( - diagnostics: Diagnostics, - ): Array { - const deps: Array = []; - - for (const { - dependencies, - description: {advice}, - location: {language, sourceType, mtime, filename}, - } of diagnostics) { - if (filename !== undefined) { - deps.push({ - type: 'reference', - path: this.createFilePath(filename), - mtime, - language, - sourceType, - }); - } - - if (dependencies !== undefined) { - for (const {filename, mtime} of dependencies) { - deps.push({ - type: 'change', - path: this.createFilePath(filename), - mtime, - }); - } - } - - for (const item of advice) { - if (item.type === 'frame') { - const {location} = item; - if ( - location.filename !== undefined && - location.sourceText === undefined - ) { - deps.push({ - type: 'reference', - path: this.createFilePath(location.filename), - language: location.language, - sourceType: location.sourceType, - mtime: location.mtime, - }); - } - } - } - } - - const depsMap: UnknownFilePathMap = new UnknownFilePathMap(); - - // Remove non-absolute filenames and normalize sourceType and language for conflicts - for (const dep of deps) { - const path = dep.path; - if (!path.isAbsolute()) { - continue; - } - - const existing = depsMap.get(path); - - // reference dependency can override change since it has more metadata that needs conflict resolution - if (existing === undefined || existing.type === 'change') { - depsMap.set(dep.path, dep); - continue; - } - - if (dep.type === 'reference') { - if (existing.sourceType !== dep.sourceType) { - existing.sourceType = 'unknown'; - } - - if (existing.language !== dep.language) { - existing.language = 'unknown'; - } - } - } - - return Array.from(depsMap.values()); - } - - fetchFileSources(diagnostics: Diagnostics) { - for (const dep of this.getDependenciesFromDiagnostics(diagnostics)) { - const {path} = dep; - if (!path.isAbsolute()) { - continue; - } - - const abs = path.assertAbsolute(); - const stats = this.readFile(abs); - if (stats === undefined) { - this.missingFileSources.add(abs); - } else { - this.addFileSource(dep, stats); - } - } - } - - print() { - const filteredDiagnostics = this.filterDiagnostics(); - this.fetchFileSources(filteredDiagnostics); - this.displayDiagnostics(filteredDiagnostics); - } - - displayDiagnostics(diagnostics: Diagnostics) { - const restoreRedirect = this.reporter.redirectOutToErr(true); - for (const diag of diagnostics) { - this.displayDiagnostic(diag); - } - this.reporter.redirectOutToErr(restoreRedirect); - } - - getOutdatedFiles(diag: Diagnostic): UnknownFilePathSet { - let outdatedFiles: UnknownFilePathSet = new UnknownFilePathSet(); - for (const { - path, - mtime: expectedMtime, - } of this.getDependenciesFromDiagnostics([diag])) { - const mtime = this.fileMtimes.get(path); - if ( - mtime !== undefined && - expectedMtime !== undefined && - mtime > expectedMtime - ) { - outdatedFiles.add(path); - } - } - return outdatedFiles; - } - - displayDiagnostic(diag: Diagnostic) { - const {reporter} = this; - const {start, end, filename} = diag.location; - let advice = [...diag.description.advice]; - - // Remove stacktrace from beginning if it contains only one frame that matches the root diagnostic location - const firstAdvice = advice[0]; - if ( - firstAdvice !== undefined && - firstAdvice.type === 'stacktrace' && - firstAdvice.frames.length === 1 - ) { - const frame = firstAdvice.frames[0]; - if (frame.filename === filename && equalPosition(frame, start)) { - advice.shift(); - } - } - - // Determine if we should skip showing the frame at the top of the diagnostic output - // We check if there are any frame advice entries that match us exactly, this is - // useful for stuff like reporting call stacks - let skipFrame = false; - if (start !== undefined && end !== undefined) { - adviceLoop: for (const item of advice) { - if ( - item.type === 'frame' && - item.location.filename === filename && - equalPosition(item.location.start, start) && - equalPosition(item.location.end, end) - ) { - skipFrame = true; - break; - } - - if (item.type === 'stacktrace') { - for (const frame of item.frames) { - if (frame.filename === filename && equalPosition(frame, start)) { - skipFrame = true; - break adviceLoop; - } - } - } - } - } - - const outdatedAdvice: DiagnosticAdvice = []; - const outdatedFiles = this.getOutdatedFiles(diag); - const isOutdated = outdatedFiles.size > 0; - if (isOutdated) { - const outdatedFilesArr = Array.from(outdatedFiles, (path) => path.join()); - - if (outdatedFilesArr.length === 1 && outdatedFilesArr[0] === filename) { - outdatedAdvice.push({ - type: 'log', - category: 'warn', - text: 'This file has been changed since the diagnostic was produced and may be out of date', - }); - } else { - outdatedAdvice.push({ - type: 'log', - category: 'warn', - text: 'This diagnostic may be out of date as it relies on the following files that have been changed since the diagnostic was generated', - }); - - outdatedAdvice.push({ - type: 'list', - list: outdatedFilesArr.map((filename) => - markup`` - ), - }); - } - } - - const derived = deriveRootAdviceFromDiagnostic( - diag, - { - skipFrame, - includeHeaderInAdvice: false, - outdated: isOutdated, - }, - ); - - reporter.hr(derived.header); - - reporter.indent(() => { - // Concat all the advice together - const allAdvice: DiagnosticAdvice = [ - ...derived.advice, - ...outdatedAdvice, - ...advice, - ]; - - // Print advice - for (const item of allAdvice) { - const res = printAdvice( - item, - { - printer: this, - flags: this.flags, - missingFileSources: this.missingFileSources, - fileSources: this.fileSources, - diagnostic: diag, - reporter, - }, - ); - if (res.printed) { - reporter.br(); - } - if (res.truncated) { - this.hasTruncatedDiagnostics = true; - } - } - - // Print verbose information - if (this.flags.verboseDiagnostics) { - const {origins} = diag; - - if (origins !== undefined && origins.length > 0) { - reporter.br(); - reporter.info('Why are you seeing this diagnostic?'); - reporter.br(); - reporter.list( - origins.map((origin) => { - let res = `${origin.category}`; - if (origin.message !== undefined) { - res += `: ${origin.message}`; - } - return res; - }), - {ordered: true}, - ); - } - } - }); - } - - filterDiagnostics(): Diagnostics { - const diagnostics = this.getDiagnostics(); - const filteredDiagnostics: Diagnostics = []; - - for (const diag of diagnostics) { - this.problemCount++; - - if (this.shouldIgnore(diag)) { - this.filteredCount++; - } else if (this.shouldTruncate()) { - this.truncatedCount++; - } else { - this.displayedCount++; - filteredDiagnostics.push(diag); - } - } - - return filteredDiagnostics; - } - - onFooterPrint(fn: FooterPrintCallback) { - this.onFooterPrintCallbacks.push(fn); - } - - footer() { - const {reporter, problemCount} = this; - - const isError = problemCount > 0; - - if (isError) { - const restoreRedirect = reporter.redirectOutToErr(true); - reporter.hr(); - reporter.redirectOutToErr(restoreRedirect); - } - - if (this.hasTruncatedDiagnostics) { - reporter.warn( - 'Some diagnostics have been truncated. Use the --verbose-diagnostics flag to disable truncation.', - ); - } - - if (isError) { - if (this.flags.fieri) { - this.showBanner(errorBanner); - } - } else { - if (this.flags.fieri) { - this.showBanner(successBanner); - } - } - - for (const handler of this.onFooterPrintCallbacks) { - const stop = handler(reporter, isError); - if (stop) { - return; - } - } - - if (isError) { - this.footerError(); - } else { - reporter.success('No known problems!'); - } - } - - showBanner(banner: Banner) { - for (const stream of this.reporter.getStreams(false)) { - for (const row of banner.rows) { - for (const field of row) { - let palleteIndex; - let times = 1; - if (Array.isArray(field)) { - [palleteIndex, times] = field; - } else { - palleteIndex = field; - } - - const pallete = banner.palettes[palleteIndex]; - stream.write( - formatAnsi.bgRgb( - ' ', - { - r: pallete[0], - g: pallete[1], - b: pallete[2], - }, - ).repeat(times), - ); - } - stream.write('\n'); - } - } - } - - footerError() { - const {reporter, filteredCount} = this; - - const displayableProblems = this.getDisplayedProblemsCount(); - let str = `Found ${displayableProblems} ${displayableProblems}`; - - if (filteredCount > 0) { - str += ` (${filteredCount} filtered)`; - } - - reporter.error(str); - - if (this.truncatedCount > 0) { - const {maxDiagnostics} = this.flags; - reporter.warn( - `Only ${maxDiagnostics} errors shown, add the --show-all-diagnostics flag to view the remaining ${displayableProblems - - maxDiagnostics} errors`, - ); - } - } + constructor(opts: DiagnosticsPrinterOptions) { + super( + "Diagnostics printer. If you're seeing this then it wasn't caught and printed correctly.", + ); + const {cwd, reporter, flags = DEFAULT_PRINTER_FLAGS} = opts; + + this.reporter = reporter; + this.flags = flags; + this.readFile = + opts.readFile === undefined ? readDiagnosticsFileLocal : opts.readFile; + this.cwd = cwd === undefined ? createAbsoluteFilePath(process.cwd()) : cwd; + this.processor = + opts.processor === undefined ? new DiagnosticsProcessor() : opts.processor; + + this.displayedCount = 0; + this.problemCount = 0; + this.filteredCount = 0; + this.truncatedCount = 0; + + this.hasTruncatedDiagnostics = false; + this.missingFileSources = new AbsoluteFilePathSet(); + this.fileSources = new UnknownFilePathMap(); + this.fileMtimes = new UnknownFilePathMap(); + this.onFooterPrintCallbacks = []; + } + + reporter: Reporter; + processor: DiagnosticsProcessor; + onFooterPrintCallbacks: Array; + flags: DiagnosticsPrinterFlags; + cwd: AbsoluteFilePath; + readFile: DiagnosticsFileReader; + + hasTruncatedDiagnostics: boolean; + missingFileSources: AbsoluteFilePathSet; + fileSources: DiagnosticsPrinterFileSources; + fileMtimes: DiagnosticsPrinterFileMtimes; + + displayedCount: number; + problemCount: number; + filteredCount: number; + truncatedCount: number; + + createFilePath(filename: undefined | string): UnknownFilePath { + if (filename === undefined) { + filename = 'unknown'; + } + + const {normalizeFilename} = this.reporter.markupOptions; + + if (normalizeFilename === undefined) { + return createUnknownFilePath(filename); + } else { + return createUnknownFilePath(normalizeFilename(filename)); + } + } + + throwIfAny() { + if (this.hasDiagnostics()) { + throw this; + } + } + + hasDiagnostics(): boolean { + return this.processor.hasDiagnostics(); + } + + getDisplayedProblemsCount() { + return this.problemCount - this.filteredCount; + } + + shouldTruncate(): boolean { + if ( + !this.flags.showAllDiagnostics && + this.displayedCount > this.flags.maxDiagnostics + ) { + return true; + } else { + return false; + } + } + + getDiagnostics(): Diagnostics { + return this.processor.getSortedDiagnostics(); + } + + shouldIgnore(diag: Diagnostic): boolean { + const {grep, inverseGrep} = this.flags; + + // An empty grep pattern means show everything + if (grep === undefined || grep === '') { + return false; + } + + // Match against the supplied grep pattern + let ignored = + markupToPlainTextString(diag.description.message.value).toLowerCase().includes( + grep, + ) === false; + if (inverseGrep) { + ignored = !ignored; + } + return ignored; + } + + addFileSource( + info: ChangeFileDependency | ReferenceFileDependency, + stats: DiagnosticsFileReaderStats, + ) { + this.fileMtimes.set(info.path, stats.mtime); + + if (info.type === 'reference') { + this.fileSources.set( + info.path, + { + sourceText: stats.content, + lines: toLines({ + path: info.path, + input: stats.content, + sourceType: info.sourceType, + language: info.language, + }), + }, + ); + } + } + + getDependenciesFromDiagnostics(diagnostics: Diagnostics): Array { + const deps: Array = []; + + for (const { + dependencies, + description: {advice}, + location: {language, sourceType, mtime, filename}, + } of diagnostics) { + if (filename !== undefined) { + deps.push({ + type: 'reference', + path: this.createFilePath(filename), + mtime, + language, + sourceType, + }); + } + + if (dependencies !== undefined) { + for (const {filename, mtime} of dependencies) { + deps.push({ + type: 'change', + path: this.createFilePath(filename), + mtime, + }); + } + } + + for (const item of advice) { + if (item.type === 'frame') { + const {location} = item; + if (location.filename !== undefined && location.sourceText === undefined) { + deps.push({ + type: 'reference', + path: this.createFilePath(location.filename), + language: location.language, + sourceType: location.sourceType, + mtime: location.mtime, + }); + } + } + } + } + + const depsMap: UnknownFilePathMap = new UnknownFilePathMap(); + + // Remove non-absolute filenames and normalize sourceType and language for conflicts + for (const dep of deps) { + const path = dep.path; + if (!path.isAbsolute()) { + continue; + } + + const existing = depsMap.get(path); + + // reference dependency can override change since it has more metadata that needs conflict resolution + if (existing === undefined || existing.type === 'change') { + depsMap.set(dep.path, dep); + continue; + } + + if (dep.type === 'reference') { + if (existing.sourceType !== dep.sourceType) { + existing.sourceType = 'unknown'; + } + + if (existing.language !== dep.language) { + existing.language = 'unknown'; + } + } + } + + return Array.from(depsMap.values()); + } + + fetchFileSources(diagnostics: Diagnostics) { + for (const dep of this.getDependenciesFromDiagnostics(diagnostics)) { + const {path} = dep; + if (!path.isAbsolute()) { + continue; + } + + const abs = path.assertAbsolute(); + const stats = this.readFile(abs); + if (stats === undefined) { + this.missingFileSources.add(abs); + } else { + this.addFileSource(dep, stats); + } + } + } + + print() { + const filteredDiagnostics = this.filterDiagnostics(); + this.fetchFileSources(filteredDiagnostics); + this.displayDiagnostics(filteredDiagnostics); + } + + displayDiagnostics(diagnostics: Diagnostics) { + const restoreRedirect = this.reporter.redirectOutToErr(true); + for (const diag of diagnostics) { + this.displayDiagnostic(diag); + } + this.reporter.redirectOutToErr(restoreRedirect); + } + + getOutdatedFiles(diag: Diagnostic): UnknownFilePathSet { + let outdatedFiles: UnknownFilePathSet = new UnknownFilePathSet(); + for (const { + path, + mtime: expectedMtime, + } of this.getDependenciesFromDiagnostics([diag])) { + const mtime = this.fileMtimes.get(path); + if ( + mtime !== undefined && + expectedMtime !== undefined && + mtime > expectedMtime + ) { + outdatedFiles.add(path); + } + } + return outdatedFiles; + } + + displayDiagnostic(diag: Diagnostic) { + const {reporter} = this; + const {start, end, filename} = diag.location; + let advice = [...diag.description.advice]; + + // Remove stacktrace from beginning if it contains only one frame that matches the root diagnostic location + const firstAdvice = advice[0]; + if ( + firstAdvice !== undefined && + firstAdvice.type === 'stacktrace' && + firstAdvice.frames.length === 1 + ) { + const frame = firstAdvice.frames[0]; + if (frame.filename === filename && equalPosition(frame, start)) { + advice.shift(); + } + } + + // Determine if we should skip showing the frame at the top of the diagnostic output + // We check if there are any frame advice entries that match us exactly, this is + // useful for stuff like reporting call stacks + let skipFrame = false; + if (start !== undefined && end !== undefined) { + adviceLoop: for (const item of advice) { + if ( + item.type === 'frame' && + item.location.filename === filename && + equalPosition(item.location.start, start) && + equalPosition(item.location.end, end) + ) { + skipFrame = true; + break; + } + + if (item.type === 'stacktrace') { + for (const frame of item.frames) { + if (frame.filename === filename && equalPosition(frame, start)) { + skipFrame = true; + break adviceLoop; + } + } + } + } + } + + const outdatedAdvice: DiagnosticAdvice = []; + const outdatedFiles = this.getOutdatedFiles(diag); + const isOutdated = outdatedFiles.size > 0; + if (isOutdated) { + const outdatedFilesArr = Array.from(outdatedFiles, (path) => path.join()); + + if (outdatedFilesArr.length === 1 && outdatedFilesArr[0] === filename) { + outdatedAdvice.push({ + type: 'log', + category: 'warn', + text: 'This file has been changed since the diagnostic was produced and may be out of date', + }); + } else { + outdatedAdvice.push({ + type: 'log', + category: 'warn', + text: 'This diagnostic may be out of date as it relies on the following files that have been changed since the diagnostic was generated', + }); + + outdatedAdvice.push({ + type: 'list', + list: outdatedFilesArr.map((filename) => + markup`` + ), + }); + } + } + + const derived = deriveRootAdviceFromDiagnostic( + diag, + { + skipFrame, + includeHeaderInAdvice: false, + outdated: isOutdated, + }, + ); + + reporter.hr(derived.header); + + reporter.indent(() => { + // Concat all the advice together + const allAdvice: DiagnosticAdvice = [ + ...derived.advice, + ...outdatedAdvice, + ...advice, + ]; + + // Print advice + for (const item of allAdvice) { + const res = printAdvice( + item, + { + printer: this, + flags: this.flags, + missingFileSources: this.missingFileSources, + fileSources: this.fileSources, + diagnostic: diag, + reporter, + }, + ); + if (res.printed) { + reporter.br(); + } + if (res.truncated) { + this.hasTruncatedDiagnostics = true; + } + } + + // Print verbose information + if (this.flags.verboseDiagnostics) { + const {origins} = diag; + + if (origins !== undefined && origins.length > 0) { + reporter.br(); + reporter.info('Why are you seeing this diagnostic?'); + reporter.br(); + reporter.list( + origins.map((origin) => { + let res = `${origin.category}`; + if (origin.message !== undefined) { + res += `: ${origin.message}`; + } + return res; + }), + {ordered: true}, + ); + } + } + }); + } + + filterDiagnostics(): Diagnostics { + const diagnostics = this.getDiagnostics(); + const filteredDiagnostics: Diagnostics = []; + + for (const diag of diagnostics) { + this.problemCount++; + + if (this.shouldIgnore(diag)) { + this.filteredCount++; + } else if (this.shouldTruncate()) { + this.truncatedCount++; + } else { + this.displayedCount++; + filteredDiagnostics.push(diag); + } + } + + return filteredDiagnostics; + } + + onFooterPrint(fn: FooterPrintCallback) { + this.onFooterPrintCallbacks.push(fn); + } + + footer() { + const {reporter, problemCount} = this; + + const isError = problemCount > 0; + + if (isError) { + const restoreRedirect = reporter.redirectOutToErr(true); + reporter.hr(); + reporter.redirectOutToErr(restoreRedirect); + } + + if (this.hasTruncatedDiagnostics) { + reporter.warn( + 'Some diagnostics have been truncated. Use the --verbose-diagnostics flag to disable truncation.', + ); + } + + if (isError) { + if (this.flags.fieri) { + this.showBanner(errorBanner); + } + } else { + if (this.flags.fieri) { + this.showBanner(successBanner); + } + } + + for (const handler of this.onFooterPrintCallbacks) { + const stop = handler(reporter, isError); + if (stop) { + return; + } + } + + if (isError) { + this.footerError(); + } else { + reporter.success('No known problems!'); + } + } + + showBanner(banner: Banner) { + for (const stream of this.reporter.getStreams(false)) { + for (const row of banner.rows) { + for (const field of row) { + let palleteIndex; + let times = 1; + if (Array.isArray(field)) { + [palleteIndex, times] = field; + } else { + palleteIndex = field; + } + + const pallete = banner.palettes[palleteIndex]; + stream.write( + formatAnsi.bgRgb( + ' ', + { + r: pallete[0], + g: pallete[1], + b: pallete[2], + }, + ).repeat(times), + ); + } + stream.write('\n'); + } + } + } + + footerError() { + const {reporter, filteredCount} = this; + + const displayableProblems = this.getDisplayedProblemsCount(); + let str = `Found ${displayableProblems} ${displayableProblems}`; + + if (filteredCount > 0) { + str += ` (${filteredCount} filtered)`; + } + + reporter.error(str); + + if (this.truncatedCount > 0) { + const {maxDiagnostics} = this.flags; + reporter.warn( + `Only ${maxDiagnostics} errors shown, add the --show-all-diagnostics flag to view the remaining ${displayableProblems - + maxDiagnostics} errors`, + ); + } + } } diff --git a/packages/@romejs/cli-diagnostics/buildMessageCodeFrame.ts b/packages/@romejs/cli-diagnostics/buildMessageCodeFrame.ts index 2b21354fab3..00aa386d039 100644 --- a/packages/@romejs/cli-diagnostics/buildMessageCodeFrame.ts +++ b/packages/@romejs/cli-diagnostics/buildMessageCodeFrame.ts @@ -6,283 +6,278 @@ */ import { - CODE_FRAME_CONTEXT_LINES, - CODE_FRAME_INDENT, - CODE_FRAME_SELECTED_INDENT, - GUTTER, - HALF_MAX_CODE_FRAME_LINES, - MAX_CODE_FRAME_LINES, + CODE_FRAME_CONTEXT_LINES, + CODE_FRAME_INDENT, + CODE_FRAME_SELECTED_INDENT, + GUTTER, + HALF_MAX_CODE_FRAME_LINES, + MAX_CODE_FRAME_LINES, } from './constants'; import {Position} from '@romejs/parser-core'; import { - ToLines, - cleanEquivalentString, - joinNoBreak, - normalizeTabs, + ToLines, + cleanEquivalentString, + joinNoBreak, + normalizeTabs, } from './utils'; import { - Number0, - ob1Coerce0, - ob1Coerce0To1, - ob1Coerce1To0, - ob1Get0, - ob1Inc, - ob1Number0, - ob1Number1Neg1, - ob1Sub, + Number0, + ob1Coerce0, + ob1Coerce0To1, + ob1Coerce1To0, + ob1Get0, + ob1Inc, + ob1Number0, + ob1Number1Neg1, + ob1Sub, } from '@romejs/ob1'; import {markupToPlainTextString} from '@romejs/string-markup'; function createPointer( - markerMessage: string, - line: string, - markerStart: Number0, - markerEnd: Number0, + markerMessage: string, + line: string, + markerStart: Number0, + markerEnd: Number0, ): undefined | string { - let result = ''; - - let markerSize = ob1Get0(ob1Sub(markerEnd, markerStart)); - - // If the range contains tabs then increase the marker size - for (let i = ob1Get0(markerStart); i < ob1Get0(markerEnd); i++) { - const char = line[i]; - if (char === '\t') { - markerSize++; - } - } - - const pointerLength: number = Math.max(markerSize, 1); - - // Skip the pointer if it's pointing at the last character - let skipPointer = pointerLength === 1 && ob1Get0(markerEnd) >= line.length; - - if (!skipPointer) { - // Add indentation, handling hard tabs as two soft spaces - for (let i = 0; i < ob1Get0(markerStart); i++) { - const char = line[i]; - if (char === '\t') { - // normalizeTabs will be called on this line and this replacement made - result += ' '; - } else { - result += ' '; - } - } - - // Add pointer - result += `${'^'.repeat(pointerLength)}`; - } - - // Add marker - if (markerMessage !== '') { - result += ` ${markerMessage}`; - } - - if (result === '') { - return undefined; - } else { - return result; - } + let result = ''; + + let markerSize = ob1Get0(ob1Sub(markerEnd, markerStart)); + + // If the range contains tabs then increase the marker size + for (let i = ob1Get0(markerStart); i < ob1Get0(markerEnd); i++) { + const char = line[i]; + if (char === '\t') { + markerSize++; + } + } + + const pointerLength: number = Math.max(markerSize, 1); + + // Skip the pointer if it's pointing at the last character + let skipPointer = pointerLength === 1 && ob1Get0(markerEnd) >= line.length; + + if (!skipPointer) { + // Add indentation, handling hard tabs as two soft spaces + for (let i = 0; i < ob1Get0(markerStart); i++) { + const char = line[i]; + if (char === '\t') { + // normalizeTabs will be called on this line and this replacement made + result += ' '; + } else { + result += ' '; + } + } + + // Add pointer + result += `${'^'.repeat(pointerLength)}`; + } + + // Add marker + if (markerMessage !== '') { + result += ` ${markerMessage}`; + } + + if (result === '') { + return undefined; + } else { + return result; + } } export default function buildMessageCodeFrame( - sourceText: string, - allLines: ToLines, - start: undefined | Position, - end: undefined | Position, - markerMessage: string, + sourceText: string, + allLines: ToLines, + start: undefined | Position, + end: undefined | Position, + markerMessage: string, ): string { - if (allLines.length === 0 || start === undefined || end === undefined) { - if (markerMessage === '') { - return ''; - } else { - return `${markerMessage}`; - } - } - - const startLineIndex = ob1Coerce1To0(start.line); - let endLineIndex = ob1Coerce1To0(end.line); - - // Increase the amount of lines we should show for "context" - let contextStartIndex = ob1Coerce0( - Math.max(0, ob1Get0(startLineIndex) - CODE_FRAME_CONTEXT_LINES), - ); - let contextEndIndex = ob1Coerce0( - Math.min( - allLines.length - 1, - ob1Get0(endLineIndex) + CODE_FRAME_CONTEXT_LINES, - ), - ); - - let maxVisibleLineNo = 0; - - let formattedLines: Array< - | { - pointer: undefined | string; - gutter: string; - line: string; - } - | undefined - > = []; - for (let i = contextStartIndex; i <= contextEndIndex; i = ob1Inc(i)) { - let rawLine: undefined | string = allLines.raw[ob1Get0(i)]; - let highlightLine: undefined | string = allLines.highlighted[ob1Get0(i)]; - if (highlightLine === undefined || rawLine === undefined) { - continue; - } - - // Ensure that the frame doesn't start with whitespace - if ( - rawLine.trim() === '' && - formattedLines.length === 0 && - i !== startLineIndex - ) { - continue; - } - - let pointer: undefined | string; - - // If this is within the highlighted line range - const shouldHighlight: boolean = i >= startLineIndex && i <= endLineIndex; - - if (shouldHighlight) { - if (i === startLineIndex && i === endLineIndex) { - // Only line in the selection - pointer = createPointer( - markerMessage, - rawLine, - start.column, - end.column, - ); - } else if (i === startLineIndex) { - // First line in selection - pointer = createPointer( - '', - rawLine, - start.column, - // line could be highlighted - ob1Coerce0(rawLine.length), - ); - } else if (i === endLineIndex) { - // Last line in selection - pointer = createPointer(markerMessage, rawLine, ob1Number0, end.column); - } - } - - // Replace hard tabs with two spaces - highlightLine = normalizeTabs(highlightLine); - - const lineNo = ob1Coerce0To1(i); - let gutter = `${String(lineNo)}${GUTTER}`; - - if (shouldHighlight) { - gutter = `${CODE_FRAME_SELECTED_INDENT}${gutter}`; - } else { - gutter = `${CODE_FRAME_INDENT}${gutter}`; - } - - formattedLines.push({ - pointer, - gutter, - line: highlightLine, - }); - - maxVisibleLineNo = ob1Get0(i) + 1; - } - - // If we have too many lines in our selection, then collapse them to an ellipsis - const pruned = formattedLines.length > MAX_CODE_FRAME_LINES + 2; - if (pruned) { - const start = formattedLines.slice(0, HALF_MAX_CODE_FRAME_LINES); - const end = formattedLines.slice(-HALF_MAX_CODE_FRAME_LINES); - formattedLines = start.concat([undefined], end); - } - - // Remove trailing blank lines - for (let i = formattedLines.length - 1; i >= 0; i--) { - const info = formattedLines[i]; - if (info !== undefined && info.line === '') { - formattedLines.pop(); - } else { - break; - } - } - - // If there's no lines to target then return the normal marker - if ( - formattedLines.length === 0 || - end.line === ob1Number1Neg1 || - start.line === ob1Number1Neg1 - ) { - if (markerMessage === '') { - return ''; - } else { - return `${markerMessage}`; - } - } - - // Calculate max size of gutter, this is the maximum visible line plus the futter length plus the frame indent - const lastLine = formattedLines[formattedLines.length - 1]; - if (lastLine === undefined) { - throw new Error('Expected there to be a last line'); - } - - const maxGutterLength = - String(maxVisibleLineNo).length + GUTTER.length + CODE_FRAME_INDENT.length; - - // If what the marker is highlighting equals the marker message then it's redundant so don't show the message - if (markerMessage !== '') { - const text = sourceText.slice(ob1Get0(start.index), ob1Get0(end.index)); - if ( - cleanEquivalentString(text) === - cleanEquivalentString(markupToPlainTextString(markerMessage)) - ) { - markerMessage = ''; - } - } - - // Output no gutter with a soft indent if this is true - if (formattedLines.length === 1) { - const selection = formattedLines[0]; - if (selection === undefined) { - throw new Error( - 'Expected a selection? undefined is only valid here as an omitted line signifier', - ); - } - - const result = [`${CODE_FRAME_INDENT}${selection.line}`]; - if (selection.pointer !== undefined) { - result.push(`${CODE_FRAME_INDENT}${selection.pointer}`); - } - - return joinNoBreak(result); - } - - // Build up the line we display when source lines are omitted - const omittedLine = - `...` + - GUTTER; - - // Build the frame - const result = []; - for (const selection of formattedLines) { - if (!selection) { - result.push(omittedLine); - continue; - } - - const {pointer, gutter, line} = selection; - - result.push( - `${gutter}` + - line, - ); - - if (pointer !== undefined) { - result.push( - `${GUTTER}${pointer}`, - ); - } - } - - return joinNoBreak(result); + if (allLines.length === 0 || start === undefined || end === undefined) { + if (markerMessage === '') { + return ''; + } else { + return `${markerMessage}`; + } + } + + const startLineIndex = ob1Coerce1To0(start.line); + let endLineIndex = ob1Coerce1To0(end.line); + + // Increase the amount of lines we should show for "context" + let contextStartIndex = ob1Coerce0( + Math.max(0, ob1Get0(startLineIndex) - CODE_FRAME_CONTEXT_LINES), + ); + let contextEndIndex = ob1Coerce0( + Math.min( + allLines.length - 1, + ob1Get0(endLineIndex) + CODE_FRAME_CONTEXT_LINES, + ), + ); + + let maxVisibleLineNo = 0; + + let formattedLines: Array< + | { + pointer: undefined | string; + gutter: string; + line: string; + } + | undefined + > = []; + for (let i = contextStartIndex; i <= contextEndIndex; i = ob1Inc(i)) { + let rawLine: undefined | string = allLines.raw[ob1Get0(i)]; + let highlightLine: undefined | string = allLines.highlighted[ob1Get0(i)]; + if (highlightLine === undefined || rawLine === undefined) { + continue; + } + + // Ensure that the frame doesn't start with whitespace + if ( + rawLine.trim() === '' && + formattedLines.length === 0 && + i !== startLineIndex + ) { + continue; + } + + let pointer: undefined | string; + + // If this is within the highlighted line range + const shouldHighlight: boolean = i >= startLineIndex && i <= endLineIndex; + + if (shouldHighlight) { + if (i === startLineIndex && i === endLineIndex) { + // Only line in the selection + pointer = createPointer(markerMessage, rawLine, start.column, end.column); + } else if (i === startLineIndex) { + // First line in selection + pointer = createPointer( + '', + rawLine, + start.column, + // line could be highlighted + ob1Coerce0(rawLine.length), + ); + } else if (i === endLineIndex) { + // Last line in selection + pointer = createPointer(markerMessage, rawLine, ob1Number0, end.column); + } + } + + // Replace hard tabs with two spaces + highlightLine = normalizeTabs(highlightLine); + + const lineNo = ob1Coerce0To1(i); + let gutter = `${String(lineNo)}${GUTTER}`; + + if (shouldHighlight) { + gutter = `${CODE_FRAME_SELECTED_INDENT}${gutter}`; + } else { + gutter = `${CODE_FRAME_INDENT}${gutter}`; + } + + formattedLines.push({ + pointer, + gutter, + line: highlightLine, + }); + + maxVisibleLineNo = ob1Get0(i) + 1; + } + + // If we have too many lines in our selection, then collapse them to an ellipsis + const pruned = formattedLines.length > MAX_CODE_FRAME_LINES + 2; + if (pruned) { + const start = formattedLines.slice(0, HALF_MAX_CODE_FRAME_LINES); + const end = formattedLines.slice(-HALF_MAX_CODE_FRAME_LINES); + formattedLines = start.concat([undefined], end); + } + + // Remove trailing blank lines + for (let i = formattedLines.length - 1; i >= 0; i--) { + const info = formattedLines[i]; + if (info !== undefined && info.line === '') { + formattedLines.pop(); + } else { + break; + } + } + + // If there's no lines to target then return the normal marker + if ( + formattedLines.length === 0 || + end.line === ob1Number1Neg1 || + start.line === ob1Number1Neg1 + ) { + if (markerMessage === '') { + return ''; + } else { + return `${markerMessage}`; + } + } + + // Calculate max size of gutter, this is the maximum visible line plus the futter length plus the frame indent + const lastLine = formattedLines[formattedLines.length - 1]; + if (lastLine === undefined) { + throw new Error('Expected there to be a last line'); + } + + const maxGutterLength = + String(maxVisibleLineNo).length + GUTTER.length + CODE_FRAME_INDENT.length; + + // If what the marker is highlighting equals the marker message then it's redundant so don't show the message + if (markerMessage !== '') { + const text = sourceText.slice(ob1Get0(start.index), ob1Get0(end.index)); + if ( + cleanEquivalentString(text) === + cleanEquivalentString(markupToPlainTextString(markerMessage)) + ) { + markerMessage = ''; + } + } + + // Output no gutter with a soft indent if this is true + if (formattedLines.length === 1) { + const selection = formattedLines[0]; + if (selection === undefined) { + throw new Error( + 'Expected a selection? undefined is only valid here as an omitted line signifier', + ); + } + + const result = [`${CODE_FRAME_INDENT}${selection.line}`]; + if (selection.pointer !== undefined) { + result.push(`${CODE_FRAME_INDENT}${selection.pointer}`); + } + + return joinNoBreak(result); + } + + // Build up the line we display when source lines are omitted + const omittedLine = + `...` + + GUTTER; + + // Build the frame + const result = []; + for (const selection of formattedLines) { + if (!selection) { + result.push(omittedLine); + continue; + } + + const {pointer, gutter, line} = selection; + + result.push( + `${gutter}` + + line, + ); + + if (pointer !== undefined) { + result.push( + `${GUTTER}${pointer}`, + ); + } + } + + return joinNoBreak(result); } diff --git a/packages/@romejs/cli-diagnostics/buildPatchCodeFrame.ts b/packages/@romejs/cli-diagnostics/buildPatchCodeFrame.ts index b98bf7f9ec4..5b3181cf9bb 100644 --- a/packages/@romejs/cli-diagnostics/buildPatchCodeFrame.ts +++ b/packages/@romejs/cli-diagnostics/buildPatchCodeFrame.ts @@ -6,10 +6,10 @@ */ import { - CODE_FRAME_CONTEXT_LINES, - CODE_FRAME_INDENT, - GUTTER, - MAX_PATCH_LINES, + CODE_FRAME_CONTEXT_LINES, + CODE_FRAME_INDENT, + GUTTER, + MAX_PATCH_LINES, } from './constants'; import {joinNoBreak, normalizeTabs, showInvisibles} from './utils'; import {Diffs, diffConstants, groupDiffByLines} from '@romejs/string-diff'; @@ -17,168 +17,168 @@ import {escapeMarkup, markup, markupTag} from '@romejs/string-markup'; import {DiagnosticAdviceDiff} from '@romejs/diagnostics'; function formatDiffLine(diffs: Diffs) { - return diffs.map(([type, text]) => { - if (type === diffConstants.DELETE) { - return markupTag('error', escapeMarkup(showInvisibles(text))); - } else if (type === diffConstants.ADD) { - return markupTag('success', escapeMarkup(showInvisibles(text))); - } else { - // type === diffConstants.EQUAL - return escapeMarkup(normalizeTabs(text)); - } - }).join(''); + return diffs.map(([type, text]) => { + if (type === diffConstants.DELETE) { + return markupTag('error', escapeMarkup(showInvisibles(text))); + } else if (type === diffConstants.ADD) { + return markupTag('success', escapeMarkup(showInvisibles(text))); + } else { + // type === diffConstants.EQUAL + return escapeMarkup(normalizeTabs(text)); + } + }).join(''); } const DELETE_MARKER = markupTag('error', '-'); const ADD_MARKER = markupTag('success', '+'); function formatSingleLineMarker(text: string): string { - return markup`${text}:`; + return markup`${text}:`; } export default function buildPatchCodeFrame( - item: DiagnosticAdviceDiff, - verbose: boolean, + item: DiagnosticAdviceDiff, + verbose: boolean, ): { - truncated: boolean; - frame: string; + truncated: boolean; + frame: string; } { - const diffsByLine = groupDiffByLines(item.diff); - let lastVisibleLine = -1; - - // Calculate the parts of the diff we should show - const shownLines: Set = new Set(); - for (let i = 0; i < diffsByLine.length; i++) { - const diffs = diffsByLine[i]; - - let hasChange = false; - for (const [type] of diffs) { - if (type === diffConstants.DELETE || type === diffConstants.ADD) { - hasChange = true; - break; - } - } - - if (hasChange) { - for ( - let start = i - CODE_FRAME_CONTEXT_LINES; - start < i + CODE_FRAME_CONTEXT_LINES; - start++ - ) { - shownLines.add(start); - - if (start > lastVisibleLine) { - lastVisibleLine = start; - } - } - } - } - - const lineLength = String(lastVisibleLine).length; - - // Don't output a gutter if there's only a single line - const singleLine = diffsByLine.length === 1; - - const {legend} = item; - const frame = []; - let displayedLines = 0; - let truncated = false; - let lastDisplayedLine = -1; - - const skippedLine = `${CODE_FRAME_INDENT}${'.'.repeat(lineLength)}${GUTTER}`; - - // Build the actual frame - for (let i = 0; i < diffsByLine.length; i++) { - if (shownLines.has(i) === false) { - continue; - } - - displayedLines++; - - if (!verbose && displayedLines > MAX_PATCH_LINES) { - truncated = true; - continue; - } - - const diffs = diffsByLine[i]; - const lineNo = i + 1; - - const deletions: Diffs = []; - const addition: Diffs = []; - - let hasDeletions = false; - let hasAddition = false; - - for (const tuple of diffs) { - let [type] = tuple; - - if (type === diffConstants.DELETE) { - hasDeletions = true; - deletions.push(tuple); - } - - if (type === diffConstants.ADD) { - hasAddition = true; - addition.push(tuple); - } - - if (type === diffConstants.EQUAL) { - addition.push(tuple); - deletions.push(tuple); - } - } - - if (lastDisplayedLine !== lineNo - 1 && lastDisplayedLine !== -1) { - frame.push(skippedLine); - } - - let gutterWithLine = ''; - let gutterNoLine = ''; - let deleteMarker = DELETE_MARKER; - let addMarker = ADD_MARKER; - - if (!singleLine) { - gutterWithLine = `${CODE_FRAME_INDENT}${lineNo}${GUTTER}`; - gutterNoLine = `${CODE_FRAME_INDENT}${' '.repeat(lineLength)}${GUTTER}`; - } - - if (singleLine && legend !== undefined) { - addMarker = formatSingleLineMarker(legend.add); - deleteMarker = formatSingleLineMarker(legend.delete); - } - - if (hasDeletions) { - const gutter = hasAddition ? gutterNoLine : gutterWithLine; - frame.push(`${gutter}${deleteMarker} ${formatDiffLine(deletions)}`); - } - - if (hasAddition) { - frame.push(`${gutterWithLine}${addMarker} ${formatDiffLine(addition)}`); - } - - if (!hasAddition && !hasDeletions) { - // Output one of the lines, they're the same - frame.push(`${gutterWithLine} ${formatDiffLine(addition)}`); - } - - lastDisplayedLine = lineNo; - } - - if (truncated) { - frame.push( - `${skippedLine} ${displayedLines - MAX_PATCH_LINES} more lines truncated`, - ); - } - - if (legend !== undefined && !singleLine) { - frame.push(''); - frame.push(`- ${escapeMarkup(legend.delete)}`); - frame.push(`+ ${escapeMarkup(legend.add)}`); - frame.push(''); - } - - return { - truncated, - frame: joinNoBreak(frame), - }; + const diffsByLine = groupDiffByLines(item.diff); + let lastVisibleLine = -1; + + // Calculate the parts of the diff we should show + const shownLines: Set = new Set(); + for (let i = 0; i < diffsByLine.length; i++) { + const diffs = diffsByLine[i]; + + let hasChange = false; + for (const [type] of diffs) { + if (type === diffConstants.DELETE || type === diffConstants.ADD) { + hasChange = true; + break; + } + } + + if (hasChange) { + for ( + let start = i - CODE_FRAME_CONTEXT_LINES; + start < i + CODE_FRAME_CONTEXT_LINES; + start++ + ) { + shownLines.add(start); + + if (start > lastVisibleLine) { + lastVisibleLine = start; + } + } + } + } + + const lineLength = String(lastVisibleLine).length; + + // Don't output a gutter if there's only a single line + const singleLine = diffsByLine.length === 1; + + const {legend} = item; + const frame = []; + let displayedLines = 0; + let truncated = false; + let lastDisplayedLine = -1; + + const skippedLine = `${CODE_FRAME_INDENT}${'.'.repeat(lineLength)}${GUTTER}`; + + // Build the actual frame + for (let i = 0; i < diffsByLine.length; i++) { + if (shownLines.has(i) === false) { + continue; + } + + displayedLines++; + + if (!verbose && displayedLines > MAX_PATCH_LINES) { + truncated = true; + continue; + } + + const diffs = diffsByLine[i]; + const lineNo = i + 1; + + const deletions: Diffs = []; + const addition: Diffs = []; + + let hasDeletions = false; + let hasAddition = false; + + for (const tuple of diffs) { + let [type] = tuple; + + if (type === diffConstants.DELETE) { + hasDeletions = true; + deletions.push(tuple); + } + + if (type === diffConstants.ADD) { + hasAddition = true; + addition.push(tuple); + } + + if (type === diffConstants.EQUAL) { + addition.push(tuple); + deletions.push(tuple); + } + } + + if (lastDisplayedLine !== lineNo - 1 && lastDisplayedLine !== -1) { + frame.push(skippedLine); + } + + let gutterWithLine = ''; + let gutterNoLine = ''; + let deleteMarker = DELETE_MARKER; + let addMarker = ADD_MARKER; + + if (!singleLine) { + gutterWithLine = `${CODE_FRAME_INDENT}${lineNo}${GUTTER}`; + gutterNoLine = `${CODE_FRAME_INDENT}${' '.repeat(lineLength)}${GUTTER}`; + } + + if (singleLine && legend !== undefined) { + addMarker = formatSingleLineMarker(legend.add); + deleteMarker = formatSingleLineMarker(legend.delete); + } + + if (hasDeletions) { + const gutter = hasAddition ? gutterNoLine : gutterWithLine; + frame.push(`${gutter}${deleteMarker} ${formatDiffLine(deletions)}`); + } + + if (hasAddition) { + frame.push(`${gutterWithLine}${addMarker} ${formatDiffLine(addition)}`); + } + + if (!hasAddition && !hasDeletions) { + // Output one of the lines, they're the same + frame.push(`${gutterWithLine} ${formatDiffLine(addition)}`); + } + + lastDisplayedLine = lineNo; + } + + if (truncated) { + frame.push( + `${skippedLine} ${displayedLines - MAX_PATCH_LINES} more lines truncated`, + ); + } + + if (legend !== undefined && !singleLine) { + frame.push(''); + frame.push(`- ${escapeMarkup(legend.delete)}`); + frame.push(`+ ${escapeMarkup(legend.add)}`); + frame.push(''); + } + + return { + truncated, + frame: joinNoBreak(frame), + }; } diff --git a/packages/@romejs/cli-diagnostics/highlightCode.ts b/packages/@romejs/cli-diagnostics/highlightCode.ts index f6985d7218b..fee6adcffb7 100644 --- a/packages/@romejs/cli-diagnostics/highlightCode.ts +++ b/packages/@romejs/cli-diagnostics/highlightCode.ts @@ -17,253 +17,253 @@ import {escapeMarkup, markupTag} from '@romejs/string-markup'; const FILE_SIZE_MAX = 100_000; export type AnsiHighlightOptions = { - path: UnknownFilePath; - input: string; - sourceType: undefined | DiagnosticSourceType; - language: undefined | DiagnosticLanguage; + path: UnknownFilePath; + input: string; + sourceType: undefined | DiagnosticSourceType; + language: undefined | DiagnosticLanguage; }; export default function highlightCode(opts: AnsiHighlightOptions): string { - if (opts.input.length > FILE_SIZE_MAX) { - return escapeMarkup(opts.input); - } - - if (opts.language === 'js') { - // js-parser does not accept an "unknown" sourceType - return highlightJS( - opts.input, - opts.sourceType === undefined || opts.sourceType === 'unknown' - ? 'script' - : opts.sourceType, - ); - } - - if (opts.language === 'json') { - return highlightJSON(opts.path, opts.input); - } - - return escapeMarkup(opts.input); + if (opts.input.length > FILE_SIZE_MAX) { + return escapeMarkup(opts.input); + } + + if (opts.language === 'js') { + // js-parser does not accept an "unknown" sourceType + return highlightJS( + opts.input, + opts.sourceType === undefined || opts.sourceType === 'unknown' + ? 'script' + : opts.sourceType, + ); + } + + if (opts.language === 'json') { + return highlightJSON(opts.path, opts.input); + } + + return escapeMarkup(opts.input); } function reduce( - input: string, - tokens: Array, - callback: (token: Token, line: string) => string, + input: string, + tokens: Array, + callback: (token: Token, line: string) => string, ): string { - let prevEnd = 0; - let buff = ''; + let prevEnd = 0; + let buff = ''; - for (const token of tokens) { - const start = ob1Get0(token.start); - const end = ob1Get0(token.end); - let value = input.slice(start, end); + for (const token of tokens) { + const start = ob1Get0(token.start); + const end = ob1Get0(token.end); + let value = input.slice(start, end); - // Add on text between tokens - buff += escapeMarkup(input.slice(prevEnd, start)); - prevEnd = end; + // Add on text between tokens + buff += escapeMarkup(input.slice(prevEnd, start)); + prevEnd = end; - // We need to break up the token text into lines, so that we can easily split the highlighted newlines and have the ansi codes be unbroken - const lines = value.split('\n'); + // We need to break up the token text into lines, so that we can easily split the highlighted newlines and have the ansi codes be unbroken + const lines = value.split('\n'); - const values: Array = lines.map((line) => { - return callback(token, escapeMarkup(line)); - }); + const values: Array = lines.map((line) => { + return callback(token, escapeMarkup(line)); + }); - buff += values.join('\n'); - } + buff += values.join('\n'); + } - return buff; + return buff; } function invalidHighlight(line: string): string { - return markupTag('emphasis', markupTag('color', line, {bg: 'red'})); + return markupTag('emphasis', markupTag('color', line, {bg: 'red'})); } function highlightJSON(path: UnknownFilePath, input: string): string { - const tokens = tokenizeJSON({ - input, - // Wont be used anywhere but activates JSON extensions if necessary - path, - }); - - return reduce( - input, - tokens, - (token, value) => { - // Try to keep the highlighting in line with JS where possible - switch (token.type) { - case 'BlockComment': - case 'LineComment': - return markupTag('color', value, {fg: 'brightBlack'}); - - case 'String': - return markupTag('color', value, {fg: 'green'}); - - case 'Number': - return markupTag('color', value, {fg: 'magenta'}); - - case 'Word': - switch (token.value) { - case 'true': - case 'false': - case 'null': - return markupTag('color', value, {fg: 'cyan'}); - - default: - return value; - } - - case 'Comma': - case 'Colon': - case 'Dot': - return markupTag('color', value, {fg: 'yellow'}); - - case 'BracketOpen': - case 'BracketClose': - case 'BraceOpen': - case 'BraceClose': - case 'Minus': - case 'Plus': - return value; - - case 'Invalid': - return invalidHighlight(value); - - // Will never be hit - case 'EOF': - case 'SOF': - return ''; - } - }, - ); + const tokens = tokenizeJSON({ + input, + // Wont be used anywhere but activates JSON extensions if necessary + path, + }); + + return reduce( + input, + tokens, + (token, value) => { + // Try to keep the highlighting in line with JS where possible + switch (token.type) { + case 'BlockComment': + case 'LineComment': + return markupTag('color', value, {fg: 'brightBlack'}); + + case 'String': + return markupTag('color', value, {fg: 'green'}); + + case 'Number': + return markupTag('color', value, {fg: 'magenta'}); + + case 'Word': + switch (token.value) { + case 'true': + case 'false': + case 'null': + return markupTag('color', value, {fg: 'cyan'}); + + default: + return value; + } + + case 'Comma': + case 'Colon': + case 'Dot': + return markupTag('color', value, {fg: 'yellow'}); + + case 'BracketOpen': + case 'BracketClose': + case 'BraceOpen': + case 'BraceClose': + case 'Minus': + case 'Plus': + return value; + + case 'Invalid': + return invalidHighlight(value); + + // Will never be hit + case 'EOF': + case 'SOF': + return ''; + } + }, + ); } function highlightJS(input: string, sourceType: ConstSourceType): string { - const tokens = tokenizeJS( - input, - { - sourceType, - // js-parser requires a filename. Doesn't really matter since we'll never be producing an AST or diagnostics - path: createUnknownFilePath('unknown'), - }, - ); - - return reduce( - input, - tokens, - (token, value) => { - const {type} = token; - - switch (type.label) { - case 'break': - case 'case': - case 'catch': - case 'continue': - case 'debugger': - case 'default': - case 'do': - case 'else': - case 'finally': - case 'for': - case 'function': - case 'if': - case 'return': - case 'switch': - case 'throw': - case 'try': - case 'var': - case 'const': - case 'while': - case 'with': - case 'new': - case 'this': - case 'super': - case 'class': - case 'extends': - case 'export': - case 'import': - case 'null': - case 'true': - case 'false': - case 'in': - case 'instanceof': - case 'typeof': - case 'void': - case 'delete': - return markupTag('color', value, {fg: 'cyan'}); - - case 'num': - case 'bigint': - return markupTag('color', value, {fg: 'magenta'}); - - case 'regexp': - return markupTag('color', value, {fg: 'magenta'}); - - case 'string': - case 'template': - case '`': - return markupTag('color', value, {fg: 'green'}); - - case 'invalid': - return invalidHighlight(value); - - case 'comment': - return markupTag('color', value, {fg: 'brightBlack'}); - - case ',': - case ';': - case ':': - case '::': - case '${': - case '.': - case '?': - case '?.': - return markupTag('color', value, {fg: 'yellow'}); - - case '[': - case ']': - case '{': - case '{|': - case '}': - case '|}': - case '(': - case ')': - return value; - - case '=>': - case '...': - case '@': - case '#': - case '=': - case '_=': - case '++/--': - case '!': - case '~': - case '??': - case '||': - case '&&': - case '|': - case '^': - case '&': - case '==/!=': - case '': - case '<>': - case '+/-': - case '%': - case '*': - case '/': - case '**': - case 'jsxName': - case 'jsxText': - case 'jsxTagStart': - case 'jsxTagEnd': - case 'name': - case 'eof': - return value; - } - }, - ); + const tokens = tokenizeJS( + input, + { + sourceType, + // js-parser requires a filename. Doesn't really matter since we'll never be producing an AST or diagnostics + path: createUnknownFilePath('unknown'), + }, + ); + + return reduce( + input, + tokens, + (token, value) => { + const {type} = token; + + switch (type.label) { + case 'break': + case 'case': + case 'catch': + case 'continue': + case 'debugger': + case 'default': + case 'do': + case 'else': + case 'finally': + case 'for': + case 'function': + case 'if': + case 'return': + case 'switch': + case 'throw': + case 'try': + case 'var': + case 'const': + case 'while': + case 'with': + case 'new': + case 'this': + case 'super': + case 'class': + case 'extends': + case 'export': + case 'import': + case 'null': + case 'true': + case 'false': + case 'in': + case 'instanceof': + case 'typeof': + case 'void': + case 'delete': + return markupTag('color', value, {fg: 'cyan'}); + + case 'num': + case 'bigint': + return markupTag('color', value, {fg: 'magenta'}); + + case 'regexp': + return markupTag('color', value, {fg: 'magenta'}); + + case 'string': + case 'template': + case '`': + return markupTag('color', value, {fg: 'green'}); + + case 'invalid': + return invalidHighlight(value); + + case 'comment': + return markupTag('color', value, {fg: 'brightBlack'}); + + case ',': + case ';': + case ':': + case '::': + case '${': + case '.': + case '?': + case '?.': + return markupTag('color', value, {fg: 'yellow'}); + + case '[': + case ']': + case '{': + case '{|': + case '}': + case '|}': + case '(': + case ')': + return value; + + case '=>': + case '...': + case '@': + case '#': + case '=': + case '_=': + case '++/--': + case '!': + case '~': + case '??': + case '||': + case '&&': + case '|': + case '^': + case '&': + case '==/!=': + case '': + case '<>': + case '+/-': + case '%': + case '*': + case '/': + case '**': + case 'jsxName': + case 'jsxText': + case 'jsxTagStart': + case 'jsxTagEnd': + case 'name': + case 'eof': + return value; + } + }, + ); } diff --git a/packages/@romejs/cli-diagnostics/index.ts b/packages/@romejs/cli-diagnostics/index.ts index a28351427b8..149a072ec5b 100644 --- a/packages/@romejs/cli-diagnostics/index.ts +++ b/packages/@romejs/cli-diagnostics/index.ts @@ -12,8 +12,8 @@ import DiagnosticsPrinter from './DiagnosticsPrinter'; export {toLines} from './utils'; export { - DEFAULT_PRINTER_FLAGS, - readDiagnosticsFileLocal, + DEFAULT_PRINTER_FLAGS, + readDiagnosticsFileLocal, } from './DiagnosticsPrinter'; export {DiagnosticsPrinter}; @@ -22,45 +22,45 @@ export * from './constants'; export * from './types'; export function printDiagnostics( - { - diagnostics, - suppressions, - printerOptions, - excludeFooter, - }: { - diagnostics: Diagnostics; - suppressions: DiagnosticSuppressions; - printerOptions: DiagnosticsPrinterOptions; - excludeFooter?: boolean; - }, + { + diagnostics, + suppressions, + printerOptions, + excludeFooter, + }: { + diagnostics: Diagnostics; + suppressions: DiagnosticSuppressions; + printerOptions: DiagnosticsPrinterOptions; + excludeFooter?: boolean; + }, ): DiagnosticsPrinter { - const printer = new DiagnosticsPrinter(printerOptions); - printer.processor.addDiagnostics(diagnostics); - printer.processor.addSuppressions(suppressions); - printer.print(); - if (!excludeFooter) { - printer.footer(); - } - return printer; + const printer = new DiagnosticsPrinter(printerOptions); + printer.processor.addDiagnostics(diagnostics); + printer.processor.addSuppressions(suppressions); + printer.print(); + if (!excludeFooter) { + printer.footer(); + } + return printer; } export function printDiagnosticsToString( - opts: { - diagnostics: Diagnostics; - suppressions: DiagnosticSuppressions; - printerOptions?: DiagnosticsPrinterOptions; - format?: ReporterStream['format']; - excludeFooter?: boolean; - }, + opts: { + diagnostics: Diagnostics; + suppressions: DiagnosticSuppressions; + printerOptions?: DiagnosticsPrinterOptions; + format?: ReporterStream['format']; + excludeFooter?: boolean; + }, ): string { - const reporter = new Reporter(); - const stream = reporter.attachCaptureStream(opts.format); - printDiagnostics({ - ...opts, - printerOptions: { - reporter, - ...opts.printerOptions, - }, - }); - return stream.read(); + const reporter = new Reporter(); + const stream = reporter.attachCaptureStream(opts.format); + printDiagnostics({ + ...opts, + printerOptions: { + reporter, + ...opts.printerOptions, + }, + }); + return stream.read(); } diff --git a/packages/@romejs/cli-diagnostics/printAdvice.ts b/packages/@romejs/cli-diagnostics/printAdvice.ts index 4b9b5bf0895..bba17ad8a3a 100644 --- a/packages/@romejs/cli-diagnostics/printAdvice.ts +++ b/packages/@romejs/cli-diagnostics/printAdvice.ts @@ -7,18 +7,18 @@ import {Reporter} from '@romejs/cli-reporter'; import { - Diagnostic, - DiagnosticAdviceAction, - DiagnosticAdviceCode, - DiagnosticAdviceCommand, - DiagnosticAdviceDiff, - DiagnosticAdviceFrame, - DiagnosticAdviceInspect, - DiagnosticAdviceItem, - DiagnosticAdviceList, - DiagnosticAdviceLog, - DiagnosticAdviceStacktrace, - diagnosticLocationToMarkupFilelink, + Diagnostic, + DiagnosticAdviceAction, + DiagnosticAdviceCode, + DiagnosticAdviceCommand, + DiagnosticAdviceDiff, + DiagnosticAdviceFrame, + DiagnosticAdviceInspect, + DiagnosticAdviceItem, + DiagnosticAdviceList, + DiagnosticAdviceLog, + DiagnosticAdviceStacktrace, + diagnosticLocationToMarkupFilelink, } from '@romejs/diagnostics'; import {Position} from '@romejs/parser-core'; import {ToLines, showInvisibles, toLines} from './utils'; @@ -35,470 +35,464 @@ import {removeCarriageReturn} from '@romejs/string-utils'; import {serializeCLIFlags} from '@romejs/cli-flags'; type AdvicePrintOptions = { - printer: DiagnosticsPrinter; - flags: DiagnosticsPrinterFlags; - missingFileSources: AbsoluteFilePathSet; - fileSources: DiagnosticsPrinterFileSources; - reporter: Reporter; - diagnostic: Diagnostic; + printer: DiagnosticsPrinter; + flags: DiagnosticsPrinterFlags; + missingFileSources: AbsoluteFilePathSet; + fileSources: DiagnosticsPrinterFileSources; + reporter: Reporter; + diagnostic: Diagnostic; }; type PrintAdviceResult = { - printed: boolean; - truncated: boolean; + printed: boolean; + truncated: boolean; }; const DID_PRINT: PrintAdviceResult = { - printed: true, - truncated: false, + printed: true, + truncated: false, }; const DID_NOT_PRINT: PrintAdviceResult = { - printed: false, - truncated: false, + printed: false, + truncated: false, }; export default function printAdvice( - item: DiagnosticAdviceItem, - opts: AdvicePrintOptions, + item: DiagnosticAdviceItem, + opts: AdvicePrintOptions, ): PrintAdviceResult { - switch (item.type) { - case 'log': - return printLog(item, opts); + switch (item.type) { + case 'log': + return printLog(item, opts); - case 'action': - return printAction(item, opts); + case 'action': + return printAction(item, opts); - case 'list': - return printList(item, opts); + case 'list': + return printList(item, opts); - case 'diff': - return printDiff(item, opts); + case 'diff': + return printDiff(item, opts); - case 'code': - return printCode(item, opts); + case 'code': + return printCode(item, opts); - case 'command': - return printCommand(item, opts); + case 'command': + return printCommand(item, opts); - case 'frame': - return printFrame(item, opts); + case 'frame': + return printFrame(item, opts); - case 'stacktrace': - return printStacktrace(item, opts); + case 'stacktrace': + return printStacktrace(item, opts); - case 'inspect': - return printInspect(item, opts); - } + case 'inspect': + return printInspect(item, opts); + } } function printAction( - item: DiagnosticAdviceAction, - opts: AdvicePrintOptions, + item: DiagnosticAdviceAction, + opts: AdvicePrintOptions, ): PrintAdviceResult { - if (item.hidden && !opts.printer.flags.verboseDiagnostics) { - return DID_NOT_PRINT; - } - - opts.reporter.info(item.instruction); - - const command = serializeCLIFlags( - { - prefix: '', - programName: 'rome', - commandName: item.command, - args: item.args, - flags: { - ...item.commandFlags, - ...item.requestFlags, - }, - }, - {type: 'none'}, - ).sourceText; - opts.reporter.command(command); - return DID_PRINT; + if (item.hidden && !opts.printer.flags.verboseDiagnostics) { + return DID_NOT_PRINT; + } + + opts.reporter.info(item.instruction); + + const command = serializeCLIFlags( + { + prefix: '', + programName: 'rome', + commandName: item.command, + args: item.args, + flags: { + ...item.commandFlags, + ...item.requestFlags, + }, + }, + {type: 'none'}, + ).sourceText; + opts.reporter.command(command); + return DID_PRINT; } function printCommand( - item: DiagnosticAdviceCommand, - opts: AdvicePrintOptions, + item: DiagnosticAdviceCommand, + opts: AdvicePrintOptions, ): PrintAdviceResult { - opts.reporter.command(item.command); - return DID_PRINT; + opts.reporter.command(item.command); + return DID_PRINT; } function printInspect( - item: DiagnosticAdviceInspect, - opts: AdvicePrintOptions, + item: DiagnosticAdviceInspect, + opts: AdvicePrintOptions, ): PrintAdviceResult { - const {reporter} = opts; - reporter.indent(() => { - reporter.inspect(item.data); - }); - return DID_PRINT; + const {reporter} = opts; + reporter.indent(() => { + reporter.inspect(item.data); + }); + return DID_PRINT; } function generateDiffHint(diffs: Diffs): undefined | DiagnosticAdviceItem { - let expected = ''; - let received = ''; - - for (const [type, text] of diffs) { - switch (type) { - case diffConstants.ADD: { - received += text; - break; - } - - case diffConstants.DELETE: { - expected += text; - break; - } - - case diffConstants.EQUAL: { - expected += text; - received += text; - break; - } - } - } - - if (expected.trim() === received.trim()) { - return { - type: 'log', - category: 'info', - text: 'Only difference is leading and trailing whitespace', - }; - } - - const receivedNoCRLF = removeCarriageReturn(received); - if (expected === receivedNoCRLF) { - return { - type: 'log', - category: 'info', - text: 'Identical except the received uses CRLF newlines, while the expected does not', - }; - } - - const expectedNoCRLF = removeCarriageReturn(expected); - if (received === expectedNoCRLF) { - return { - type: 'log', - category: 'info', - text: 'Identical except the expected uses CRLF newlines, while the received does not', - }; - } - - return undefined; + let expected = ''; + let received = ''; + + for (const [type, text] of diffs) { + switch (type) { + case diffConstants.ADD: { + received += text; + break; + } + + case diffConstants.DELETE: { + expected += text; + break; + } + + case diffConstants.EQUAL: { + expected += text; + received += text; + break; + } + } + } + + if (expected.trim() === received.trim()) { + return { + type: 'log', + category: 'info', + text: 'Only difference is leading and trailing whitespace', + }; + } + + const receivedNoCRLF = removeCarriageReturn(received); + if (expected === receivedNoCRLF) { + return { + type: 'log', + category: 'info', + text: 'Identical except the received uses CRLF newlines, while the expected does not', + }; + } + + const expectedNoCRLF = removeCarriageReturn(expected); + if (received === expectedNoCRLF) { + return { + type: 'log', + category: 'info', + text: 'Identical except the expected uses CRLF newlines, while the received does not', + }; + } + + return undefined; } function printDiff( - item: DiagnosticAdviceDiff, - opts: AdvicePrintOptions, + item: DiagnosticAdviceDiff, + opts: AdvicePrintOptions, ): PrintAdviceResult { - const {frame, truncated} = buildPatchCodeFrame( - item, - opts.flags.verboseDiagnostics, - ); - if (frame === '') { - return DID_NOT_PRINT; - } - - opts.reporter.logAll(frame); - - const hint = generateDiffHint(item.diff); - if (hint !== undefined) { - opts.reporter.br(); - printAdvice(hint, opts); - opts.reporter.br(); - } - - return { - printed: true, - truncated, - }; + const {frame, truncated} = buildPatchCodeFrame( + item, + opts.flags.verboseDiagnostics, + ); + if (frame === '') { + return DID_NOT_PRINT; + } + + opts.reporter.logAll(frame); + + const hint = generateDiffHint(item.diff); + if (hint !== undefined) { + opts.reporter.br(); + printAdvice(hint, opts); + opts.reporter.br(); + } + + return { + printed: true, + truncated, + }; } function printList( - item: DiagnosticAdviceList, - opts: AdvicePrintOptions, + item: DiagnosticAdviceList, + opts: AdvicePrintOptions, ): PrintAdviceResult { - if (item.list.length === 0) { - return DID_NOT_PRINT; - } else { - const {truncated} = opts.reporter.list( - item.list, - { - truncate: opts.flags.verboseDiagnostics ? undefined : 20, - reverse: item.reverse, - ordered: item.ordered, - }, - ); - return { - printed: true, - truncated, - }; - } + if (item.list.length === 0) { + return DID_NOT_PRINT; + } else { + const {truncated} = opts.reporter.list( + item.list, + { + truncate: opts.flags.verboseDiagnostics ? undefined : 20, + reverse: item.reverse, + ordered: item.ordered, + }, + ); + return { + printed: true, + truncated, + }; + } } function printCode( - item: DiagnosticAdviceCode, - opts: AdvicePrintOptions, + item: DiagnosticAdviceCode, + opts: AdvicePrintOptions, ): PrintAdviceResult { - const {reporter} = opts; - - const truncated = - !opts.flags.verboseDiagnostics && item.code.length > RAW_CODE_MAX_LENGTH; - let code = truncated ? item.code.slice(0, RAW_CODE_MAX_LENGTH) : item.code; - - reporter.indent(() => { - if (code === '') { - reporter.logAll('empty input'); - } else { - // If it's a string with only whitespace then make it obvious - if (code.trim() === '') { - code = showInvisibles(code); - } - - reporter.logAll(`${escapeMarkup(code)}`); - } - - if (truncated) { - reporter.logAll( - `${item.code.length - RAW_CODE_MAX_LENGTH} more characters truncated`, - ); - } - }); - - return { - printed: true, - truncated, - }; + const {reporter} = opts; + + const truncated = + !opts.flags.verboseDiagnostics && item.code.length > RAW_CODE_MAX_LENGTH; + let code = truncated ? item.code.slice(0, RAW_CODE_MAX_LENGTH) : item.code; + + reporter.indent(() => { + if (code === '') { + reporter.logAll('empty input'); + } else { + // If it's a string with only whitespace then make it obvious + if (code.trim() === '') { + code = showInvisibles(code); + } + + reporter.logAll(`${escapeMarkup(code)}`); + } + + if (truncated) { + reporter.logAll( + `${item.code.length - RAW_CODE_MAX_LENGTH} more characters truncated`, + ); + } + }); + + return { + printed: true, + truncated, + }; } function printFrame( - item: DiagnosticAdviceFrame, - opts: AdvicePrintOptions, + item: DiagnosticAdviceFrame, + opts: AdvicePrintOptions, ): PrintAdviceResult { - const {reporter} = opts; - const {marker, start, end, filename} = item.location; - let {sourceText} = item.location; - const path = opts.printer.createFilePath(filename); - - let cleanMarker: string = ''; - if (marker !== undefined) { - cleanMarker = markupTag('emphasis', cleanMessage(marker)); - } - - let lines: ToLines = { - length: 0, - raw: [], - highlighted: [], - }; - if (sourceText !== undefined) { - lines = toLines({ - path, - input: sourceText, - sourceType: item.location.sourceType, - language: item.location.language, - }); - } else if (filename !== undefined) { - const source = opts.fileSources.get(path); - if (source !== undefined) { - lines = source.lines; - sourceText = source.sourceText; - } - } else if ( - path.isAbsolute() && - opts.missingFileSources.has(path.assertAbsolute()) - ) { - lines = { - length: 1, - raw: ['File does not exist'], - highlighted: ['File does not exist'], - }; - } - - if (sourceText === undefined) { - sourceText = ''; - } - - const frame = buildMessageCodeFrame( - sourceText, - lines, - start, - end, - cleanMarker, - ); - if (frame.trim() === '') { - return DID_NOT_PRINT; - } - - reporter.logAll(frame); - return DID_PRINT; + const {reporter} = opts; + const {marker, start, end, filename} = item.location; + let {sourceText} = item.location; + const path = opts.printer.createFilePath(filename); + + let cleanMarker: string = ''; + if (marker !== undefined) { + cleanMarker = markupTag('emphasis', cleanMessage(marker)); + } + + let lines: ToLines = { + length: 0, + raw: [], + highlighted: [], + }; + if (sourceText !== undefined) { + lines = toLines({ + path, + input: sourceText, + sourceType: item.location.sourceType, + language: item.location.language, + }); + } else if (filename !== undefined) { + const source = opts.fileSources.get(path); + if (source !== undefined) { + lines = source.lines; + sourceText = source.sourceText; + } + } else if ( + path.isAbsolute() && + opts.missingFileSources.has(path.assertAbsolute()) + ) { + lines = { + length: 1, + raw: ['File does not exist'], + highlighted: ['File does not exist'], + }; + } + + if (sourceText === undefined) { + sourceText = ''; + } + + const frame = buildMessageCodeFrame(sourceText, lines, start, end, cleanMarker); + if (frame.trim() === '') { + return DID_NOT_PRINT; + } + + reporter.logAll(frame); + return DID_PRINT; } function printStacktrace( - item: DiagnosticAdviceStacktrace, - opts: AdvicePrintOptions, + item: DiagnosticAdviceStacktrace, + opts: AdvicePrintOptions, ): PrintAdviceResult { - const {diagnostic} = opts; - const {frames} = item; - - let shownCodeFrames = 0; - - const isFirstPart = diagnostic.description.advice[0] === item; - if (!isFirstPart) { - const {title} = item; - if (title !== undefined) { - opts.reporter.info(escapeMarkup(title)); - opts.reporter.br(true); - } - } - - opts.reporter.processedList( - frames, - (reporter, frame) => { - const { - filename, - object, - suffix, - property, - prefix, - line, - column, - language, - sourceText: code, - } = frame; - - const logParts = []; - - // Add prefix - if (prefix !== undefined) { - logParts.push(markupTag('dim', escapeMarkup(prefix))); - } - - // Build path - const objParts = []; - if (object !== undefined) { - objParts.push(markupTag('highlight', escapeMarkup(object), {i: 0})); - } - if (property !== undefined) { - objParts.push(markupTag('highlight', escapeMarkup(property), {i: 1})); - } - if (objParts.length > 0) { - logParts.push(objParts.join('.')); - } - - // Add suffix - if (suffix !== undefined) { - logParts.push(markupTag('success', escapeMarkup(suffix))); - } - - // Add source - if (filename !== undefined && line !== undefined && column !== undefined) { - const header = diagnosticLocationToMarkupFilelink({ - filename, - start: { - index: ob1Number0Neg1, - line, - column, - }, - }); - - if (logParts.length === 0) { - logParts.push(header); - } else { - logParts.push(`(${header})`); - } - } - - reporter.logAll(logParts.join(' ')); - - if ( - shownCodeFrames < 2 && - filename !== undefined && - line !== undefined && - column !== undefined - ) { - const pos: Position = { - index: ob1Number0Neg1, - line, - column, - }; - - const skipped = printFrame( - { - type: 'frame', - location: { - language, - filename, - sourceType: 'module', - start: pos, - end: pos, - sourceText: code, - }, - }, - { - ...opts, - reporter, - }, - ); - if (!skipped) { - reporter.br(true); - shownCodeFrames++; - } - } - }, - { - ordered: true, - truncate: opts.flags.verboseDiagnostics ? undefined : 20, - }, - ); - - return DID_PRINT; + const {diagnostic} = opts; + const {frames} = item; + + let shownCodeFrames = 0; + + const isFirstPart = diagnostic.description.advice[0] === item; + if (!isFirstPart) { + const {title} = item; + if (title !== undefined) { + opts.reporter.info(escapeMarkup(title)); + opts.reporter.br(true); + } + } + + opts.reporter.processedList( + frames, + (reporter, frame) => { + const { + filename, + object, + suffix, + property, + prefix, + line, + column, + language, + sourceText: code, + } = frame; + + const logParts = []; + + // Add prefix + if (prefix !== undefined) { + logParts.push(markupTag('dim', escapeMarkup(prefix))); + } + + // Build path + const objParts = []; + if (object !== undefined) { + objParts.push(markupTag('highlight', escapeMarkup(object), {i: 0})); + } + if (property !== undefined) { + objParts.push(markupTag('highlight', escapeMarkup(property), {i: 1})); + } + if (objParts.length > 0) { + logParts.push(objParts.join('.')); + } + + // Add suffix + if (suffix !== undefined) { + logParts.push(markupTag('success', escapeMarkup(suffix))); + } + + // Add source + if (filename !== undefined && line !== undefined && column !== undefined) { + const header = diagnosticLocationToMarkupFilelink({ + filename, + start: { + index: ob1Number0Neg1, + line, + column, + }, + }); + + if (logParts.length === 0) { + logParts.push(header); + } else { + logParts.push(`(${header})`); + } + } + + reporter.logAll(logParts.join(' ')); + + if ( + shownCodeFrames < 2 && + filename !== undefined && + line !== undefined && + column !== undefined + ) { + const pos: Position = { + index: ob1Number0Neg1, + line, + column, + }; + + const skipped = printFrame( + { + type: 'frame', + location: { + language, + filename, + sourceType: 'module', + start: pos, + end: pos, + sourceText: code, + }, + }, + { + ...opts, + reporter, + }, + ); + if (!skipped) { + reporter.br(true); + shownCodeFrames++; + } + } + }, + { + ordered: true, + truncate: opts.flags.verboseDiagnostics ? undefined : 20, + }, + ); + + return DID_PRINT; } function printLog( - item: DiagnosticAdviceLog, - opts: AdvicePrintOptions, + item: DiagnosticAdviceLog, + opts: AdvicePrintOptions, ): PrintAdviceResult { - const {reporter} = opts; - const {text: message, category} = item; - - if (message !== undefined) { - switch (category) { - case 'none': { - reporter.logAll(message); - break; - } - - case 'warn': { - reporter.warn(message); - break; - } - - case 'info': { - reporter.info(message); - break; - } - - case 'error': { - reporter.error(message); - break; - } - - default: - throw new Error(`Unknown message item log category ${category}`); - } - } - - return item.compact ? DID_NOT_PRINT : DID_PRINT; + const {reporter} = opts; + const {text: message, category} = item; + + if (message !== undefined) { + switch (category) { + case 'none': { + reporter.logAll(message); + break; + } + + case 'warn': { + reporter.warn(message); + break; + } + + case 'info': { + reporter.info(message); + break; + } + + case 'error': { + reporter.error(message); + break; + } + + default: + throw new Error(`Unknown message item log category ${category}`); + } + } + + return item.compact ? DID_NOT_PRINT : DID_PRINT; } function cleanMessage(msg: string): string { - msg = msg.trim(); - if (msg.endsWith('.')) { - msg = msg.slice(0, -1); - } - return msg; + msg = msg.trim(); + if (msg.endsWith('.')) { + msg = msg.slice(0, -1); + } + return msg; } diff --git a/packages/@romejs/cli-diagnostics/types.ts b/packages/@romejs/cli-diagnostics/types.ts index 104c1daab8b..32bbabcaada 100644 --- a/packages/@romejs/cli-diagnostics/types.ts +++ b/packages/@romejs/cli-diagnostics/types.ts @@ -10,27 +10,27 @@ import {AbsoluteFilePath} from '@romejs/path'; import {DiagnosticsProcessor} from '@romejs/diagnostics'; export type DiagnosticsPrinterFlags = { - grep: string; - fieri: boolean; - inverseGrep: boolean; - verboseDiagnostics: boolean; - maxDiagnostics: number; - showAllDiagnostics: boolean; + grep: string; + fieri: boolean; + inverseGrep: boolean; + verboseDiagnostics: boolean; + maxDiagnostics: number; + showAllDiagnostics: boolean; }; export type DiagnosticsFileReader = ( - path: AbsoluteFilePath, + path: AbsoluteFilePath, ) => undefined | DiagnosticsFileReaderStats; export type DiagnosticsFileReaderStats = { - content: string; - mtime: number; + content: string; + mtime: number; }; export type DiagnosticsPrinterOptions = { - processor?: DiagnosticsProcessor; - reporter: Reporter; - cwd?: AbsoluteFilePath; - flags?: DiagnosticsPrinterFlags; - readFile?: DiagnosticsFileReader; + processor?: DiagnosticsProcessor; + reporter: Reporter; + cwd?: AbsoluteFilePath; + flags?: DiagnosticsPrinterFlags; + readFile?: DiagnosticsFileReader; }; diff --git a/packages/@romejs/cli-diagnostics/utils.ts b/packages/@romejs/cli-diagnostics/utils.ts index ec0f360b44f..89907fcb2df 100644 --- a/packages/@romejs/cli-diagnostics/utils.ts +++ b/packages/@romejs/cli-diagnostics/utils.ts @@ -10,80 +10,80 @@ import highlightCode, {AnsiHighlightOptions} from './highlightCode'; import {NEWLINE} from '@romejs/js-parser-utils'; export function normalizeTabs(str: string): string { - return str.replace(/\t/g, ' '); + return str.replace(/\t/g, ' '); } export function showInvisibles(str: string): string { - let ret = ''; - for (const cha of str) { - switch (cha) { - case ' ': { - ret += '\xb7'; // Middle Dot, \u00B7 - break; - } - case '\r': { - ret += '\u240d'; - break; - } - case '\n': { - ret += '\u23ce'; // Return Symbol, \u23ce - break; - } - case '\t': { - ret += '\u21b9'; // Left Arrow To Bar Over Right Arrow To Bar, \u21b9 - break; - } - default: { - ret += cha; - break; - } - } - } - return ret; + let ret = ''; + for (const cha of str) { + switch (cha) { + case ' ': { + ret += '\xb7'; // Middle Dot, \u00B7 + break; + } + case '\r': { + ret += '\u240d'; + break; + } + case '\n': { + ret += '\u23ce'; // Return Symbol, \u23ce + break; + } + case '\t': { + ret += '\u21b9'; // Left Arrow To Bar Over Right Arrow To Bar, \u21b9 + break; + } + default: { + ret += cha; + break; + } + } + } + return ret; } export function cleanEquivalentString(str: string): string { - str = markupToPlainTextString(str); + str = markupToPlainTextString(str); - // Replace all whitespace with spaces - str = str.replace(/[\s\n]+/g, ' '); + // Replace all whitespace with spaces + str = str.replace(/[\s\n]+/g, ' '); - // Remove trailing dot - str = str.replace(/\.+$/, ''); + // Remove trailing dot + str = str.replace(/\.+$/, ''); - // Remove surrounding quotes - str = str.replace(/^"(.*?)"$/, '$1'); + // Remove surrounding quotes + str = str.replace(/^"(.*?)"$/, '$1'); - return str; + return str; } export function joinNoBreak(lines: Array): string { - return `${lines.join('\n')}`; + return `${lines.join('\n')}`; } export function splitLines(src: string): Array { - return src.replace(/\t/g, ' ').split(NEWLINE); + return src.replace(/\t/g, ' ').split(NEWLINE); } export type ToLines = { - length: number; - raw: Array; - highlighted: Array; + length: number; + raw: Array; + highlighted: Array; }; export function toLines(opts: AnsiHighlightOptions): ToLines { - const raw = splitLines(opts.input); - const highlighted = splitLines(highlightCode(opts)); + const raw = splitLines(opts.input); + const highlighted = splitLines(highlightCode(opts)); - if (raw.length !== highlighted.length) { - throw new Error( - `raw and highlighted line count mismatch ${raw.length} !== ${highlighted.length}`, - ); - } + if (raw.length !== highlighted.length) { + throw new Error( + `raw and highlighted line count mismatch ${raw.length} !== ${highlighted.length}`, + ); + } - return { - length: raw.length, - raw, - highlighted, - }; + return { + length: raw.length, + raw, + highlighted, + }; } diff --git a/packages/@romejs/cli-flags/Parser.test.ts b/packages/@romejs/cli-flags/Parser.test.ts index e5dc19b779b..abb8e101b3a 100644 --- a/packages/@romejs/cli-flags/Parser.test.ts +++ b/packages/@romejs/cli-flags/Parser.test.ts @@ -13,268 +13,268 @@ import {catchDiagnostics} from '@romejs/diagnostics'; import {printDiagnostics} from '@romejs/cli-diagnostics'; async function testParser( - t: TestHelper, - { - defineFlags, - args, - callback, - }: { - defineFlags: (consumer: Consumer) => T; - args: Array; - callback?: (parser: Parser, flags: T) => void; - }, + t: TestHelper, + { + defineFlags, + args, + callback, + }: { + defineFlags: (consumer: Consumer) => T; + args: Array; + callback?: (parser: Parser, flags: T) => void; + }, ) { - const reporter = new Reporter(); - const stream = reporter.attachCaptureStream(); + const reporter = new Reporter(); + const stream = reporter.attachCaptureStream(); - const parser = new Parser( - reporter, - { - programName: 'test', - defineFlags, - }, - args, - ); + const parser = new Parser( + reporter, + { + programName: 'test', + defineFlags, + }, + args, + ); - const {diagnostics} = await catchDiagnostics(async () => { - const flags = await parser.init(); - t.namedSnapshot('flags', flags); - if (callback !== undefined) { - callback(parser, flags); - } - }); + const {diagnostics} = await catchDiagnostics(async () => { + const flags = await parser.init(); + t.namedSnapshot('flags', flags); + if (callback !== undefined) { + callback(parser, flags); + } + }); - if (diagnostics !== undefined) { - printDiagnostics({ - diagnostics, - suppressions: [], - printerOptions: { - reporter, - }, - }); - } + if (diagnostics !== undefined) { + printDiagnostics({ + diagnostics, + suppressions: [], + printerOptions: { + reporter, + }, + }); + } - t.namedSnapshot('output', stream.read()); + t.namedSnapshot('output', stream.read()); - const helpStream = reporter.attachCaptureStream(); - await parser.showHelp(); - t.namedSnapshot('help', helpStream.read()); + const helpStream = reporter.attachCaptureStream(); + await parser.showHelp(); + t.namedSnapshot('help', helpStream.read()); } test( - 'does not allow shorthands', - async (t) => { - await testParser( - t, - { - defineFlags: () => {}, - args: ['-f'], - }, - ); - }, + 'does not allow shorthands', + async (t) => { + await testParser( + t, + { + defineFlags: () => {}, + args: ['-f'], + }, + ); + }, ); test( - 'does not allow camel case', - async (t) => { - await testParser( - t, - { - defineFlags: () => {}, - args: ['--fooBar'], - }, - ); - }, + 'does not allow camel case', + async (t) => { + await testParser( + t, + { + defineFlags: () => {}, + args: ['--fooBar'], + }, + ); + }, ); test( - 'flag value equals', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - name: c.get('name').asString(), - }; - }, - args: ['--name=sebastian'], - }, - ); - }, + 'flag value equals', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + name: c.get('name').asString(), + }; + }, + args: ['--name=sebastian'], + }, + ); + }, ); test( - 'required flag', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - name: c.get('name').asString(), - }; - }, - args: ['--name', 'sebastian'], - }, - ); - }, + 'required flag', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + name: c.get('name').asString(), + }; + }, + args: ['--name', 'sebastian'], + }, + ); + }, ); test( - 'required flag omitted', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - name: c.get('name').asString(), - }; - }, - args: [], - }, - ); - }, + 'required flag omitted', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + name: c.get('name').asString(), + }; + }, + args: [], + }, + ); + }, ); test( - 'optional flag', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - name: c.get('name').asStringOrVoid(), - }; - }, - args: ['--name', 'sebastian'], - }, - ); - }, + 'optional flag', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + name: c.get('name').asStringOrVoid(), + }; + }, + args: ['--name', 'sebastian'], + }, + ); + }, ); test( - 'optional flag omitted', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - name: c.get('name').asStringOrVoid(), - }; - }, - args: [], - }, - ); - }, + 'optional flag omitted', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + name: c.get('name').asStringOrVoid(), + }; + }, + args: [], + }, + ); + }, ); test( - 'flag with description', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - name: c.get( - 'name', - { - description: 'the name of the coolest person in the world', - }, - ).asStringOrVoid(), - }; - }, - args: ['--name', 'sebastian'], - }, - ); - }, + 'flag with description', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + name: c.get( + 'name', + { + description: 'the name of the coolest person in the world', + }, + ).asStringOrVoid(), + }; + }, + args: ['--name', 'sebastian'], + }, + ); + }, ); test( - 'optional boolean flag', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - run: c.get('run').asBooleanOrVoid(), - }; - }, - args: ['--run'], - }, - ); - }, + 'optional boolean flag', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + run: c.get('run').asBooleanOrVoid(), + }; + }, + args: ['--run'], + }, + ); + }, ); test( - 'optional boolean flag omitted', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - run: c.get('run').asBooleanOrVoid(), - }; - }, - args: ['--run'], - }, - ); - }, + 'optional boolean flag omitted', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + run: c.get('run').asBooleanOrVoid(), + }; + }, + args: ['--run'], + }, + ); + }, ); test( - 'required boolean flag', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - run: c.get('run').asBoolean(), - }; - }, - args: ['--run'], - }, - ); - }, + 'required boolean flag', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + run: c.get('run').asBoolean(), + }; + }, + args: ['--run'], + }, + ); + }, ); test( - 'required boolean flag omitted', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - run: c.get('run').asBoolean(), - }; - }, - args: ['--run'], - }, - ); - }, + 'required boolean flag omitted', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + run: c.get('run').asBoolean(), + }; + }, + args: ['--run'], + }, + ); + }, ); test( - 'flip boolean flag', - async (t) => { - await testParser( - t, - { - defineFlags: (c) => { - return { - run: c.get('run').asBoolean(), - }; - }, - args: ['--no-run'], - }, - ); - }, + 'flip boolean flag', + async (t) => { + await testParser( + t, + { + defineFlags: (c) => { + return { + run: c.get('run').asBoolean(), + }; + }, + args: ['--no-run'], + }, + ); + }, ); diff --git a/packages/@romejs/cli-flags/Parser.ts b/packages/@romejs/cli-flags/Parser.ts index 2c06873b1f6..ae854f8d9b0 100644 --- a/packages/@romejs/cli-flags/Parser.ts +++ b/packages/@romejs/cli-flags/Parser.ts @@ -8,17 +8,17 @@ import {Reporter, ReporterTableField} from '@romejs/cli-reporter'; import {serializeCLIFlags} from './serializeCLIFlags'; import { - ConsumePath, - ConsumePropertyDefinition, - ConsumeSourceLocationRequestTarget, - Consumer, - consume, + ConsumePath, + ConsumePropertyDefinition, + ConsumeSourceLocationRequestTarget, + Consumer, + consume, } from '@romejs/consume'; import { - dedent, - naturalCompare, - toCamelCase, - toKebabCase, + dedent, + naturalCompare, + toCamelCase, + toKebabCase, } from '@romejs/string-utils'; import {createUnknownFilePath} from '@romejs/path'; import {Dict} from '@romejs/typescript-helpers'; @@ -27,51 +27,51 @@ import {descriptions} from '@romejs/diagnostics'; import {JSONObject} from '@romejs/codec-json'; export type Examples = Array<{ - description: string; - command: string; + description: string; + command: string; }>; type CommandOptions = { - name: string; - category?: string; - description?: string; - usage?: string; - examples?: Examples; - ignoreFlags?: Array; - defineFlags?: (consumer: Consumer) => T; - callback: (flags: T) => void | Promise; + name: string; + category?: string; + description?: string; + usage?: string; + examples?: Examples; + ignoreFlags?: Array; + defineFlags?: (consumer: Consumer) => T; + callback: (flags: T) => void | Promise; }; type AnyCommandOptions = CommandOptions; type ArgDeclaration = { - definition: ConsumePropertyDefinition; - name: string; - command: undefined | string; + definition: ConsumePropertyDefinition; + name: string; + command: undefined | string; }; type DefinedCommand = { - flags: JSONObject; - command: AnyCommandOptions; + flags: JSONObject; + command: AnyCommandOptions; }; export type ParserOptions = { - examples?: Examples; - programName: string; - usage?: string; - description?: string; - version?: string; - ignoreFlags?: Array; - defineFlags: (consumer: Consumer) => T; + examples?: Examples; + programName: string; + usage?: string; + description?: string; + version?: string; + ignoreFlags?: Array; + defineFlags: (consumer: Consumer) => T; }; function splitCommandName(cmd: string): Array { - return cmd.split(' '); + return cmd.split(' '); } // Whether we can display this value in help function isDisplayableHelpValue(value: unknown): value is string | number { - return typeof value === 'string' || typeof value === 'number'; + return typeof value === 'string' || typeof value === 'number'; } type _FlagValue = undefined | number | string | boolean; @@ -81,613 +81,602 @@ export type FlagValue = _FlagValue | Array<_FlagValue>; type SupportedAutocompleteShells = 'bash' | 'fish'; export default class Parser { - constructor( - reporter: Reporter, - opts: ParserOptions, - rawArgs: Array, - ) { - this.reporter = reporter; - this.opts = opts; - - this.shorthandFlags = new Set(); - this.incorrectCaseFlags = new Set(); - this.declaredFlags = new Map(); - this.defaultFlags = new Map(); - this.flags = new Map(); - this.args = []; - - // These are used to track where we should insert an argument for a boolean flag value - this.flagToArgIndex = new Map(); - this.flagToArgOffset = 0; - - this.consumeRawArgs(rawArgs); - - this.commands = new Map(); - this.ranCommand = undefined; - this.currentCommand = undefined; - } - - reporter: Reporter; - opts: ParserOptions; - incorrectCaseFlags: Set; - shorthandFlags: Set; - flags: Map; - defaultFlags: Map; - declaredFlags: Map; - flagToArgIndex: Map; - flagToArgOffset: number; - currentCommand: undefined | string; - ranCommand: undefined | AnyCommandOptions; - commands: Map; - args: Array; - - looksLikeFlag(flag: undefined | string): boolean { - return flag?.[0] === '-'; - } - - toCamelCase(name: string): string { - const camelName = toCamelCase(name); - - // Don't allow passing in straight camelcased names - if (toKebabCase(name) !== name) { - this.incorrectCaseFlags.add(name); - } - - return camelName; - } - - setFlag(key: string, value: string | boolean) { - let newValue: FlagValue = value; - const existing = this.flags.get(key); - if (existing !== undefined) { - if (Array.isArray(existing)) { - newValue = [...existing, value]; - } else { - newValue = [existing, value]; - } - } - this.flags.set(key, newValue); - } - - consumeRawArgs(rawArgs: Array) { - while (rawArgs.length > 0) { - const arg: string = String(rawArgs.shift()); - - if (arg === '--') { - // We consider a -- by itself to halt parsing of args, the rest of the remaining args are added to _ - this.args = this.args.concat(rawArgs); - break; - } else if (arg[0] === '-') { - // Clean the argument by stripping off the dashes - const name = arg[1] === '-' ? arg.slice(2) : arg.slice(1); - - // Flags beginning with no- are always false - if (name.startsWith('no-')) { - const camelName = this.toCamelCase(name.slice(3)); - this.setFlag(camelName, false); - continue; - } - - // Allow for arguments to be passed as --foo=bar - const equalsIndex = name.indexOf('='); - if (equalsIndex !== -1) { - const cleanName = this.toCamelCase(name.slice(0, equalsIndex)); - const value = name.slice(equalsIndex + 1); - this.setFlag(cleanName, value); - continue; - } - - const camelName = this.toCamelCase(name); - - // If the next argument is a flag or we're at the end of the args then just set it to `true` - if (rawArgs.length === 0 || this.looksLikeFlag(rawArgs[0])) { - this.setFlag(camelName, true); - } else { - // Otherwise, take that value - this.setFlag(camelName, String(rawArgs.shift())); - } - - this.flagToArgIndex.set(camelName, this.args.length); - - if (arg[0] === '-' && arg[1] !== '-') { - this.shorthandFlags.add(camelName); - } - } else { - // Not a flag and hasn't been consumed already by a previous arg so it must be a file - this.args.push(arg); - } - } - } - - getFlagsConsumer(): Consumer { - const defaultFlags: Dict = {}; - - const flags: Dict = {}; - for (const [key, value] of this.flags) { - flags[toCamelCase(key)] = value; - } - - return consume({ - filePath: createUnknownFilePath('argv'), - value: flags, - onDefinition: (def, valueConsumer) => { - const key = def.objectPath.join('.'); - - // Detect root object - if (key === '') { - return; - } - - const value = flags[key]; - - // Allow omitting a string flag value - if (def.type === 'string' && value === true) { - valueConsumer.setValue(''); - } - - this.declareArgument({ - name: key, - command: this.currentCommand, - definition: def, - }); - defaultFlags[key] = (def.default as FlagValue); - - // We've parsed arguments like `--foo bar` as `{foo: 'bar}` - // However, --foo may be a boolean flag, so `bar` needs to be correctly added to args - if ( - def.type === 'boolean' && - value !== true && - value !== false && - value !== undefined - ) { - const argIndex = this.flagToArgIndex.get(key); - if (argIndex === undefined) { - throw new Error('No arg index. Should always exist.'); - } - - // Insert the argument at the correct place - this.args.splice(argIndex + this.flagToArgOffset, 0, String(value)); - - // Increase offset to correct subsequent insertions - this.flagToArgOffset++; - - // - valueConsumer.setValue(true); - } - }, - context: { - category: 'flags/invalid', - normalizeKey: (key) => { - return this.incorrectCaseFlags.has(key) ? key : toKebabCase(key); - }, - getOriginalValue: (keys: ConsumePath) => { - return flags[keys[0]]; - }, - getDiagnosticPointer: ( - keys: ConsumePath, - target: ConsumeSourceLocationRequestTarget, - ) => { - const {programName} = this.opts; - - return serializeCLIFlags( - { - programName, - commandName: this.currentCommand, - args: this.args, - defaultFlags, - flags, - incorrectCaseFlags: this.incorrectCaseFlags, - shorthandFlags: this.shorthandFlags, - }, - { - type: 'flag', - key: String(keys[0]), - target, - }, - ); - }, - }, - }); - } - - hasArg(name: string): boolean { - return this.flags.has(name) && this.flags.get(name) !== undefined; - } - - declareArgument(decl: ArgDeclaration) { - // Commands may have colliding flags, this is only a problem in help mode, so make it unique - const key = - decl.command === undefined ? decl.name : `${decl.command}.${decl.name}`; - - // Ensure it hasn't been declared more than once - if (this.declaredFlags.has(key)) { - throw new Error(`Already declared argument ${key}`); - } - - // Declare argument - this.declaredFlags.set(key, decl); - this.defaultFlags.set(key, decl.definition.default); - } - - getInterface(): ParserInterface { - return new ParserInterface(this); - } - - async maybeDefineCommandFlags( - command: AnyCommandOptions, - consumer: Consumer, - ): Promise { - // A command name could be made of multiple strings - const commandParts = splitCommandName(command.name); - for (let i = 0; i < commandParts.length; i++) { - if (commandParts[i] !== this.args[i]) { - return; - } - } - - // Remove command name from arguments - this.args = this.args.slice(commandParts.length); - return await this.defineCommandFlags(command, consumer); - } - - checkBadFlags(consumer: Consumer, definedCommand: undefined | DefinedCommand) { - // Ignore flags from command and root parser options - const ignoreFlags: Array = [ - ...((definedCommand !== undefined && definedCommand.command.ignoreFlags) || []), - ...(this.opts.ignoreFlags || []), - ]; - for (const key of ignoreFlags) { - this.shorthandFlags.delete(key); - this.incorrectCaseFlags.delete(key); - consumer.markUsedProperty(key); - } - - for (const shorthandName of this.shorthandFlags) { - consumer.get(shorthandName).unexpected( - descriptions.FLAGS.UNSUPPORTED_SHORTHANDS, - ); - } - - for (const incorrectName of this.incorrectCaseFlags) { - consumer.get(incorrectName).unexpected( - descriptions.FLAGS.INCORRECT_CASED_FLAG(incorrectName), - ); - } - - consumer.enforceUsedProperties('flag', false); - } - - async init(): Promise { - const consumer = this.getFlagsConsumer(); - - // Show help for --version - const version = this.opts.version; - if (version !== undefined) { - const shouldDisplayVersion = consumer.get( - 'version', - { - description: 'Show the version', - }, - ).asBoolean(false); - if (shouldDisplayVersion) { - this.reporter.logAll(version); - process.exit(0); - } - } - - // i could add a flag for dev-rome itself - // i could take the input command name from the flag - const generateAutocomplete: undefined | SupportedAutocompleteShells = consumer.get( - 'generateAutocomplete', - { - description: 'Generate a shell autocomplete', - inputName: 'shell', - }, - ).asStringSetOrVoid(['fish', 'bash']); - if (generateAutocomplete !== undefined) { - await this.generateAutocomplete(generateAutocomplete); - process.exit(0); - } - - // Show help for --help - const shouldShowHelp = consumer.get( - 'help', - { - description: 'Show this help screen', - }, - ).asBoolean(false); - - let definedCommand: undefined | DefinedCommand; - - const rootFlags = await consumer.bufferDiagnostics(async (consumer) => { - const rootFlags = this.opts.defineFlags(consumer); - - for (const [key, command] of this.commands) { - const definedFlags = await this.maybeDefineCommandFlags( - command, - consumer, - ); - if (definedFlags !== undefined) { - this.currentCommand = key; - definedCommand = {flags: definedFlags, command}; - break; - } - } - - if (!shouldShowHelp) { - this.checkBadFlags(consumer, definedCommand); - } - - this.currentCommand = undefined; - - return rootFlags; - }); - - // Show help for --help - if (shouldShowHelp) { - await this.showHelp( - definedCommand === undefined ? undefined : definedCommand.command, - ); - process.exit(1); - } - - if (definedCommand !== undefined) { - this.ranCommand = definedCommand.command; - await definedCommand.command.callback(definedCommand.flags); - } - - return rootFlags; - } - - buildOptionsHelp(keys: Array): Array> { - const optionOutput: Array<{ - argName: string; - arg: string; - description: string; - }> = []; - let argColumnLength: number = 0; - - // Build up options, we need to do this to line up the columns correctly - for (const key of keys) { - const decl = this.declaredFlags.get(key)!; - - const {definition: def} = decl; - const {metadata} = def; - let argName = decl.name; - let argCol = toKebabCase(decl.name); - - // For booleans that default to `true`, show the --no- version as that'll be what users should use - if (def.type === 'boolean' && def.default === true) { - argCol = `--no-${argCol}`; - argName = `no-${argName}`; - } else { - argCol = `--${argCol}`; - } - - // Add input specifier unless a boolean - if (def.type !== 'boolean') { - let {inputName} = metadata; - - if (inputName === undefined) { - if (def.type === 'number') { - inputName = 'num'; - } else { - inputName = 'input'; - } - } - - argCol += ` <${inputName}>`; - } - - // Set arg col length if we'll be longer - if (argColumnLength < argCol.length) { - argColumnLength = argCol.length; - } - - let descCol: string = - metadata.description === undefined - ? 'no description found' - : metadata.description; - - const {default: defaultValue} = def; - if (defaultValue !== undefined && isDisplayableHelpValue(defaultValue)) { - descCol += ` (default: ${JSON.stringify(defaultValue)})`; - } - - if (def.type === 'string' && def.allowedValues !== undefined) { - const displayAllowedValues = def.allowedValues.filter((item) => - isDisplayableHelpValue(item) - ); - if (displayAllowedValues !== undefined) { - descCol += ` (values: ${displayAllowedValues.join('|')})`; - } - } - - optionOutput.push({ - argName, - arg: markup`${argCol}`, - description: descCol, - }); - } - - // Sort options by argument name - optionOutput.sort((a, b) => naturalCompare(a.argName, b.argName)); - - // Build table rows - return optionOutput.map((opt) => [ - {align: 'right', value: opt.arg}, - opt.description, - ]); - } - - showUsageHelp( - description?: string, - usage: string = '[flags]', - prefix?: string, - ) { - const {reporter} = this; - const {programName} = this.opts; - - reporter.section( - `Usage`, - () => { - if (description !== undefined) { - reporter.logAll(description); - reporter.br(true); - } - - const commandParts = [programName]; - if (prefix !== undefined) { - commandParts.push(prefix); - } - commandParts.push(usage); - - const command = commandParts.join(' '); - reporter.command(command); - }, - ); - } - - showFocusedCommandHelp(command: AnyCommandOptions) { - const {reporter} = this; - const {name, usage, description, examples} = command; - - reporter.br(true); - this.showUsageHelp(description, usage, name); - this.showHelpExamples(examples, name); - - // Find arguments that belong to this command - const argKeys = []; - for (const [key, decl] of this.declaredFlags) { - if (decl.command === name) { - argKeys.push(key); - } - } - - const optRows = this.buildOptionsHelp(argKeys); - if (optRows.length > 0) { - reporter.section( - 'Command Flags', - () => { - reporter.table([], optRows); - }, - ); - } - - reporter.section( - 'Global Flags', - () => { - reporter.info('To view global flags run'); - reporter.command('rome --help'); - }, - ); - } - - showGlobalFlags() { - const {reporter} = this; - reporter.section( - 'Global Flags', - () => { - // Show options not attached to any commands - const lonerArgKeys = []; - for (const [key, decl] of this.declaredFlags) { - if (decl.command === undefined) { - lonerArgKeys.push(key); - } - } - - reporter.table([], this.buildOptionsHelp(lonerArgKeys)); - }, - ); - } - - async generateAutocomplete(shell: SupportedAutocompleteShells) { - const {reporter} = this; - - // Execute all command defineFlags. Only one is usually ran when the arguments match the command name. - // But to generate autocomplete we want all the flags to be declared for all commands. - - const flags = this.getFlagsConsumer(); - for (const command of this.commands.values()) { - // capture() will cause diagnostics to be suppressed - const {consumer} = flags.capture(); - await this.defineCommandFlags(command, consumer); - } - - const { programName } = this.opts; - - switch (shell) { - case 'bash': { - reporter.logAllNoMarkup(this.genBashCompletions(programName)); - break; - } - case 'fish': { - reporter.logAllNoMarkup(this.genFishCompletions(programName)); - break; - } - } - } - - genFishCompletions(prg: string): string { - let script = ''; - const scriptPre = `complete -c ${prg}`; - - // add rome - script += `${scriptPre} -f\n`; - - // add command completions - for (let [subcmd, meta] of this.commands.entries()) { - script += `${scriptPre} -n '__fish_use_subcommand' -a '${subcmd}' -d '${meta.description}'\n`; - } - - // add flag completions - for (let meta of this.declaredFlags.values()) { - const subcmdCond = - meta.command === undefined - ? '' - : `-n '__fish_seen_subcommand_from ${meta.command}'`; - script += `${scriptPre} ${subcmdCond} -l '${meta.name}'\n`; - } - - return script; - } - - genBashCompletions(prg: string): string { - let romeCmds = ''; - let commandFuncs = ''; - let globalFlags = ''; - let cmdFlagMap = new Map(); - - for (let subcmd of this.commands.keys()) { - romeCmds += `${subcmd} `; - } - - for (let meta of this.declaredFlags.values()) { - if (meta.command === undefined) { - globalFlags += `--${meta.name} `; - } else { - if (cmdFlagMap.has(meta.command)) { - cmdFlagMap.set( - meta.command, - `${cmdFlagMap.get(meta.command)} --${meta.name}`, - ); - } else { - cmdFlagMap.set(meta.command, `--${meta.name}`); - } - } - } - - for (let [cmd, flags] of cmdFlagMap.entries()) { - commandFuncs += ` + constructor(reporter: Reporter, opts: ParserOptions, rawArgs: Array) { + this.reporter = reporter; + this.opts = opts; + + this.shorthandFlags = new Set(); + this.incorrectCaseFlags = new Set(); + this.declaredFlags = new Map(); + this.defaultFlags = new Map(); + this.flags = new Map(); + this.args = []; + + // These are used to track where we should insert an argument for a boolean flag value + this.flagToArgIndex = new Map(); + this.flagToArgOffset = 0; + + this.consumeRawArgs(rawArgs); + + this.commands = new Map(); + this.ranCommand = undefined; + this.currentCommand = undefined; + } + + reporter: Reporter; + opts: ParserOptions; + incorrectCaseFlags: Set; + shorthandFlags: Set; + flags: Map; + defaultFlags: Map; + declaredFlags: Map; + flagToArgIndex: Map; + flagToArgOffset: number; + currentCommand: undefined | string; + ranCommand: undefined | AnyCommandOptions; + commands: Map; + args: Array; + + looksLikeFlag(flag: undefined | string): boolean { + return flag?.[0] === '-'; + } + + toCamelCase(name: string): string { + const camelName = toCamelCase(name); + + // Don't allow passing in straight camelcased names + if (toKebabCase(name) !== name) { + this.incorrectCaseFlags.add(name); + } + + return camelName; + } + + setFlag(key: string, value: string | boolean) { + let newValue: FlagValue = value; + const existing = this.flags.get(key); + if (existing !== undefined) { + if (Array.isArray(existing)) { + newValue = [...existing, value]; + } else { + newValue = [existing, value]; + } + } + this.flags.set(key, newValue); + } + + consumeRawArgs(rawArgs: Array) { + while (rawArgs.length > 0) { + const arg: string = String(rawArgs.shift()); + + if (arg === '--') { + // We consider a -- by itself to halt parsing of args, the rest of the remaining args are added to _ + this.args = this.args.concat(rawArgs); + break; + } else if (arg[0] === '-') { + // Clean the argument by stripping off the dashes + const name = arg[1] === '-' ? arg.slice(2) : arg.slice(1); + + // Flags beginning with no- are always false + if (name.startsWith('no-')) { + const camelName = this.toCamelCase(name.slice(3)); + this.setFlag(camelName, false); + continue; + } + + // Allow for arguments to be passed as --foo=bar + const equalsIndex = name.indexOf('='); + if (equalsIndex !== -1) { + const cleanName = this.toCamelCase(name.slice(0, equalsIndex)); + const value = name.slice(equalsIndex + 1); + this.setFlag(cleanName, value); + continue; + } + + const camelName = this.toCamelCase(name); + + // If the next argument is a flag or we're at the end of the args then just set it to `true` + if (rawArgs.length === 0 || this.looksLikeFlag(rawArgs[0])) { + this.setFlag(camelName, true); + } else { + // Otherwise, take that value + this.setFlag(camelName, String(rawArgs.shift())); + } + + this.flagToArgIndex.set(camelName, this.args.length); + + if (arg[0] === '-' && arg[1] !== '-') { + this.shorthandFlags.add(camelName); + } + } else { + // Not a flag and hasn't been consumed already by a previous arg so it must be a file + this.args.push(arg); + } + } + } + + getFlagsConsumer(): Consumer { + const defaultFlags: Dict = {}; + + const flags: Dict = {}; + for (const [key, value] of this.flags) { + flags[toCamelCase(key)] = value; + } + + return consume({ + filePath: createUnknownFilePath('argv'), + value: flags, + onDefinition: (def, valueConsumer) => { + const key = def.objectPath.join('.'); + + // Detect root object + if (key === '') { + return; + } + + const value = flags[key]; + + // Allow omitting a string flag value + if (def.type === 'string' && value === true) { + valueConsumer.setValue(''); + } + + this.declareArgument({ + name: key, + command: this.currentCommand, + definition: def, + }); + defaultFlags[key] = (def.default as FlagValue); + + // We've parsed arguments like `--foo bar` as `{foo: 'bar}` + // However, --foo may be a boolean flag, so `bar` needs to be correctly added to args + if ( + def.type === 'boolean' && + value !== true && + value !== false && + value !== undefined + ) { + const argIndex = this.flagToArgIndex.get(key); + if (argIndex === undefined) { + throw new Error('No arg index. Should always exist.'); + } + + // Insert the argument at the correct place + this.args.splice(argIndex + this.flagToArgOffset, 0, String(value)); + + // Increase offset to correct subsequent insertions + this.flagToArgOffset++; + + // + valueConsumer.setValue(true); + } + }, + context: { + category: 'flags/invalid', + normalizeKey: (key) => { + return this.incorrectCaseFlags.has(key) ? key : toKebabCase(key); + }, + getOriginalValue: (keys: ConsumePath) => { + return flags[keys[0]]; + }, + getDiagnosticPointer: ( + keys: ConsumePath, + target: ConsumeSourceLocationRequestTarget, + ) => { + const {programName} = this.opts; + + return serializeCLIFlags( + { + programName, + commandName: this.currentCommand, + args: this.args, + defaultFlags, + flags, + incorrectCaseFlags: this.incorrectCaseFlags, + shorthandFlags: this.shorthandFlags, + }, + { + type: 'flag', + key: String(keys[0]), + target, + }, + ); + }, + }, + }); + } + + hasArg(name: string): boolean { + return this.flags.has(name) && this.flags.get(name) !== undefined; + } + + declareArgument(decl: ArgDeclaration) { + // Commands may have colliding flags, this is only a problem in help mode, so make it unique + const key = + decl.command === undefined ? decl.name : `${decl.command}.${decl.name}`; + + // Ensure it hasn't been declared more than once + if (this.declaredFlags.has(key)) { + throw new Error(`Already declared argument ${key}`); + } + + // Declare argument + this.declaredFlags.set(key, decl); + this.defaultFlags.set(key, decl.definition.default); + } + + getInterface(): ParserInterface { + return new ParserInterface(this); + } + + async maybeDefineCommandFlags( + command: AnyCommandOptions, + consumer: Consumer, + ): Promise { + // A command name could be made of multiple strings + const commandParts = splitCommandName(command.name); + for (let i = 0; i < commandParts.length; i++) { + if (commandParts[i] !== this.args[i]) { + return; + } + } + + // Remove command name from arguments + this.args = this.args.slice(commandParts.length); + return await this.defineCommandFlags(command, consumer); + } + + checkBadFlags(consumer: Consumer, definedCommand: undefined | DefinedCommand) { + // Ignore flags from command and root parser options + const ignoreFlags: Array = [ + ...((definedCommand !== undefined && definedCommand.command.ignoreFlags) || []), + ...(this.opts.ignoreFlags || []), + ]; + for (const key of ignoreFlags) { + this.shorthandFlags.delete(key); + this.incorrectCaseFlags.delete(key); + consumer.markUsedProperty(key); + } + + for (const shorthandName of this.shorthandFlags) { + consumer.get(shorthandName).unexpected( + descriptions.FLAGS.UNSUPPORTED_SHORTHANDS, + ); + } + + for (const incorrectName of this.incorrectCaseFlags) { + consumer.get(incorrectName).unexpected( + descriptions.FLAGS.INCORRECT_CASED_FLAG(incorrectName), + ); + } + + consumer.enforceUsedProperties('flag', false); + } + + async init(): Promise { + const consumer = this.getFlagsConsumer(); + + // Show help for --version + const version = this.opts.version; + if (version !== undefined) { + const shouldDisplayVersion = consumer.get( + 'version', + { + description: 'Show the version', + }, + ).asBoolean(false); + if (shouldDisplayVersion) { + this.reporter.logAll(version); + process.exit(0); + } + } + + // i could add a flag for dev-rome itself + // i could take the input command name from the flag + const generateAutocomplete: undefined | SupportedAutocompleteShells = consumer.get( + 'generateAutocomplete', + { + description: 'Generate a shell autocomplete', + inputName: 'shell', + }, + ).asStringSetOrVoid(['fish', 'bash']); + if (generateAutocomplete !== undefined) { + await this.generateAutocomplete(generateAutocomplete); + process.exit(0); + } + + // Show help for --help + const shouldShowHelp = consumer.get( + 'help', + { + description: 'Show this help screen', + }, + ).asBoolean(false); + + let definedCommand: undefined | DefinedCommand; + + const rootFlags = await consumer.bufferDiagnostics(async (consumer) => { + const rootFlags = this.opts.defineFlags(consumer); + + for (const [key, command] of this.commands) { + const definedFlags = await this.maybeDefineCommandFlags(command, consumer); + if (definedFlags !== undefined) { + this.currentCommand = key; + definedCommand = {flags: definedFlags, command}; + break; + } + } + + if (!shouldShowHelp) { + this.checkBadFlags(consumer, definedCommand); + } + + this.currentCommand = undefined; + + return rootFlags; + }); + + // Show help for --help + if (shouldShowHelp) { + await this.showHelp( + definedCommand === undefined ? undefined : definedCommand.command, + ); + process.exit(1); + } + + if (definedCommand !== undefined) { + this.ranCommand = definedCommand.command; + await definedCommand.command.callback(definedCommand.flags); + } + + return rootFlags; + } + + buildOptionsHelp(keys: Array): Array> { + const optionOutput: Array<{ + argName: string; + arg: string; + description: string; + }> = []; + let argColumnLength: number = 0; + + // Build up options, we need to do this to line up the columns correctly + for (const key of keys) { + const decl = this.declaredFlags.get(key)!; + + const {definition: def} = decl; + const {metadata} = def; + let argName = decl.name; + let argCol = toKebabCase(decl.name); + + // For booleans that default to `true`, show the --no- version as that'll be what users should use + if (def.type === 'boolean' && def.default === true) { + argCol = `--no-${argCol}`; + argName = `no-${argName}`; + } else { + argCol = `--${argCol}`; + } + + // Add input specifier unless a boolean + if (def.type !== 'boolean') { + let {inputName} = metadata; + + if (inputName === undefined) { + if (def.type === 'number') { + inputName = 'num'; + } else { + inputName = 'input'; + } + } + + argCol += ` <${inputName}>`; + } + + // Set arg col length if we'll be longer + if (argColumnLength < argCol.length) { + argColumnLength = argCol.length; + } + + let descCol: string = + metadata.description === undefined + ? 'no description found' + : metadata.description; + + const {default: defaultValue} = def; + if (defaultValue !== undefined && isDisplayableHelpValue(defaultValue)) { + descCol += ` (default: ${JSON.stringify(defaultValue)})`; + } + + if (def.type === 'string' && def.allowedValues !== undefined) { + const displayAllowedValues = def.allowedValues.filter((item) => + isDisplayableHelpValue(item) + ); + if (displayAllowedValues !== undefined) { + descCol += ` (values: ${displayAllowedValues.join('|')})`; + } + } + + optionOutput.push({ + argName, + arg: markup`${argCol}`, + description: descCol, + }); + } + + // Sort options by argument name + optionOutput.sort((a, b) => naturalCompare(a.argName, b.argName)); + + // Build table rows + return optionOutput.map((opt) => [ + {align: 'right', value: opt.arg}, + opt.description, + ]); + } + + showUsageHelp(description?: string, usage: string = '[flags]', prefix?: string) { + const {reporter} = this; + const {programName} = this.opts; + + reporter.section( + `Usage`, + () => { + if (description !== undefined) { + reporter.logAll(description); + reporter.br(true); + } + + const commandParts = [programName]; + if (prefix !== undefined) { + commandParts.push(prefix); + } + commandParts.push(usage); + + const command = commandParts.join(' '); + reporter.command(command); + }, + ); + } + + showFocusedCommandHelp(command: AnyCommandOptions) { + const {reporter} = this; + const {name, usage, description, examples} = command; + + reporter.br(true); + this.showUsageHelp(description, usage, name); + this.showHelpExamples(examples, name); + + // Find arguments that belong to this command + const argKeys = []; + for (const [key, decl] of this.declaredFlags) { + if (decl.command === name) { + argKeys.push(key); + } + } + + const optRows = this.buildOptionsHelp(argKeys); + if (optRows.length > 0) { + reporter.section( + 'Command Flags', + () => { + reporter.table([], optRows); + }, + ); + } + + reporter.section( + 'Global Flags', + () => { + reporter.info('To view global flags run'); + reporter.command('rome --help'); + }, + ); + } + + showGlobalFlags() { + const {reporter} = this; + reporter.section( + 'Global Flags', + () => { + // Show options not attached to any commands + const lonerArgKeys = []; + for (const [key, decl] of this.declaredFlags) { + if (decl.command === undefined) { + lonerArgKeys.push(key); + } + } + + reporter.table([], this.buildOptionsHelp(lonerArgKeys)); + }, + ); + } + + async generateAutocomplete(shell: SupportedAutocompleteShells) { + const {reporter} = this; + + // Execute all command defineFlags. Only one is usually ran when the arguments match the command name. + // But to generate autocomplete we want all the flags to be declared for all commands. + + const flags = this.getFlagsConsumer(); + for (const command of this.commands.values()) { + // capture() will cause diagnostics to be suppressed + const {consumer} = flags.capture(); + await this.defineCommandFlags(command, consumer); + } + + const {programName} = this.opts; + + switch (shell) { + case 'bash': { + reporter.logAllNoMarkup(this.genBashCompletions(programName)); + break; + } + case 'fish': { + reporter.logAllNoMarkup(this.genFishCompletions(programName)); + break; + } + } + } + + genFishCompletions(prg: string): string { + let script = ''; + const scriptPre = `complete -c ${prg}`; + + // add rome + script += `${scriptPre} -f\n`; + + // add command completions + for (let [subcmd, meta] of this.commands.entries()) { + script += `${scriptPre} -n '__fish_use_subcommand' -a '${subcmd}' -d '${meta.description}'\n`; + } + + // add flag completions + for (let meta of this.declaredFlags.values()) { + const subcmdCond = + meta.command === undefined + ? '' + : `-n '__fish_seen_subcommand_from ${meta.command}'`; + script += `${scriptPre} ${subcmdCond} -l '${meta.name}'\n`; + } + + return script; + } + + genBashCompletions(prg: string): string { + let romeCmds = ''; + let commandFuncs = ''; + let globalFlags = ''; + let cmdFlagMap = new Map(); + + for (let subcmd of this.commands.keys()) { + romeCmds += `${subcmd} `; + } + + for (let meta of this.declaredFlags.values()) { + if (meta.command === undefined) { + globalFlags += `--${meta.name} `; + } else { + if (cmdFlagMap.has(meta.command)) { + cmdFlagMap.set( + meta.command, + `${cmdFlagMap.get(meta.command)} --${meta.name}`, + ); + } else { + cmdFlagMap.set(meta.command, `--${meta.name}`); + } + } + } + + for (let [cmd, flags] of cmdFlagMap.entries()) { + commandFuncs += ` __${prg}_${cmd}() { cmds=""; local_flags="${flags}" } `; - } + } - let romeFunc = ` + let romeFunc = ` __${prg}() { cmds="${romeCmds}" @@ -695,7 +684,7 @@ export default class Parser { } `; - let mainScript = ` + let mainScript = ` #!/usr/bin/env bash global_flags="${globalFlags}" @@ -740,188 +729,187 @@ export default class Parser { } `; - return dedent` + return dedent` ${mainScript} ${commandFuncs} ${romeFunc} complete -F __${prg}_gen_completions ${prg} `; - } - - async showHelp(command: undefined | AnyCommandOptions = this.ranCommand) { - if (command !== undefined) { - this.showFocusedCommandHelp(command); - return; - } - - const {reporter} = this; - const {description, usage, examples, programName} = this.opts; - - this.showUsageHelp(description, usage); - this.showGlobalFlags(); - - // Sort commands into their appropriate categories for output - const commandsByCategory: Map> = new Map(); - const categoryNames: Set = new Set(); - for (const [name, command] of this.commands) { - if (name[0] === '_') { - continue; - } - - const {category} = command; - let commandsForCategory = commandsByCategory.get(category); - if (commandsForCategory === undefined) { - commandsForCategory = []; - commandsByCategory.set(category, commandsForCategory); - } - commandsForCategory.push(command); - categoryNames.add(category); - } - - reporter.section( - 'Commands', - () => { - const sortedCategoryNames: Array = Array.from( - categoryNames, - ).sort(); - - // Always make sure categoryless commands are displayed first - if (sortedCategoryNames.includes(undefined)) { - sortedCategoryNames.splice(sortedCategoryNames.indexOf(undefined), 1); - sortedCategoryNames.unshift(undefined); - } - - for (const category of sortedCategoryNames) { - const commands = commandsByCategory.get(category)!; - - if (category !== undefined) { - reporter.logAll(`${category} Commands`); - } - - // Sort by name - commands.sort((a, b) => a.name.localeCompare(b.name)); - - reporter.list( - commands.map((cmd) => { - return `${cmd.name} ${cmd.description === - undefined - ? '' - : cmd.description}`; - }), - ); - reporter.br(); - } - - reporter.info('To view help for a specific command run'); - reporter.command(`${programName} command_name --help`); - }, - ); - - this.showHelpExamples(examples); - } - - showHelpExamples(examples?: Examples, prefix?: string) { - const {programName} = this.opts; - const {reporter} = this; - - if (examples === undefined || examples.length === 0) { - return; - } - - reporter.section( - 'Examples', - () => { - for (const {description, command} of examples) { - const commandParts = []; - if (programName !== undefined) { - commandParts.push(programName); - } - if (prefix !== undefined) { - commandParts.push(prefix); - } - commandParts.push(command); - - const builtCommand = commandParts.join(' '); - - reporter.br(); - if (description !== undefined) { - reporter.logAll(description); - } - reporter.command(builtCommand); - } - }, - ); - } - - commandRequired() { - if (this.ranCommand) { - return; - } - - if (this.args.length === 0) { - this.reporter.error( - 'No command specified. Run --help to see available commands.', - ); - } else { - // TODO command name is not sanitized for markup - // TODO produce a diagnostic instead - this.reporter.error( - `Unknown command ${this.args.join(' ')}. Run --help to see available commands.`, - ); - } - - process.exit(1); - } - - addCommand(opts: AnyCommandOptions) { - if (this.currentCommand !== undefined) { - throw new Error("Nested commands aren't allowed"); - } - - this.commands.set(opts.name, opts); - } - - async defineCommandFlags( - command: AnyCommandOptions, - consumer: Consumer, - ): Promise { - this.currentCommand = command.name; - - let flags: JSONObject = {}; - if (command.defineFlags !== undefined) { - flags = command.defineFlags(consumer); - } - - this.currentCommand = undefined; - - return flags; - } + } + + async showHelp(command: undefined | AnyCommandOptions = this.ranCommand) { + if (command !== undefined) { + this.showFocusedCommandHelp(command); + return; + } + + const {reporter} = this; + const {description, usage, examples, programName} = this.opts; + + this.showUsageHelp(description, usage); + this.showGlobalFlags(); + + // Sort commands into their appropriate categories for output + const commandsByCategory: Map> = new Map(); + const categoryNames: Set = new Set(); + for (const [name, command] of this.commands) { + if (name[0] === '_') { + continue; + } + + const {category} = command; + let commandsForCategory = commandsByCategory.get(category); + if (commandsForCategory === undefined) { + commandsForCategory = []; + commandsByCategory.set(category, commandsForCategory); + } + commandsForCategory.push(command); + categoryNames.add(category); + } + + reporter.section( + 'Commands', + () => { + const sortedCategoryNames: Array = Array.from( + categoryNames, + ).sort(); + + // Always make sure categoryless commands are displayed first + if (sortedCategoryNames.includes(undefined)) { + sortedCategoryNames.splice(sortedCategoryNames.indexOf(undefined), 1); + sortedCategoryNames.unshift(undefined); + } + + for (const category of sortedCategoryNames) { + const commands = commandsByCategory.get(category)!; + + if (category !== undefined) { + reporter.logAll(`${category} Commands`); + } + + // Sort by name + commands.sort((a, b) => a.name.localeCompare(b.name)); + + reporter.list( + commands.map((cmd) => { + return `${cmd.name} ${cmd.description === undefined + ? '' + : cmd.description}`; + }), + ); + reporter.br(); + } + + reporter.info('To view help for a specific command run'); + reporter.command(`${programName} command_name --help`); + }, + ); + + this.showHelpExamples(examples); + } + + showHelpExamples(examples?: Examples, prefix?: string) { + const {programName} = this.opts; + const {reporter} = this; + + if (examples === undefined || examples.length === 0) { + return; + } + + reporter.section( + 'Examples', + () => { + for (const {description, command} of examples) { + const commandParts = []; + if (programName !== undefined) { + commandParts.push(programName); + } + if (prefix !== undefined) { + commandParts.push(prefix); + } + commandParts.push(command); + + const builtCommand = commandParts.join(' '); + + reporter.br(); + if (description !== undefined) { + reporter.logAll(description); + } + reporter.command(builtCommand); + } + }, + ); + } + + commandRequired() { + if (this.ranCommand) { + return; + } + + if (this.args.length === 0) { + this.reporter.error( + 'No command specified. Run --help to see available commands.', + ); + } else { + // TODO command name is not sanitized for markup + // TODO produce a diagnostic instead + this.reporter.error( + `Unknown command ${this.args.join(' ')}. Run --help to see available commands.`, + ); + } + + process.exit(1); + } + + addCommand(opts: AnyCommandOptions) { + if (this.currentCommand !== undefined) { + throw new Error("Nested commands aren't allowed"); + } + + this.commands.set(opts.name, opts); + } + + async defineCommandFlags( + command: AnyCommandOptions, + consumer: Consumer, + ): Promise { + this.currentCommand = command.name; + + let flags: JSONObject = {}; + if (command.defineFlags !== undefined) { + flags = command.defineFlags(consumer); + } + + this.currentCommand = undefined; + + return flags; + } } export class ParserInterface { - constructor(parser: Parser) { - this.parser = parser; - } + constructor(parser: Parser) { + this.parser = parser; + } - parser: Parser; + parser: Parser; - init(): Promise { - return this.parser.init(); - } + init(): Promise { + return this.parser.init(); + } - showHelp(): Promise { - return this.parser.showHelp(); - } + showHelp(): Promise { + return this.parser.showHelp(); + } - getArgs(): Array { - return this.parser.args; - } + getArgs(): Array { + return this.parser.args; + } - commandRequired() { - this.parser.commandRequired(); - } + commandRequired() { + this.parser.commandRequired(); + } - command(opts: AnyCommandOptions) { - this.parser.addCommand(opts); - } + command(opts: AnyCommandOptions) { + this.parser.addCommand(opts); + } } diff --git a/packages/@romejs/cli-flags/index.ts b/packages/@romejs/cli-flags/index.ts index 60b57f58f3e..4b58b44ee51 100644 --- a/packages/@romejs/cli-flags/index.ts +++ b/packages/@romejs/cli-flags/index.ts @@ -12,27 +12,27 @@ export {FlagValue} from './Parser'; export {ParserInterface as FlagParser}; export function parseCLIFlags( - reporter: Reporter, - args: Array, - opts: ParserOptions, + reporter: Reporter, + args: Array, + opts: ParserOptions, ): ParserInterface { - const parser = new Parser(reporter, opts, args); - return parser.getInterface(); + const parser = new Parser(reporter, opts, args); + return parser.getInterface(); } export function parseCLIFlagsFromProcess( - opts: ParserOptions, + opts: ParserOptions, ): ParserInterface { - return parseCLIFlags( - Reporter.fromProcess(), - process.argv.slice(2), - { - ...opts, - programName: opts.programName === undefined - ? process.argv[1] - : opts.programName, - }, - ); + return parseCLIFlags( + Reporter.fromProcess(), + process.argv.slice(2), + { + ...opts, + programName: opts.programName === undefined + ? process.argv[1] + : opts.programName, + }, + ); } export * from './serializeCLIFlags'; diff --git a/packages/@romejs/cli-flags/serializeCLIFlags.ts b/packages/@romejs/cli-flags/serializeCLIFlags.ts index 33de01a0fd0..53e2c3327e9 100644 --- a/packages/@romejs/cli-flags/serializeCLIFlags.ts +++ b/packages/@romejs/cli-flags/serializeCLIFlags.ts @@ -13,195 +13,195 @@ import {Dict, RequiredProps} from '@romejs/typescript-helpers'; import {FlagValue} from './Parser'; type SerializeCLIData = { - programName: undefined | string; - commandName: undefined | string; - args?: Array; - defaultFlags?: Dict; - flags?: Dict; - incorrectCaseFlags?: Set; - shorthandFlags?: Set; - prefix?: string; + programName: undefined | string; + commandName: undefined | string; + args?: Array; + defaultFlags?: Dict; + flags?: Dict; + incorrectCaseFlags?: Set; + shorthandFlags?: Set; + prefix?: string; }; export type SerializeCLITarget = - | { - type: 'flag'; - key: string; - target?: ConsumeSourceLocationRequestTarget; - } - | { - type: 'arg'; - key: number; - } - | { - type: 'arg-range'; - from: number; - to?: number; - } - | { - type: 'none'; - } - | { - type: 'command'; - } - | { - type: 'program'; - }; + | { + type: 'flag'; + key: string; + target?: ConsumeSourceLocationRequestTarget; + } + | { + type: 'arg'; + key: number; + } + | { + type: 'arg-range'; + from: number; + to?: number; + } + | { + type: 'none'; + } + | { + type: 'command'; + } + | { + type: 'program'; + }; export function serializeCLIFlags( - { - args = [], - flags = {}, - programName, - commandName, - defaultFlags = {}, - shorthandFlags = new Set(), - incorrectCaseFlags = new Set(), - prefix = '$ ', - }: SerializeCLIData, - target: SerializeCLITarget, + { + args = [], + flags = {}, + programName, + commandName, + defaultFlags = {}, + shorthandFlags = new Set(), + incorrectCaseFlags = new Set(), + prefix = '$ ', + }: SerializeCLIData, + target: SerializeCLITarget, ): RequiredProps { - let startColumn: Number0 = ob1Number0Neg1; - let endColumn: Number0 = ob1Number0Neg1; - let code = prefix; - - function setStartColumn() { - startColumn = ob1Coerce0(code.length); - } - - function setEndColumn() { - // Never point to a space - if (code[code.length - 1] === ' ') { - endColumn = ob1Coerce0(code.length - 1); - } else { - endColumn = ob1Coerce0(code.length); - } - } - - if (programName !== undefined) { - if (target.type === 'program') { - setStartColumn(); - } - - code += `${programName} `; - - if (target.type === 'program') { - setEndColumn(); - } - } - - if (commandName !== undefined) { - if (target.type === 'command') { - setStartColumn(); - } - - code += `${commandName} `; - - if (target.type === 'command') { - setEndColumn(); - } - } - - // Add args - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - - let isTarget = false; - if (target.type === 'arg' && i === target.key) { - isTarget = true; - } - if (target.type === 'arg-range' && target.from === i) { - isTarget = true; - } - - if (isTarget) { - setStartColumn(); - } - - code += `${arg} `; - - let isEndTarget = isTarget; - - // We are the end target if we're within the from-to range or we're greater than from with no to - if ( - target.type === 'arg-range' && - i > target.from && - (target.to === undefined || target.to <= i) - ) { - isEndTarget = true; - } - - if (isEndTarget) { - setEndColumn(); - } - } - - // Add flags - for (const key in flags) { - const val = flags[key]; - - // Ignore pointless default values - if (val === defaultFlags[key]) { - continue; - } - - const values = Array.isArray(val) ? val : [val]; - - const isTarget = target.type === 'flag' && key === target.key; - - if (isTarget) { - setStartColumn(); - } - - for (const val of values) { - const flagPrefix = shorthandFlags.has(key) ? '-' : '--'; - const kebabKey = incorrectCaseFlags.has(key) ? key : toKebabCase(key); - if (val === false) { - code += `${flagPrefix}no-${kebabKey} `; - } else { - code += `${flagPrefix}${kebabKey} `; - } - - // Booleans are always indicated with just their flag - if (typeof val !== 'boolean') { - // Only point to the value for flags that specify it - if ( - isTarget && - target.type === 'flag' && - (target.target === 'value' || target.target === 'inner-value') - ) { - startColumn = ob1Coerce0(code.length); - } - - // Number or string - code += `${String(val)} `; - } - } - - if (isTarget) { - setEndColumn(); - } - } - - if (startColumn === ob1Number0Neg1 || endColumn === ob1Number0Neg1) { - startColumn = ob1Coerce0(code.length - 1); - endColumn = startColumn; - } - - return { - language: 'shell', - mtime: undefined, - sourceText: code, - filename: 'argv', - start: { - line: ob1Number1, - column: startColumn, - index: startColumn, - }, - end: { - line: ob1Number1, - column: endColumn, - index: endColumn, - }, - }; + let startColumn: Number0 = ob1Number0Neg1; + let endColumn: Number0 = ob1Number0Neg1; + let code = prefix; + + function setStartColumn() { + startColumn = ob1Coerce0(code.length); + } + + function setEndColumn() { + // Never point to a space + if (code[code.length - 1] === ' ') { + endColumn = ob1Coerce0(code.length - 1); + } else { + endColumn = ob1Coerce0(code.length); + } + } + + if (programName !== undefined) { + if (target.type === 'program') { + setStartColumn(); + } + + code += `${programName} `; + + if (target.type === 'program') { + setEndColumn(); + } + } + + if (commandName !== undefined) { + if (target.type === 'command') { + setStartColumn(); + } + + code += `${commandName} `; + + if (target.type === 'command') { + setEndColumn(); + } + } + + // Add args + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + let isTarget = false; + if (target.type === 'arg' && i === target.key) { + isTarget = true; + } + if (target.type === 'arg-range' && target.from === i) { + isTarget = true; + } + + if (isTarget) { + setStartColumn(); + } + + code += `${arg} `; + + let isEndTarget = isTarget; + + // We are the end target if we're within the from-to range or we're greater than from with no to + if ( + target.type === 'arg-range' && + i > target.from && + (target.to === undefined || target.to <= i) + ) { + isEndTarget = true; + } + + if (isEndTarget) { + setEndColumn(); + } + } + + // Add flags + for (const key in flags) { + const val = flags[key]; + + // Ignore pointless default values + if (val === defaultFlags[key]) { + continue; + } + + const values = Array.isArray(val) ? val : [val]; + + const isTarget = target.type === 'flag' && key === target.key; + + if (isTarget) { + setStartColumn(); + } + + for (const val of values) { + const flagPrefix = shorthandFlags.has(key) ? '-' : '--'; + const kebabKey = incorrectCaseFlags.has(key) ? key : toKebabCase(key); + if (val === false) { + code += `${flagPrefix}no-${kebabKey} `; + } else { + code += `${flagPrefix}${kebabKey} `; + } + + // Booleans are always indicated with just their flag + if (typeof val !== 'boolean') { + // Only point to the value for flags that specify it + if ( + isTarget && + target.type === 'flag' && + (target.target === 'value' || target.target === 'inner-value') + ) { + startColumn = ob1Coerce0(code.length); + } + + // Number or string + code += `${String(val)} `; + } + } + + if (isTarget) { + setEndColumn(); + } + } + + if (startColumn === ob1Number0Neg1 || endColumn === ob1Number0Neg1) { + startColumn = ob1Coerce0(code.length - 1); + endColumn = startColumn; + } + + return { + language: 'shell', + mtime: undefined, + sourceText: code, + filename: 'argv', + start: { + line: ob1Number1, + column: startColumn, + index: startColumn, + }, + end: { + line: ob1Number1, + column: endColumn, + index: endColumn, + }, + }; } diff --git a/packages/@romejs/cli-reporter/Progress.ts b/packages/@romejs/cli-reporter/Progress.ts index 914c641e830..020e7796bc4 100644 --- a/packages/@romejs/cli-reporter/Progress.ts +++ b/packages/@romejs/cli-reporter/Progress.ts @@ -8,9 +8,9 @@ import {humanizeNumber, humanizeTime} from '@romejs/string-utils'; import {Reporter} from '@romejs/cli-reporter'; import { - RemoteReporterClientMessage, - ReporterProgressOptions, - ReporterStream, + RemoteReporterClientMessage, + ReporterProgressOptions, + ReporterStream, } from './types'; import ProgressBase from './ProgressBase'; import {ansiEscapes, formatAnsi} from '@romejs/string-markup'; @@ -24,390 +24,390 @@ const BOUNCER_INTERVAL = 1_000 / 30; const BOUNCER_WIDTH = 20; export default class Progress extends ProgressBase { - constructor( - reporter: Reporter, - opts: ReporterProgressOptions = {}, - onEnd?: () => void, - ) { - super(reporter, opts); + constructor( + reporter: Reporter, + opts: ReporterProgressOptions = {}, + onEnd?: () => void, + ) { + super(reporter, opts); - this.startTime = Date.now(); - this.lastRenderTime = Date.now(); - this.lastRenderCurrent = 0; + this.startTime = Date.now(); + this.lastRenderTime = Date.now(); + this.lastRenderCurrent = 0; - this.closed = false; - this.onEnd = onEnd; + this.closed = false; + this.onEnd = onEnd; - this.delay = 60; - this.renderEvery = 0; - - this.streamToBouncerStart = new Map(); - this.startBouncer(); - - this.queueRender(opts.initDelay); - this.initName(opts.name); - } - - closed: boolean; - delay: number; - renderTimer: undefined | NodeJS.Timeout; - - streamToBouncerStart: Map; - bouncerTimer: undefined | NodeJS.Timeout; + this.delay = 60; + this.renderEvery = 0; + + this.streamToBouncerStart = new Map(); + this.startBouncer(); + + this.queueRender(opts.initDelay); + this.initName(opts.name); + } + + closed: boolean; + delay: number; + renderTimer: undefined | NodeJS.Timeout; + + streamToBouncerStart: Map; + bouncerTimer: undefined | NodeJS.Timeout; - onEnd: undefined | (() => void); - renderEvery: number; - startTime: number; - lastRenderCurrent: number; - lastRenderTime: number; + onEnd: undefined | (() => void); + renderEvery: number; + startTime: number; + lastRenderCurrent: number; + lastRenderTime: number; - initName(name: undefined | string) { - if (name === undefined) { - return; - } + initName(name: undefined | string) { + if (name === undefined) { + return; + } - // TODO fetch approximate total and eta based on `name` - } - - processRemoteClientMessage(msg: RemoteReporterClientMessage) { - switch (msg.type) { - case 'PROGRESS_SET_CURRENT': - return this.setCurrent(msg.current); - - case 'PROGRESS_SET_TOTAL': - return this.setTotal(msg.total, msg.approximate); - - case 'PROGRESS_SET_TEXT': - return this.setText(msg.text); - - case 'PROGRESS_PUSH_TEXT': - return this.pushText(msg.text); - - case 'PROGRESS_POP_TEXT': - return this.popText(msg.text); - - case 'PROGRESS_SET_APPROXIMATE_ETA': - return this.setApproximateETA(msg.duration); - - case 'PROGRESS_TICK': - return this.tick(); - - case 'PROGRESS_END': - return this.end(); - - case 'PROGRESS_RESUME': - return this.resume(); - - case 'PROGRESS_PAUSE': - return this.pause(); - } - } - - getElapsedTime(): number { - return Date.now() - this.startTime - this.pausedElapsed; - } - - getBouncerPosition(stream: ReporterStream): number { - const start = this.streamToBouncerStart.get(stream); - if (start === undefined) { - return 0; - } else { - return start; - } - } - - startBouncer() { - const queueTick = () => { - this.bouncerTimer = setTimeout(tick, BOUNCER_INTERVAL); - }; - - const tick = this.reporter.wrapCallback(() => { - if (this.paused) { - queueTick(); - return; - } - - const elapsedTime = this.getElapsedTime(); - const elapsedFrames = Math.round(elapsedTime / BOUNCER_INTERVAL); - - for (const stream of this.reporter.streams) { - // We remove the bouncer width from the total columns since we'll append it - const width = stream.columns - BOUNCER_WIDTH; - - // Position to place the bouncer - let position = elapsedFrames % width; - - // Every odd complete bounce should reverse direction - const totalBounces = Math.floor(elapsedFrames / width); - if (totalBounces % 2 === 1) { - position = width - position; - } - - this.streamToBouncerStart.set(stream, position); - } - - queueTick(); - this.render(); - }); - - queueTick(); - } - - setCurrent(current: number) { - if (this.closed) { - return; - } - - super.setCurrent(current); - - if (this.isRenderDue()) { - this.render(); - } - } - - setTotal(total: number, approximate: boolean = false) { - super.setTotal(total, approximate); - this.renderEvery = Math.round(total / 100); - this.endBouncer(); - } - - setText(text: string) { - if (this.closed) { - return; - } - - super.setText(text); - } - - queueRender(delay: number = this.delay) { - if (this.closed) { - // Progress bar has been removed - return; - } - - if (this.renderTimer !== undefined) { - // Render already queued - return; - } - - this.renderTimer = setTimeout( - this.reporter.wrapCallback(() => { - this.render(); - }), - delay, - ); - } - - endBouncer() { - if (this.bouncerTimer !== undefined) { - clearTimeout(this.bouncerTimer); - } - this.bouncerTimer = undefined; - } - - endRender() { - if (this.renderTimer !== undefined) { - clearTimeout(this.renderTimer); - } - this.renderTimer = undefined; - } - - end() { - this.closed = true; - this.endBouncer(); - this.endRender(); - this.reporter.clearLineAll(); - - if (this.onEnd !== undefined) { - this.onEnd(); - } - } - - // Ensure that we update the progress bar after a certain amount of ticks - - // This allows us to use the progress bar for sync work where the event loop is always blocked - isRenderDue(): boolean { - const isDue: boolean = - this.current > this.lastRenderCurrent + this.renderEvery; - if (isDue) { - // We also make sure that we never force update more often than once a second - // This is to ensure that the progress bar isn't negatively effecting performance - const timeSinceLastRender: number = Date.now() - this.lastRenderTime; - return timeSinceLastRender > 1_000; - } else { - return false; - } - } - - isBoldCharacter(i: number, ranges: BoldRanges): boolean { - for (const [start, end] of ranges) { - if (start >= i && end <= i) { - return true; - } - } - - return false; - } - - splitCharacters(str: string, boldRanges: BoldRanges): SplitBar { - return str.split('').map((char, i) => { - if (this.isBoldCharacter(i, boldRanges)) { - return [i, formatAnsi.bold(char)]; - } else { - return [i, char]; - } - }); - } - - buildProgressBouncer(stream: ReporterStream, bar: SplitBar): string { - let start = this.getBouncerPosition(stream); - let fullBar = ''; - for (const [i, char] of bar) { - const isBounce = i >= start && i < start + BOUNCER_WIDTH; - - if (isBounce) { - if (this.paused) { - fullBar += formatAnsi.inverse(char); - } else { - fullBar += formatAnsi.white(formatAnsi.bgYellow(char)); - } - } else { - fullBar += char; - } - } - return fullBar; - } - - buildProgressBar(stream: ReporterStream, bar: SplitBar, total: number): string { - const ratio = Math.min(Math.max(this.current / total, 0), 1); - - const completeLength = Math.round(stream.columns * ratio); - let fullBar = ''; - for (const [i, char] of bar) { - if (i < completeLength) { - if (this.paused) { - fullBar += formatAnsi.inverse(char); - } else { - fullBar += formatAnsi.white(formatAnsi.bgGreen(char)); - } - } else { - fullBar += char; - } - } - return fullBar; - } - - buildBar(stream: ReporterStream) { - const {total, current, title} = this; - - // Text ranges that we should make bold - const boldRanges: BoldRanges = []; - - // Text to prefix to the bar - let prefix = ''; - if (title !== undefined) { - prefix += title; - - // Only the title should be bold, not the subtext - boldRanges.push([0, prefix.length - 1]); - } - - const text = this.getText(); - if (text !== undefined) { - // Separate a title and it's text with a colon - if (title !== undefined) { - prefix += ': '; - } - prefix += text; - } - - // Text to put at the end of the bar - let suffix = ''; - - // Total time since the progress bar was created - const elapsed = this.getElapsedTime(); - - // Time elapsed eg: elapsed 1m5s - if (this.opts.elapsed) { - suffix += `elapsed ${humanizeTime(elapsed)} `; - } - - // Don't bother with a suffix if we haven't completed a single item - if (current > 0) { - // How many milliseconds spent per total items - const averagePerItem = elapsed / current; - - // ETA eg: 1m5s - if (this.opts.eta) { - if (this.approximateETA !== undefined && elapsed < this.approximateETA) { - // Approximate ETA - const left = elapsed - this.approximateETA; - suffix += `eta ~${humanizeTime(left)} `; - } else if (total !== undefined) { - // How many items we have left - const itemsLeft = total - current; - - // Total estimated time left - const eta = itemsLeft * averagePerItem; - suffix += `eta ${humanizeTime(eta)} `; - } else { - const ops = Math.round(1_000 / averagePerItem); - suffix += `${humanizeNumber(ops)} op/s `; - } - } - - // Counter eg: 5/100 - suffix += `${humanizeNumber(current)}`; - if (total !== undefined) { - suffix += '/'; - if (this.approximateTotal) { - suffix += '~'; - } - suffix += humanizeNumber(total); - } - } - - // Get the full width of the bar. We take off 3 for padding. - const width = stream.columns - 3; - - // The amount of spaces to put between the title and counter - const spacerLength = Math.max(0, width - prefix.length - suffix.length); - const spacer = ' '.repeat(spacerLength); - - // Trim the prefix if it will overflow - prefix = prefix.slice(0, width - spacerLength - suffix.length); - - // The full raw bar without any coloring - const raw = ` ${prefix}${spacer} ${suffix}`; - - // Make sure the counter is bold - boldRanges.push([raw.length - suffix.length, raw.length - 1]); - - // Split the raw bar into an array of formatted characters - const chars = this.splitCharacters(raw, boldRanges); - - if (total === undefined) { - return this.buildProgressBouncer(stream, chars); - } else { - return this.buildProgressBar(stream, chars, total); - } - } - - render() { - if (this.closed) { - return; - } - - this.endRender(); - - this.lastRenderCurrent = this.current; - this.lastRenderTime = Date.now(); - - for (const stream of this.reporter.getStreams(false)) { - if (stream.format === 'ansi') { - stream.write(ansiEscapes.cursorTo(0)); - stream.write(this.buildBar(stream)); - } - } - } + // TODO fetch approximate total and eta based on `name` + } + + processRemoteClientMessage(msg: RemoteReporterClientMessage) { + switch (msg.type) { + case 'PROGRESS_SET_CURRENT': + return this.setCurrent(msg.current); + + case 'PROGRESS_SET_TOTAL': + return this.setTotal(msg.total, msg.approximate); + + case 'PROGRESS_SET_TEXT': + return this.setText(msg.text); + + case 'PROGRESS_PUSH_TEXT': + return this.pushText(msg.text); + + case 'PROGRESS_POP_TEXT': + return this.popText(msg.text); + + case 'PROGRESS_SET_APPROXIMATE_ETA': + return this.setApproximateETA(msg.duration); + + case 'PROGRESS_TICK': + return this.tick(); + + case 'PROGRESS_END': + return this.end(); + + case 'PROGRESS_RESUME': + return this.resume(); + + case 'PROGRESS_PAUSE': + return this.pause(); + } + } + + getElapsedTime(): number { + return Date.now() - this.startTime - this.pausedElapsed; + } + + getBouncerPosition(stream: ReporterStream): number { + const start = this.streamToBouncerStart.get(stream); + if (start === undefined) { + return 0; + } else { + return start; + } + } + + startBouncer() { + const queueTick = () => { + this.bouncerTimer = setTimeout(tick, BOUNCER_INTERVAL); + }; + + const tick = this.reporter.wrapCallback(() => { + if (this.paused) { + queueTick(); + return; + } + + const elapsedTime = this.getElapsedTime(); + const elapsedFrames = Math.round(elapsedTime / BOUNCER_INTERVAL); + + for (const stream of this.reporter.streams) { + // We remove the bouncer width from the total columns since we'll append it + const width = stream.columns - BOUNCER_WIDTH; + + // Position to place the bouncer + let position = elapsedFrames % width; + + // Every odd complete bounce should reverse direction + const totalBounces = Math.floor(elapsedFrames / width); + if (totalBounces % 2 === 1) { + position = width - position; + } + + this.streamToBouncerStart.set(stream, position); + } + + queueTick(); + this.render(); + }); + + queueTick(); + } + + setCurrent(current: number) { + if (this.closed) { + return; + } + + super.setCurrent(current); + + if (this.isRenderDue()) { + this.render(); + } + } + + setTotal(total: number, approximate: boolean = false) { + super.setTotal(total, approximate); + this.renderEvery = Math.round(total / 100); + this.endBouncer(); + } + + setText(text: string) { + if (this.closed) { + return; + } + + super.setText(text); + } + + queueRender(delay: number = this.delay) { + if (this.closed) { + // Progress bar has been removed + return; + } + + if (this.renderTimer !== undefined) { + // Render already queued + return; + } + + this.renderTimer = setTimeout( + this.reporter.wrapCallback(() => { + this.render(); + }), + delay, + ); + } + + endBouncer() { + if (this.bouncerTimer !== undefined) { + clearTimeout(this.bouncerTimer); + } + this.bouncerTimer = undefined; + } + + endRender() { + if (this.renderTimer !== undefined) { + clearTimeout(this.renderTimer); + } + this.renderTimer = undefined; + } + + end() { + this.closed = true; + this.endBouncer(); + this.endRender(); + this.reporter.clearLineAll(); + + if (this.onEnd !== undefined) { + this.onEnd(); + } + } + + // Ensure that we update the progress bar after a certain amount of ticks + + // This allows us to use the progress bar for sync work where the event loop is always blocked + isRenderDue(): boolean { + const isDue: boolean = + this.current > this.lastRenderCurrent + this.renderEvery; + if (isDue) { + // We also make sure that we never force update more often than once a second + // This is to ensure that the progress bar isn't negatively effecting performance + const timeSinceLastRender: number = Date.now() - this.lastRenderTime; + return timeSinceLastRender > 1_000; + } else { + return false; + } + } + + isBoldCharacter(i: number, ranges: BoldRanges): boolean { + for (const [start, end] of ranges) { + if (start >= i && end <= i) { + return true; + } + } + + return false; + } + + splitCharacters(str: string, boldRanges: BoldRanges): SplitBar { + return str.split('').map((char, i) => { + if (this.isBoldCharacter(i, boldRanges)) { + return [i, formatAnsi.bold(char)]; + } else { + return [i, char]; + } + }); + } + + buildProgressBouncer(stream: ReporterStream, bar: SplitBar): string { + let start = this.getBouncerPosition(stream); + let fullBar = ''; + for (const [i, char] of bar) { + const isBounce = i >= start && i < start + BOUNCER_WIDTH; + + if (isBounce) { + if (this.paused) { + fullBar += formatAnsi.inverse(char); + } else { + fullBar += formatAnsi.white(formatAnsi.bgYellow(char)); + } + } else { + fullBar += char; + } + } + return fullBar; + } + + buildProgressBar(stream: ReporterStream, bar: SplitBar, total: number): string { + const ratio = Math.min(Math.max(this.current / total, 0), 1); + + const completeLength = Math.round(stream.columns * ratio); + let fullBar = ''; + for (const [i, char] of bar) { + if (i < completeLength) { + if (this.paused) { + fullBar += formatAnsi.inverse(char); + } else { + fullBar += formatAnsi.white(formatAnsi.bgGreen(char)); + } + } else { + fullBar += char; + } + } + return fullBar; + } + + buildBar(stream: ReporterStream) { + const {total, current, title} = this; + + // Text ranges that we should make bold + const boldRanges: BoldRanges = []; + + // Text to prefix to the bar + let prefix = ''; + if (title !== undefined) { + prefix += title; + + // Only the title should be bold, not the subtext + boldRanges.push([0, prefix.length - 1]); + } + + const text = this.getText(); + if (text !== undefined) { + // Separate a title and it's text with a colon + if (title !== undefined) { + prefix += ': '; + } + prefix += text; + } + + // Text to put at the end of the bar + let suffix = ''; + + // Total time since the progress bar was created + const elapsed = this.getElapsedTime(); + + // Time elapsed eg: elapsed 1m5s + if (this.opts.elapsed) { + suffix += `elapsed ${humanizeTime(elapsed)} `; + } + + // Don't bother with a suffix if we haven't completed a single item + if (current > 0) { + // How many milliseconds spent per total items + const averagePerItem = elapsed / current; + + // ETA eg: 1m5s + if (this.opts.eta) { + if (this.approximateETA !== undefined && elapsed < this.approximateETA) { + // Approximate ETA + const left = elapsed - this.approximateETA; + suffix += `eta ~${humanizeTime(left)} `; + } else if (total !== undefined) { + // How many items we have left + const itemsLeft = total - current; + + // Total estimated time left + const eta = itemsLeft * averagePerItem; + suffix += `eta ${humanizeTime(eta)} `; + } else { + const ops = Math.round(1_000 / averagePerItem); + suffix += `${humanizeNumber(ops)} op/s `; + } + } + + // Counter eg: 5/100 + suffix += `${humanizeNumber(current)}`; + if (total !== undefined) { + suffix += '/'; + if (this.approximateTotal) { + suffix += '~'; + } + suffix += humanizeNumber(total); + } + } + + // Get the full width of the bar. We take off 3 for padding. + const width = stream.columns - 3; + + // The amount of spaces to put between the title and counter + const spacerLength = Math.max(0, width - prefix.length - suffix.length); + const spacer = ' '.repeat(spacerLength); + + // Trim the prefix if it will overflow + prefix = prefix.slice(0, width - spacerLength - suffix.length); + + // The full raw bar without any coloring + const raw = ` ${prefix}${spacer} ${suffix}`; + + // Make sure the counter is bold + boldRanges.push([raw.length - suffix.length, raw.length - 1]); + + // Split the raw bar into an array of formatted characters + const chars = this.splitCharacters(raw, boldRanges); + + if (total === undefined) { + return this.buildProgressBouncer(stream, chars); + } else { + return this.buildProgressBar(stream, chars, total); + } + } + + render() { + if (this.closed) { + return; + } + + this.endRender(); + + this.lastRenderCurrent = this.current; + this.lastRenderTime = Date.now(); + + for (const stream of this.reporter.getStreams(false)) { + if (stream.format === 'ansi') { + stream.write(ansiEscapes.cursorTo(0)); + stream.write(this.buildBar(stream)); + } + } + } } diff --git a/packages/@romejs/cli-reporter/ProgressBase.ts b/packages/@romejs/cli-reporter/ProgressBase.ts index c0f5745df8d..e776746d1ff 100644 --- a/packages/@romejs/cli-reporter/ProgressBase.ts +++ b/packages/@romejs/cli-reporter/ProgressBase.ts @@ -9,144 +9,144 @@ import Reporter from './Reporter'; import {ReporterProgress, ReporterProgressOptions} from './types'; const DEFAULT_PROGRESS_OPTIONS: ReporterProgressOptions = { - name: undefined, - title: undefined, - initDelay: undefined, - elapsed: true, - eta: true, - persistent: false, + name: undefined, + title: undefined, + initDelay: undefined, + elapsed: true, + eta: true, + persistent: false, }; export default class ProgressBase implements ReporterProgress { - constructor(reporter: Reporter, opts: ReporterProgressOptions = {}) { - this.total = undefined; - this.reporter = reporter; - this.current = 0; - - this.approximateTotal = false; - this.approximateETA = undefined; - - this.textStack = []; - this.text = undefined; - this.title = - opts.title === undefined ? undefined : reporter.stripMarkup(opts.title); - - this.paused = false; - this.pausedStart = undefined; - this.pausedElapsed = 0; - - this.opts = { - ...DEFAULT_PROGRESS_OPTIONS, - ...opts, - }; - } - - reporter: Reporter; - opts: ReporterProgressOptions; - - current: number; - total: undefined | number; - - text: undefined | string; - textStack: Array; - title: undefined | string; - - approximateETA: undefined | number; - approximateTotal: boolean; - - pausedStart: undefined | number; - pausedElapsed: number; - paused: boolean; - - setCurrent(current: number) { - this.current = current; - this.queueRender(); - - // Progress complete - if ( - this.total !== undefined && - this.current >= this.total && - !this.opts.persistent - ) { - this.end(); - } - } - - getText(): undefined | string { - const {text} = this; - if (text === undefined || text === '') { - return undefined; - } else { - return this.reporter.stripMarkup(text); - } - } - - setText(text: string) { - this.text = text; - this.queueRender(); - } - - setApproximateETA(duration: number) { - this.approximateETA = duration; - } - - setTotal(total: number, approximate: boolean = false) { - this.total = total; - this.approximateTotal = approximate; - this.queueRender(); - } - - pushText(text: string) { - this.setText(text); - this.textStack.push(text); - } - - popText(text: string) { - // Find - const {textStack} = this; - const index = textStack.indexOf(text); - if (index === -1) { - throw new Error(`No pushed text: ${text}`); - } - - // Remove - textStack.splice(index, 1); - - // Set last - const last: undefined | string = textStack[textStack.length - 1]; - this.setText(last === undefined ? '' : last); - } - - tick() { - this.setCurrent(this.current + 1); - } - - resume() { - if (!this.paused || this.pausedStart === undefined) { - return; - } - - this.pausedElapsed += Date.now() - this.pausedStart; - this.pausedStart = undefined; - this.paused = false; - this.render(); - } - - pause() { - if (this.paused) { - return; - } - - this.pausedStart = Date.now(); - this.paused = true; - this.render(); - } - - queueRender() { - this.render(); - } - - end() {} - - render() {} + constructor(reporter: Reporter, opts: ReporterProgressOptions = {}) { + this.total = undefined; + this.reporter = reporter; + this.current = 0; + + this.approximateTotal = false; + this.approximateETA = undefined; + + this.textStack = []; + this.text = undefined; + this.title = + opts.title === undefined ? undefined : reporter.stripMarkup(opts.title); + + this.paused = false; + this.pausedStart = undefined; + this.pausedElapsed = 0; + + this.opts = { + ...DEFAULT_PROGRESS_OPTIONS, + ...opts, + }; + } + + reporter: Reporter; + opts: ReporterProgressOptions; + + current: number; + total: undefined | number; + + text: undefined | string; + textStack: Array; + title: undefined | string; + + approximateETA: undefined | number; + approximateTotal: boolean; + + pausedStart: undefined | number; + pausedElapsed: number; + paused: boolean; + + setCurrent(current: number) { + this.current = current; + this.queueRender(); + + // Progress complete + if ( + this.total !== undefined && + this.current >= this.total && + !this.opts.persistent + ) { + this.end(); + } + } + + getText(): undefined | string { + const {text} = this; + if (text === undefined || text === '') { + return undefined; + } else { + return this.reporter.stripMarkup(text); + } + } + + setText(text: string) { + this.text = text; + this.queueRender(); + } + + setApproximateETA(duration: number) { + this.approximateETA = duration; + } + + setTotal(total: number, approximate: boolean = false) { + this.total = total; + this.approximateTotal = approximate; + this.queueRender(); + } + + pushText(text: string) { + this.setText(text); + this.textStack.push(text); + } + + popText(text: string) { + // Find + const {textStack} = this; + const index = textStack.indexOf(text); + if (index === -1) { + throw new Error(`No pushed text: ${text}`); + } + + // Remove + textStack.splice(index, 1); + + // Set last + const last: undefined | string = textStack[textStack.length - 1]; + this.setText(last === undefined ? '' : last); + } + + tick() { + this.setCurrent(this.current + 1); + } + + resume() { + if (!this.paused || this.pausedStart === undefined) { + return; + } + + this.pausedElapsed += Date.now() - this.pausedStart; + this.pausedStart = undefined; + this.paused = false; + this.render(); + } + + pause() { + if (this.paused) { + return; + } + + this.pausedStart = Date.now(); + this.paused = true; + this.render(); + } + + queueRender() { + this.render(); + } + + end() {} + + render() {} } diff --git a/packages/@romejs/cli-reporter/Reporter.ts b/packages/@romejs/cli-reporter/Reporter.ts index e680a4a5816..dc28e31477e 100644 --- a/packages/@romejs/cli-reporter/Reporter.ts +++ b/packages/@romejs/cli-reporter/Reporter.ts @@ -6,23 +6,23 @@ */ import { - MarkupFormatOptions, - ansiEscapes, - markupTag, - markupToAnsi, - markupToPlainText, + MarkupFormatOptions, + ansiEscapes, + markupTag, + markupToAnsi, + markupToPlainText, } from '@romejs/string-markup'; import { - RemoteReporterClientMessage, - RemoteReporterReceiveMessage as RemoteReporterServerMessage, - ReporterDerivedStreams, - ReporterProgress, - ReporterProgressOptions, - ReporterStream, - ReporterStreamMeta, - ReporterTableField, - SelectArguments, - SelectOptions, + RemoteReporterClientMessage, + RemoteReporterReceiveMessage as RemoteReporterServerMessage, + ReporterDerivedStreams, + ReporterProgress, + ReporterProgressOptions, + ReporterStream, + ReporterStreamMeta, + ReporterTableField, + SelectArguments, + SelectOptions, } from './types'; import {removeSuffix} from '@romejs/string-utils'; import Progress from './Progress'; @@ -32,1248 +32,1235 @@ import {CWD_PATH} from '@romejs/path'; import {Event} from '@romejs/events'; import readline = require('readline'); import { - MarkupFormatGridOptions, - MarkupTagName, + MarkupFormatGridOptions, + MarkupTagName, } from '@romejs/string-markup/types'; import select from './select'; import {MarkupLinesAndWidth} from '@romejs/string-markup/format'; import {onKeypress} from './util'; type ListOptions = { - reverse?: boolean; - truncate?: number; - ordered?: boolean; - start?: number; + reverse?: boolean; + truncate?: number; + ordered?: boolean; + start?: number; }; // rome-ignore lint/noExplicitAny type WrapperFactory = ) => any>(callback: T) => T; export type ReporterOptions = { - streams?: Array; - stdin?: NodeJS.ReadStream; - programName?: string; - hasClearScreen?: boolean; - programVersion?: string; - markupOptions?: MarkupFormatOptions; - disabled?: boolean; - verbose?: boolean; - useRemoteProgressBars?: boolean; - startTime?: number; - wrapperFactory?: WrapperFactory; + streams?: Array; + stdin?: NodeJS.ReadStream; + programName?: string; + hasClearScreen?: boolean; + programVersion?: string; + markupOptions?: MarkupFormatOptions; + disabled?: boolean; + verbose?: boolean; + useRemoteProgressBars?: boolean; + startTime?: number; + wrapperFactory?: WrapperFactory; }; export type LogOptions = { - nonTTY?: string; - noPrefix?: boolean; - stderr?: boolean; - newline?: boolean; + nonTTY?: string; + noPrefix?: boolean; + stderr?: boolean; + newline?: boolean; }; export type LogCategoryOptions = LogOptions & { - unicodePrefix: string; - rawPrefix: string; - markupTag: MarkupTagName; + unicodePrefix: string; + rawPrefix: string; + markupTag: MarkupTagName; }; type QuestionValidateResult = [false, string] | [true, T]; type QuestionOptions = { - hint?: string; - default?: string; - yes?: boolean; - normalize?: (value: string) => string; + hint?: string; + default?: string; + yes?: boolean; + normalize?: (value: string) => string; }; let remoteProgressIdCounter = 0; -const INDENT = ' '; - type Stdout = stream.Writable & { - isTTY?: boolean; - columns?: number; + isTTY?: boolean; + columns?: number; }; function getStreamFormat(stdout: undefined | Stdout): ReporterStream['format'] { - return stdout !== undefined && stdout.isTTY === true ? 'ansi' : 'none'; + return stdout !== undefined && stdout.isTTY === true ? 'ansi' : 'none'; } export default class Reporter { - constructor(opts: ReporterOptions = {}) { - this.programName = - opts.programName === undefined ? 'rome' : opts.programName; - this.programVersion = opts.programVersion; - - this.noProgress = process.env.CI === '1'; - this.isVerbose = Boolean(opts.verbose); - - this.startTime = opts.startTime === undefined ? Date.now() : opts.startTime; - this.hasClearScreen = - opts.hasClearScreen === undefined ? true : opts.hasClearScreen; - this.activeElements = new Set(); - this.indentLevel = 0; - this.indentString = ''; - this.enabled = opts.disabled === true ? 0 : 1; - this.markupOptions = - opts.markupOptions === undefined ? {} : opts.markupOptions; - this.streamsWithDoubleNewlineEnd = new Set(); - this.streamsWithNewlineEnd = new Set(); - this.shouldRedirectOutToErr = false; - this.stdin = opts.stdin; - - this.wrapperFactory = opts.wrapperFactory; - - this.remoteClientProgressBars = new Map(); - this.remoteServerProgressBars = new Map(); - - this.sendRemoteServerMessage = new Event({ - name: 'sendRemoteServerMessage', - }); - this.sendRemoteClientMessage = new Event({ - name: 'sendRemoteClientMessage', - }); - - this.isRemote = opts.useRemoteProgressBars === true; - - this.outStreams = new Set(); - this.errStreams = new Set(); - this.streams = new Set(); - - if (opts.streams !== undefined) { - for (const stream of opts.streams) { - this.addStream(stream); - } - } - } - - static DEFAULT_COLUMNS = 100; - - attachStdoutStreams(stdout?: Stdout, stderr?: Stdout): ReporterDerivedStreams { - const columns = - stdout === undefined || stdout.columns === undefined - ? Reporter.DEFAULT_COLUMNS - : stdout.columns; - - const columnsUpdated: Event = new Event({ - name: 'columnsUpdated', - }); - - // Windows terminals are awful - const unicode = process.platform !== 'win32'; - - const outStream: ReporterStream = { - type: 'out', - format: getStreamFormat(stdout), - columns, - unicode, - write(chunk) { - if (stdout !== undefined) { - stdout.write(chunk); - } - }, - teardown() {}, - }; - - const errStream: ReporterStream = { - type: 'error', - format: getStreamFormat(stderr), - columns, - unicode, - write(chunk) { - if (stderr !== undefined) { - stderr.write(chunk); - } - }, - }; - - // Watch for resizing - if (outStream.format === 'ansi' && stdout !== undefined) { - const onStdoutResize = () => { - if (stdout?.columns !== undefined) { - const {columns} = stdout; - columnsUpdated.send(columns); - this.setStreamColumns([outStream, errStream], columns); - } - }; - - outStream.teardown = () => { - stdout.off('resize', onStdoutResize); - }; - - stdout.on('resize', onStdoutResize); - } - - this.addStream(outStream); - this.addStream(errStream); - - return { - columnsUpdated, - stdout: outStream, - stderr: errStream, - }; - } - - attachCaptureStream( - format: ReporterStream['format'] = 'none', - ): { - read: () => string; - remove: () => void; - } { - let buff = ''; - - const stream: ReporterStream = { - format, - type: 'all', - columns: Reporter.DEFAULT_COLUMNS, - unicode: true, - write(chunk) { - buff += chunk; - }, - }; - - this.addStream(stream); - - return { - read() { - return buff; - }, - remove: () => { - this.removeStream(stream); - }, - }; - } - - static fromProcess(opts: ReporterOptions = {}): Reporter { - const reporter = new Reporter({ - ...opts, - markupOptions: { - cwd: CWD_PATH, - ...opts.markupOptions, - }, - }); - - reporter.attachStdoutStreams(process.stdout, process.stderr); - - return reporter; - } - - programName: string; - programVersion: string | undefined; - markupOptions: MarkupFormatOptions; - - isRemote: boolean; - noProgress: boolean; - isVerbose: boolean; - streamsWithNewlineEnd: Set; - streamsWithDoubleNewlineEnd: Set; - indentLevel: number; - indentString: string; - enabled: number; - startTime: number; - shouldRedirectOutToErr: boolean; - wrapperFactory: undefined | WrapperFactory; - outStreams: Set; - errStreams: Set; - streams: Set; - sendRemoteServerMessage: Event; - sendRemoteClientMessage: Event; - stdin: undefined | NodeJS.ReadStream; - - remoteClientProgressBars: Map; - remoteServerProgressBars: Map< - string, - { - end: () => void; - } - >; - - // track whether we've output anything, we need this to avoid outputting multiple spacers etc - hasClearScreen: boolean; - - //Store active progress indicators so we can easily bail out and destroy them - activeElements: Set<{ - render: () => void; - end: () => void; - }>; - - processRemoteClientMessage(msg: RemoteReporterClientMessage) { - if (msg.type === 'PROGRESS_CREATE') { - this.remoteClientProgressBars.set( - msg.id, - this.progressLocal( - msg.opts, - () => { - this.sendRemoteServerMessage.call({ - type: 'ENDED', - id: msg.id, - }); - }, - ), - ); - return; - } - - let bar = this.remoteClientProgressBars.get(msg.id); - if (bar === undefined) { - throw new Error( - `Remote reporter message for progress bar ${msg.id} that does not exist`, - ); - } - - bar.processRemoteClientMessage(msg); - - if (msg.type === 'PROGRESS_END') { - this.remoteClientProgressBars.delete(msg.id); - } - } - - receivedRemoteServerMessage(msg: RemoteReporterServerMessage) { - // Currently the only message a remote Reporter can send is that it has ended - switch (msg.type) { - case 'ENDED': { - const progress = this.remoteServerProgressBars.get(msg.id); - if (progress !== undefined) { - progress.end(); - } - } - } - } - - getMessagePrefix(): string { - return ''; - } - - normalizeMessage( - stream: ReporterStream, - tty: string, - opts: LogOptions, - ): string { - let msg = - stream.format !== 'none' || opts.nonTTY === undefined ? tty : opts.nonTTY; - - if (opts.noPrefix !== true) { - msg = this.getMessagePrefix() + msg; - } - - // Don't indent if there is no indent, or the message is empty - const {indentString} = this; - if ( - this.streamsWithNewlineEnd.has(stream) && - indentString !== '' && - msg !== '' - ) { - // Indent each line, leaving out the indentation for empty lines - msg = indentString + msg.replace(/\n([^\n])/g, `\n${indentString}$1`); - } - - return msg; - } - - redirectOutToErr(should: boolean): boolean { - const old = this.shouldRedirectOutToErr; - this.shouldRedirectOutToErr = should; - return old; - } - - setStreamColumns(streams: Array, columns: number) { - for (const stream of streams) { - if (!this.streams.has(stream)) { - throw new Error( - "Trying to setStreamColumns on a stream that isn't attached to this Reporter", - ); - } - - stream.columns = columns; - } - - for (const elem of this.activeElements) { - elem.render(); - } - } - - addStream(stream: ReporterStream) { - this.streamsWithNewlineEnd.add(stream); - this.streams.add(stream); - - if (stream.type === 'error' || stream.type === 'all') { - this.errStreams.add(stream); - } - - if (stream.type === 'out' || stream.type === 'all') { - this.outStreams.add(stream); - } - } - - removeStream(stream: ReporterStream) { - if (stream.teardown !== undefined) { - stream.teardown(); - } - this.streams.delete(stream); - this.outStreams.delete(stream); - this.errStreams.delete(stream); - } - - //# Stdin - getStdin(): NodeJS.ReadStream { - const {stdin} = this; - if (stdin === undefined) { - throw new Error('This operation expected a stdin but we have none'); - } - return stdin; - } - - async question( - message: string, - {hint, default: def = '', yes = false}: QuestionOptions = {}, - ): Promise { - if (yes) { - return def; - } - - const stdin = this.getStdin(); - - const origPrompt = `? ${message}`; - let prompt = origPrompt; - if (hint !== undefined) { - prompt += ` ${hint}`; - } - if (def !== '') { - prompt += ` (${def})`; - } - prompt += ': '; - this.logAll( - prompt, - { - newline: false, - }, - ); - - const rl = readline.createInterface({ - input: stdin, - output: new stream.Writable({ - write: (chunk, encoding, callback) => { - this.writeAll(chunk); - callback(); - }, - }), - terminal: false, - }); - - return new Promise((resolve) => { - rl.on( - 'line', - (line) => { - rl.close(); - - const normalized = line === '' ? def : line; - - // Replace initial prompt - this.writeAll(ansiEscapes.cursorUp()); - this.writeAll(ansiEscapes.eraseLine); - - let prompt = origPrompt; - prompt += ': '; - if (normalized === '') { - prompt += 'empty'; - } else { - prompt += normalized; - } - this.logAll(prompt); - - resolve(normalized); - }, - ); - }); - } - - async questionValidate( - message: string, - validate: (value: string) => QuestionValidateResult, - options: Omit = {}, - ): Promise { - while (true) { - let res: undefined | QuestionValidateResult; - - await this.question( - `${message}`, - { - ...options, - normalize: (value: string): string => { - res = validate(value); - - if (res[0] === true && typeof res[1] === 'string') { - return res[1]; - } else { - return value; - } - }, - }, - ); - - if (res === undefined) { - throw new Error('normalize should have been called'); - } - - if (res[0] === false) { - this.error(res[1]); - continue; - } else { - return res[1]; - } - } - } - - async radioConfirm(message: string): Promise { - const answer = await this.radio( - message, - { - options: { - yes: { - label: 'Yes', - shortcut: 'y', - }, - no: { - label: 'No', - shortcut: 'n', - }, - }, - }, - ); - return answer === 'yes'; - } - - async confirm(message: string = 'Press any key to continue'): Promise { - this.logAll(`${message}`, {newline: false}); - - await new Promise((resolve) => { - const keypress = onKeypress( - this, - () => { - keypress.finish(); - resolve(); - }, - ); - }); - - // Newline - this.logAll(''); - } - - async radio( - message: string, - arg: SelectArguments, - ): Promise { - const set = await this.select(message, {...arg, radio: true}); - - // Should always have at least one element - return Array.from(set)[0]; - } - - async select( - message: string, - args: SelectArguments, - ): Promise> { - return select(this, message, args); - } - - //# Control - isEnabled(stderr: undefined | boolean): boolean { - return this.getStreams(stderr).size > 0; - } - - getStreams(stderr: undefined | boolean): Set { - if (this.enabled === 0) { - return new Set(); - } - - if (this.shouldRedirectOutToErr) { - return this.errStreams; - } - - if (stderr) { - return this.errStreams; - } - - return this.outStreams; - } - - enable(): () => void { - let alreadyDisabled = false; - - this.enabled++; - - return () => { - if (alreadyDisabled) { - throw new Error('Already disabled Reporter'); - } - - this.enabled--; - alreadyDisabled = true; - }; - } - - //# LIFECYCLE - teardown() { - for (const stream of this.streams) { - this.removeStream(stream); - } - - for (const elem of this.activeElements) { - elem.end(); - } - this.activeElements.clear(); - } - - fork(opts: Partial = {}) { - return new Reporter({ - streams: [...this.streams], - verbose: this.isVerbose, - markupOptions: this.markupOptions, - wrapperFactory: this.wrapperFactory, - ...opts, - }); - } - - //# INDENTATION METHODS - indent(callback: () => void) { - this.indentLevel++; - this.updateIndent(); - - callback(); - this.indentLevel--; - this.updateIndent(); - } - - noIndent(callback: () => void) { - const prevIndentLevel = this.indentLevel; - this.indentLevel = 0; - this.updateIndent(); - callback(); - this.indentLevel = prevIndentLevel; - this.updateIndent(); - } - - updateIndent() { - this.indentString = INDENT.repeat(this.indentLevel); - } - - //# INTERNAL - prependEmoji( - stream: ReporterStream, - msg: string, - emoji: string, - fallback?: string, - ): string { - if (stream.format === 'none') { - return `${emoji} ${msg}`; - } else { - if (fallback === undefined) { - return msg; - } else { - return `${fallback} ${msg}`; - } - } - } - - //# VISUALISATION - table( - head: Array, - rawBody: Array>, - ) { - let body = ''; - - if (head.length > 0) { - body += ''; - for (const field of head) { - body += `${field}`; - } - body += ''; - } - - for (const row of rawBody) { - body += ''; - for (let field of row) { - if (typeof field === 'string' || typeof field === 'number') { - field = {align: 'left', value: field}; - } - - let {value, align} = field; - if (typeof value === 'number') { - value = `${value}`; - } - body += `${value}`; - } - body += ''; - } - - this.logAll(`${body}
`); - } - - verboseInspect(val: unknown) { - if (this.isVerbose) { - this.inspect(val); - } - } - - inspect(value: unknown) { - if (!this.isEnabled(false)) { - return; - } - - let formatted = value; - - if (typeof formatted !== 'number' && typeof formatted !== 'string') { - formatted = prettyFormat(formatted, {markup: true}); - } - - this.logAll(String(formatted)); - } - - //# ESCAPE HATCHES - clearLineAll() { - for (const stream of this.getStreams(false)) { - this.clearLineSpecific(stream); - } - } - - clearLineSpecific(stream: ReporterStream) { - stream.write(ansiEscapes.eraseLine); - stream.write(ansiEscapes.cursorTo(0)); - } - - writeAll(msg: string, opts: LogOptions = {}) { - for (const stream of this.getStreams(opts.stderr)) { - this.writeSpecific(stream, msg, opts); - } - } - - writeSpecific(stream: ReporterStream, msg: string, opts: LogOptions = {}) { - if (!this.isEnabled(opts.stderr)) { - return; - } - - this.hasClearScreen = false; - - if (stream.format === 'ansi' && this.activeElements.size > 0) { - // A progress bar is active and has probably drawn to the screen - this.clearLineSpecific(stream); - } - - stream.write(msg); - } - - //# UTILITIES - getTotalTime(): number { - return Date.now() - this.startTime; - } - - clearScreen() { - for (const stream of this.getStreams(false)) { - if (stream.format === 'ansi') { - stream.write(ansiEscapes.clearScreen); - } - } - this.hasClearScreen = true; - } - - //# SECTIONS - heading(text: string) { - this.br(); - this.logAll( - `${text}`, - { - nonTTY: `# ${text}`, - }, - ); - this.br(); - } - - section(title: undefined | string, callback: () => void) { - this.hr(title === undefined ? undefined : `${title}`); - this.indent(() => { - callback(); - this.br(); - }); - } - - hr(text: string = '') { - const {hasClearScreen} = this; - - this.br(); - - if (hasClearScreen && text === undefined) { - return; - } - - this.logAll(`
${text}`); - this.br(); - } - - async steps( - callbacks: Array<{ - message: string; - callback: () => Promise; - clear?: boolean; - }>, - ) { - const total = callbacks.length; - let current = 1; - for (const {clear, message, callback} of callbacks) { - this.step(current, total, message); - - if (clear) { - this.hasClearScreen = true; - } - - await callback(); - current++; - - // If a step doesn't produce any output, or just progress bars that are cleared, we can safely remove the previous `step` message line - if (clear && this.hasClearScreen) { - for (const stream of this.getStreams(false)) { - if (stream.format === 'ansi') { - stream.write(ansiEscapes.cursorTo(0)); - stream.write(ansiEscapes.cursorUp()); - stream.write(ansiEscapes.eraseLine); - } - } - } - } - } - - step(current: number, total: number, msg: string) { - if (msg.endsWith('?')) { - msg = `${removeSuffix(msg, '?')}...?`; - } else { - msg += '...'; - } - - this.logAll(`[${current}/${total}] ${msg}`); - } - - br(force: boolean = false) { - for (const stream of this.getStreams(false)) { - if (!this.streamsWithDoubleNewlineEnd.has(stream) || force) { - this.logOne(stream, ''); - } - } - } - - wrapCallback: WrapperFactory = (callback) => { - const {wrapperFactory} = this; - if (wrapperFactory === undefined) { - return callback; - } else { - return wrapperFactory(callback); - } - }; - - stripMarkup(str: string): string { - return markupToPlainText(str, this.markupOptions).lines.join('\n'); - } - - markupify( - stream: ReporterStreamMeta, - str: string, - viewportShrink: number = 0, - ): MarkupLinesAndWidth { - if (str === '') { - return {lines: [''], width: 0}; - } - - const gridMarkupOptions: MarkupFormatGridOptions = { - ...this.markupOptions, - columns: stream.columns - this.indentString.length - viewportShrink, - }; - - switch (stream.format) { - case 'ansi': - return markupToAnsi(str, gridMarkupOptions); - - case 'html': - // TODO - return markupToPlainText(str, gridMarkupOptions); - - case 'none': - return markupToPlainText(str, gridMarkupOptions); - - case 'markup': - return { - width: 0, - lines: [str], - }; - } - } - - logAll(tty: string, opts: LogOptions = {}) { - for (const stream of this.getStreams(opts.stderr)) { - this.logOne(stream, tty, opts); - } - } - - logAllNoMarkup(tty: string, opts: LogOptions = {}) { - for (const stream of this.getStreams(opts.stderr)) { - this.logOneNoMarkup(stream, tty, opts); - } - } - - logOne(stream: ReporterStream, tty: string, opts: LogOptions = {}) { - const msg = - stream.format !== 'none' || opts.nonTTY === undefined ? tty : opts.nonTTY; - const {lines} = this.markupify(stream, msg); - for (const line of lines) { - this.logOneNoMarkup(stream, line, opts); - } - } - - logOneNoMarkup(stream: ReporterStream, tty: string, opts: LogOptions = {}) { - if (!this.isEnabled(opts.stderr)) { - return; - } - - let msg = this.normalizeMessage(stream, tty, opts); - if (opts.newline !== false) { - msg += '\n'; - } - - // Track if there's going to be a completely empty line - const hasDoubleNewline = msg === '\n' || msg.endsWith('\n\n'); - if (hasDoubleNewline) { - this.streamsWithDoubleNewlineEnd.add(stream); - } else { - this.streamsWithDoubleNewlineEnd.delete(stream); - } - if (msg.endsWith('\n')) { - this.streamsWithNewlineEnd.add(stream); - } else { - this.streamsWithNewlineEnd.delete(stream); - } - - this.writeSpecific(stream, msg, opts); - } - - logAllWithCategory( - rawInner: string, - args: Array, - opts: LogCategoryOptions, - ) { - if (!this.isEnabled(opts.stderr)) { - return; - } - - const inner = markupTag(opts.markupTag, rawInner); - - for (const stream of this.getStreams(opts.stderr)) { - // Format the prefix, selecting it depending on if we're a unicode stream - const prefixInner = stream.unicode ? opts.unicodePrefix : opts.rawPrefix; - const prefix = markupTag( - 'emphasis', - markupTag(opts.markupTag, this.getMessagePrefix() + prefixInner), - ); - - // Should only be one line - const {lines: prefixLines, width: prefixWidth} = this.markupify( - stream, - prefix, - ); - const prefixLine = prefixLines[0]; - if (prefixLines.length !== 1) { - throw new Error('Expected 1 prefix line'); - } - - const {lines} = this.markupify(stream, inner, prefixWidth); - for (let i = 0; i < lines.length; i++) { - let line = lines[i]; - if (i === 0) { - line = `${prefixLine}${line}`; - } else { - line = `${' '.repeat(prefixWidth)}${line}`; - } - this.logOneNoMarkup( - stream, - line, - { - noPrefix: true, - ...opts, - }, - ); - } - } - } - - success(msg: string, ...args: Array) { - this.logAllWithCategory( - msg, - args, - { - unicodePrefix: '\u2714 ', - rawPrefix: '\u221a ', - markupTag: 'success', - }, - ); - } - - error(msg: string, ...args: Array) { - this.logAllWithCategory( - msg, - args, - { - markupTag: 'error', - unicodePrefix: '\u2716 ', - rawPrefix: '\xd7 ', - stderr: true, - }, - ); - } - - errorObj(err: Error) { - this.error(err.stack || err.message || err.name || 'Unknown Error'); - } - - info(msg: string, ...args: Array) { - this.logAllWithCategory( - msg, - args, - { - unicodePrefix: '\u2139 ', - rawPrefix: 'i ', - markupTag: 'info', - }, - ); - } - - warn(msg: string, ...args: Array) { - this.logAllWithCategory( - msg, - args, - { - unicodePrefix: '\u26a0 ', - rawPrefix: '! ', - markupTag: 'warn', - stderr: true, - }, - ); - } - - verbose(msg: string, ...args: Array) { - if (this.isVerbose) { - this.verboseForce(msg, args); - } - } - - verboseForce(msg: string, ...args: Array) { - this.logAllWithCategory( - msg, - args, - { - unicodePrefix: '\u26a1 ', - rawPrefix: '* ', - markupTag: 'dim', - }, - ); - } - - command(command: string) { - this.logAll( - `$ ${command}`, - { - nonTTY: `$ ${command}`, - }, - ); - } - - //# LISTS - _getListIndentation(): string { - // If we're at the top level then add some implicit indentation - return this.indentLevel === 0 ? ' ' : ''; - } - - processedList( - items: Array, - callback: (reporter: Reporter, item: T) => void, - opts: ListOptions = {}, - ): { - truncated: boolean; - } { - if (items.length === 0) { - // We make some assumptions that there's at least one item - return {truncated: false}; - } - - let truncatedCount = 0; - - let start = opts.start || 0; - if (opts.truncate !== undefined && items.length > opts.truncate) { - truncatedCount = items.length - opts.truncate; - items = items.slice(0, opts.truncate); - start += truncatedCount; - } - - let buff = ''; - - for (const item of items) { - const reporter = this.fork({ - streams: [], - }); - const stream = reporter.attachCaptureStream('markup'); - callback(reporter, item); - stream.remove(); - buff += `
  • ${stream.read()}
  • `; - } - - if (opts.ordered) { - this.logAll(markupTag('ol', buff, {start, reversed: opts.reverse})); - } else { - this.logAll(`
      ${buff}
    `); - } - - if (truncatedCount > 0) { - this.logAll(`and ${truncatedCount} others...`); - return {truncated: true}; - } else { - return {truncated: false}; - } - } - - list(items: Array, opts: ListOptions = {}) { - return this.processedList( - items, - (reporter, str) => { - reporter.logAll(str, {newline: false}); - }, - opts, - ); - } - - progress(opts?: ReporterProgressOptions): ReporterProgress { - if (this.isRemote) { - return this.progressRemote(opts); - } else { - return this.progressLocal(opts); - } - } - - progressLocal(opts?: ReporterProgressOptions, onEnd?: () => void): Progress { - const bar = new Progress( - this, - opts, - () => { - this.activeElements.delete(bar); - if (onEnd !== undefined) { - onEnd(); - } - }, - ); - this.activeElements.add(bar); - return bar; - } - - progressRemote(opts?: ReporterProgressOptions): ReporterProgress { - const id: string = `${process.pid}:${remoteProgressIdCounter++}`; - - this.sendRemoteClientMessage.send({ - type: 'PROGRESS_CREATE', - opts, - id, - }); - - let closed = false; - - const dispatch = (message: RemoteReporterClientMessage) => { - if (!closed) { - this.sendRemoteClientMessage.send(message); - } - }; - - const end = () => { - this.activeElements.delete(progress); - this.remoteServerProgressBars.delete(id); - closed = true; - }; - - const progress: ReporterProgress = { - render() { - // Don't do anything - // This is called when columns have updated and we want to force a rerender - }, - setCurrent: (current: number) => { - dispatch({ - type: 'PROGRESS_SET_CURRENT', - current, - id, - }); - }, - setTotal: (total: number, approximate: boolean = false) => { - dispatch({ - type: 'PROGRESS_SET_TOTAL', - total, - approximate, - id, - }); - }, - setText: (text: string) => { - dispatch({ - type: 'PROGRESS_SET_TEXT', - text, - id, - }); - }, - setApproximateETA: (duration: number) => { - dispatch({ - type: 'PROGRESS_SET_APPROXIMATE_ETA', - duration, - id, - }); - }, - pushText: (text: string) => { - dispatch({ - type: 'PROGRESS_PUSH_TEXT', - text, - id, - }); - }, - popText: (text: string) => { - dispatch({ - type: 'PROGRESS_POP_TEXT', - text, - id, - }); - }, - tick: () => { - dispatch({ - type: 'PROGRESS_TICK', - id, - }); - }, - end: () => { - dispatch({ - type: 'PROGRESS_END', - id, - }); - }, - pause: () => { - dispatch({ - type: 'PROGRESS_PAUSE', - id, - }); - }, - resume: () => { - dispatch({ - type: 'PROGRESS_RESUME', - id, - }); - }, - }; - - this.remoteServerProgressBars.set( - id, - { - end, - }, - ); - - this.activeElements.add(progress); - - return progress; - } + constructor(opts: ReporterOptions = {}) { + this.programName = opts.programName === undefined ? 'rome' : opts.programName; + this.programVersion = opts.programVersion; + + this.noProgress = process.env.CI === '1'; + this.isVerbose = Boolean(opts.verbose); + + this.startTime = opts.startTime === undefined ? Date.now() : opts.startTime; + this.hasClearScreen = + opts.hasClearScreen === undefined ? true : opts.hasClearScreen; + this.activeElements = new Set(); + this.indentLevel = 0; + this.indentString = ''; + this.enabled = opts.disabled === true ? 0 : 1; + this.markupOptions = + opts.markupOptions === undefined ? {} : opts.markupOptions; + this.streamsWithDoubleNewlineEnd = new Set(); + this.streamsWithNewlineEnd = new Set(); + this.shouldRedirectOutToErr = false; + this.stdin = opts.stdin; + + this.wrapperFactory = opts.wrapperFactory; + + this.remoteClientProgressBars = new Map(); + this.remoteServerProgressBars = new Map(); + + this.sendRemoteServerMessage = new Event({ + name: 'sendRemoteServerMessage', + }); + this.sendRemoteClientMessage = new Event({ + name: 'sendRemoteClientMessage', + }); + + this.isRemote = opts.useRemoteProgressBars === true; + + this.outStreams = new Set(); + this.errStreams = new Set(); + this.streams = new Set(); + + if (opts.streams !== undefined) { + for (const stream of opts.streams) { + this.addStream(stream); + } + } + } + + static DEFAULT_COLUMNS = 100; + + attachStdoutStreams(stdout?: Stdout, stderr?: Stdout): ReporterDerivedStreams { + const columns = + stdout === undefined || stdout.columns === undefined + ? Reporter.DEFAULT_COLUMNS + : stdout.columns; + + const columnsUpdated: Event = new Event({ + name: 'columnsUpdated', + }); + + // Windows terminals are awful + const unicode = process.platform !== 'win32'; + + const outStream: ReporterStream = { + type: 'out', + format: getStreamFormat(stdout), + columns, + unicode, + write(chunk) { + if (stdout !== undefined) { + stdout.write(chunk); + } + }, + teardown() {}, + }; + + const errStream: ReporterStream = { + type: 'error', + format: getStreamFormat(stderr), + columns, + unicode, + write(chunk) { + if (stderr !== undefined) { + stderr.write(chunk); + } + }, + }; + + // Watch for resizing + if (outStream.format === 'ansi' && stdout !== undefined) { + const onStdoutResize = () => { + if (stdout?.columns !== undefined) { + const {columns} = stdout; + columnsUpdated.send(columns); + this.setStreamColumns([outStream, errStream], columns); + } + }; + + outStream.teardown = () => { + stdout.off('resize', onStdoutResize); + }; + + stdout.on('resize', onStdoutResize); + } + + this.addStream(outStream); + this.addStream(errStream); + + return { + columnsUpdated, + stdout: outStream, + stderr: errStream, + }; + } + + attachCaptureStream( + format: ReporterStream['format'] = 'none', + ): { + read: () => string; + remove: () => void; + } { + let buff = ''; + + const stream: ReporterStream = { + format, + type: 'all', + columns: Reporter.DEFAULT_COLUMNS, + unicode: true, + write(chunk) { + buff += chunk; + }, + }; + + this.addStream(stream); + + return { + read() { + return buff; + }, + remove: () => { + this.removeStream(stream); + }, + }; + } + + static fromProcess(opts: ReporterOptions = {}): Reporter { + const reporter = new Reporter({ + ...opts, + markupOptions: { + cwd: CWD_PATH, + ...opts.markupOptions, + }, + }); + + reporter.attachStdoutStreams(process.stdout, process.stderr); + + return reporter; + } + + programName: string; + programVersion: string | undefined; + markupOptions: MarkupFormatOptions; + + isRemote: boolean; + noProgress: boolean; + isVerbose: boolean; + streamsWithNewlineEnd: Set; + streamsWithDoubleNewlineEnd: Set; + indentLevel: number; + indentString: string; + enabled: number; + startTime: number; + shouldRedirectOutToErr: boolean; + wrapperFactory: undefined | WrapperFactory; + outStreams: Set; + errStreams: Set; + streams: Set; + sendRemoteServerMessage: Event; + sendRemoteClientMessage: Event; + stdin: undefined | NodeJS.ReadStream; + + remoteClientProgressBars: Map; + remoteServerProgressBars: Map< + string, + { + end: () => void; + } + >; + + // track whether we've output anything, we need this to avoid outputting multiple spacers etc + hasClearScreen: boolean; + + //Store active progress indicators so we can easily bail out and destroy them + activeElements: Set<{ + render: () => void; + end: () => void; + }>; + + processRemoteClientMessage(msg: RemoteReporterClientMessage) { + if (msg.type === 'PROGRESS_CREATE') { + this.remoteClientProgressBars.set( + msg.id, + this.progressLocal( + msg.opts, + () => { + this.sendRemoteServerMessage.call({ + type: 'ENDED', + id: msg.id, + }); + }, + ), + ); + return; + } + + let bar = this.remoteClientProgressBars.get(msg.id); + if (bar === undefined) { + throw new Error( + `Remote reporter message for progress bar ${msg.id} that does not exist`, + ); + } + + bar.processRemoteClientMessage(msg); + + if (msg.type === 'PROGRESS_END') { + this.remoteClientProgressBars.delete(msg.id); + } + } + + receivedRemoteServerMessage(msg: RemoteReporterServerMessage) { + // Currently the only message a remote Reporter can send is that it has ended + switch (msg.type) { + case 'ENDED': { + const progress = this.remoteServerProgressBars.get(msg.id); + if (progress !== undefined) { + progress.end(); + } + } + } + } + + getMessagePrefix(): string { + return ''; + } + + normalizeMessage(stream: ReporterStream, tty: string, opts: LogOptions): string { + let msg = + stream.format !== 'none' || opts.nonTTY === undefined ? tty : opts.nonTTY; + + if (opts.noPrefix !== true) { + msg = this.getMessagePrefix() + msg; + } + + // Don't indent if there is no indent, or the message is empty + const {indentString} = this; + if ( + this.streamsWithNewlineEnd.has(stream) && + indentString !== '' && + msg !== '' + ) { + // Indent each line, leaving out the indentation for empty lines + msg = indentString + msg.replace(/\n([^\n])/g, `\n${indentString}$1`); + } + + return msg; + } + + redirectOutToErr(should: boolean): boolean { + const old = this.shouldRedirectOutToErr; + this.shouldRedirectOutToErr = should; + return old; + } + + setStreamColumns(streams: Array, columns: number) { + for (const stream of streams) { + if (!this.streams.has(stream)) { + throw new Error( + "Trying to setStreamColumns on a stream that isn't attached to this Reporter", + ); + } + + stream.columns = columns; + } + + for (const elem of this.activeElements) { + elem.render(); + } + } + + addStream(stream: ReporterStream) { + this.streamsWithNewlineEnd.add(stream); + this.streams.add(stream); + + if (stream.type === 'error' || stream.type === 'all') { + this.errStreams.add(stream); + } + + if (stream.type === 'out' || stream.type === 'all') { + this.outStreams.add(stream); + } + } + + removeStream(stream: ReporterStream) { + if (stream.teardown !== undefined) { + stream.teardown(); + } + this.streams.delete(stream); + this.outStreams.delete(stream); + this.errStreams.delete(stream); + } + + //# Stdin + getStdin(): NodeJS.ReadStream { + const {stdin} = this; + if (stdin === undefined) { + throw new Error('This operation expected a stdin but we have none'); + } + return stdin; + } + + async question( + message: string, + {hint, default: def = '', yes = false}: QuestionOptions = {}, + ): Promise { + if (yes) { + return def; + } + + const stdin = this.getStdin(); + + const origPrompt = `? ${message}`; + let prompt = origPrompt; + if (hint !== undefined) { + prompt += ` ${hint}`; + } + if (def !== '') { + prompt += ` (${def})`; + } + prompt += ': '; + this.logAll( + prompt, + { + newline: false, + }, + ); + + const rl = readline.createInterface({ + input: stdin, + output: new stream.Writable({ + write: (chunk, encoding, callback) => { + this.writeAll(chunk); + callback(); + }, + }), + terminal: false, + }); + + return new Promise((resolve) => { + rl.on( + 'line', + (line) => { + rl.close(); + + const normalized = line === '' ? def : line; + + // Replace initial prompt + this.writeAll(ansiEscapes.cursorUp()); + this.writeAll(ansiEscapes.eraseLine); + + let prompt = origPrompt; + prompt += ': '; + if (normalized === '') { + prompt += 'empty'; + } else { + prompt += normalized; + } + this.logAll(prompt); + + resolve(normalized); + }, + ); + }); + } + + async questionValidate( + message: string, + validate: (value: string) => QuestionValidateResult, + options: Omit = {}, + ): Promise { + while (true) { + let res: undefined | QuestionValidateResult; + + await this.question( + `${message}`, + { + ...options, + normalize: (value: string): string => { + res = validate(value); + + if (res[0] === true && typeof res[1] === 'string') { + return res[1]; + } else { + return value; + } + }, + }, + ); + + if (res === undefined) { + throw new Error('normalize should have been called'); + } + + if (res[0] === false) { + this.error(res[1]); + continue; + } else { + return res[1]; + } + } + } + + async radioConfirm(message: string): Promise { + const answer = await this.radio( + message, + { + options: { + yes: { + label: 'Yes', + shortcut: 'y', + }, + no: { + label: 'No', + shortcut: 'n', + }, + }, + }, + ); + return answer === 'yes'; + } + + async confirm(message: string = 'Press any key to continue'): Promise { + this.logAll(`${message}`, {newline: false}); + + await new Promise((resolve) => { + const keypress = onKeypress( + this, + () => { + keypress.finish(); + resolve(); + }, + ); + }); + + // Newline + this.logAll(''); + } + + async radio( + message: string, + arg: SelectArguments, + ): Promise { + const set = await this.select(message, {...arg, radio: true}); + + // Should always have at least one element + return Array.from(set)[0]; + } + + async select( + message: string, + args: SelectArguments, + ): Promise> { + return select(this, message, args); + } + + //# Control + isEnabled(stderr: undefined | boolean): boolean { + return this.getStreams(stderr).size > 0; + } + + getStreams(stderr: undefined | boolean): Set { + if (this.enabled === 0) { + return new Set(); + } + + if (this.shouldRedirectOutToErr) { + return this.errStreams; + } + + if (stderr) { + return this.errStreams; + } + + return this.outStreams; + } + + enable(): () => void { + let alreadyDisabled = false; + + this.enabled++; + + return () => { + if (alreadyDisabled) { + throw new Error('Already disabled Reporter'); + } + + this.enabled--; + alreadyDisabled = true; + }; + } + + //# LIFECYCLE + teardown() { + for (const stream of this.streams) { + this.removeStream(stream); + } + + for (const elem of this.activeElements) { + elem.end(); + } + this.activeElements.clear(); + } + + fork(opts: Partial = {}) { + return new Reporter({ + streams: [...this.streams], + verbose: this.isVerbose, + markupOptions: this.markupOptions, + wrapperFactory: this.wrapperFactory, + ...opts, + }); + } + + //# INDENTATION METHODS + indent(callback: () => void) { + this.indentLevel++; + this.updateIndent(); + + callback(); + this.indentLevel--; + this.updateIndent(); + } + + noIndent(callback: () => void) { + const prevIndentLevel = this.indentLevel; + this.indentLevel = 0; + this.updateIndent(); + callback(); + this.indentLevel = prevIndentLevel; + this.updateIndent(); + } + + updateIndent() { + this.indentString = ' '.repeat(this.indentLevel); + } + + //# INTERNAL + prependEmoji( + stream: ReporterStream, + msg: string, + emoji: string, + fallback?: string, + ): string { + if (stream.format === 'none') { + return `${emoji} ${msg}`; + } else { + if (fallback === undefined) { + return msg; + } else { + return `${fallback} ${msg}`; + } + } + } + + //# VISUALISATION + table( + head: Array, + rawBody: Array>, + ) { + let body = ''; + + if (head.length > 0) { + body += ''; + for (const field of head) { + body += `${field}`; + } + body += ''; + } + + for (const row of rawBody) { + body += ''; + for (let field of row) { + if (typeof field === 'string' || typeof field === 'number') { + field = {align: 'left', value: field}; + } + + let {value, align} = field; + if (typeof value === 'number') { + value = `${value}`; + } + body += `${value}`; + } + body += ''; + } + + this.logAll(`${body}
    `); + } + + verboseInspect(val: unknown) { + if (this.isVerbose) { + this.inspect(val); + } + } + + inspect(value: unknown) { + if (!this.isEnabled(false)) { + return; + } + + let formatted = value; + + if (typeof formatted !== 'number' && typeof formatted !== 'string') { + formatted = prettyFormat(formatted, {markup: true}); + } + + this.logAll(String(formatted)); + } + + //# ESCAPE HATCHES + clearLineAll() { + for (const stream of this.getStreams(false)) { + this.clearLineSpecific(stream); + } + } + + clearLineSpecific(stream: ReporterStream) { + stream.write(ansiEscapes.eraseLine); + stream.write(ansiEscapes.cursorTo(0)); + } + + writeAll(msg: string, opts: LogOptions = {}) { + for (const stream of this.getStreams(opts.stderr)) { + this.writeSpecific(stream, msg, opts); + } + } + + writeSpecific(stream: ReporterStream, msg: string, opts: LogOptions = {}) { + if (!this.isEnabled(opts.stderr)) { + return; + } + + this.hasClearScreen = false; + + if (stream.format === 'ansi' && this.activeElements.size > 0) { + // A progress bar is active and has probably drawn to the screen + this.clearLineSpecific(stream); + } + + stream.write(msg); + } + + //# UTILITIES + getTotalTime(): number { + return Date.now() - this.startTime; + } + + clearScreen() { + for (const stream of this.getStreams(false)) { + if (stream.format === 'ansi') { + stream.write(ansiEscapes.clearScreen); + } + } + this.hasClearScreen = true; + } + + //# SECTIONS + heading(text: string) { + this.br(); + this.logAll( + `${text}`, + { + nonTTY: `# ${text}`, + }, + ); + this.br(); + } + + section(title: undefined | string, callback: () => void) { + this.hr(title === undefined ? undefined : `${title}`); + this.indent(() => { + callback(); + this.br(); + }); + } + + hr(text: string = '') { + const {hasClearScreen} = this; + + this.br(); + + if (hasClearScreen && text === undefined) { + return; + } + + this.logAll(`
    ${text}`); + this.br(); + } + + async steps( + callbacks: Array<{ + message: string; + callback: () => Promise; + clear?: boolean; + }>, + ) { + const total = callbacks.length; + let current = 1; + for (const {clear, message, callback} of callbacks) { + this.step(current, total, message); + + if (clear) { + this.hasClearScreen = true; + } + + await callback(); + current++; + + // If a step doesn't produce any output, or just progress bars that are cleared, we can safely remove the previous `step` message line + if (clear && this.hasClearScreen) { + for (const stream of this.getStreams(false)) { + if (stream.format === 'ansi') { + stream.write(ansiEscapes.cursorTo(0)); + stream.write(ansiEscapes.cursorUp()); + stream.write(ansiEscapes.eraseLine); + } + } + } + } + } + + step(current: number, total: number, msg: string) { + if (msg.endsWith('?')) { + msg = `${removeSuffix(msg, '?')}...?`; + } else { + msg += '...'; + } + + this.logAll(`[${current}/${total}] ${msg}`); + } + + br(force: boolean = false) { + for (const stream of this.getStreams(false)) { + if (!this.streamsWithDoubleNewlineEnd.has(stream) || force) { + this.logOne(stream, ''); + } + } + } + + wrapCallback: WrapperFactory = (callback) => { + const {wrapperFactory} = this; + if (wrapperFactory === undefined) { + return callback; + } else { + return wrapperFactory(callback); + } + }; + + stripMarkup(str: string): string { + return markupToPlainText(str, this.markupOptions).lines.join('\n'); + } + + markupify( + stream: ReporterStreamMeta, + str: string, + viewportShrink: number = 0, + ): MarkupLinesAndWidth { + if (str === '') { + return {lines: [''], width: 0}; + } + + const gridMarkupOptions: MarkupFormatGridOptions = { + ...this.markupOptions, + columns: stream.columns - this.indentString.length - viewportShrink, + }; + + switch (stream.format) { + case 'ansi': + return markupToAnsi(str, gridMarkupOptions); + + case 'html': + // TODO + return markupToPlainText(str, gridMarkupOptions); + + case 'none': + return markupToPlainText(str, gridMarkupOptions); + + case 'markup': + return { + width: 0, + lines: [str], + }; + } + } + + logAll(tty: string, opts: LogOptions = {}) { + for (const stream of this.getStreams(opts.stderr)) { + this.logOne(stream, tty, opts); + } + } + + logAllNoMarkup(tty: string, opts: LogOptions = {}) { + for (const stream of this.getStreams(opts.stderr)) { + this.logOneNoMarkup(stream, tty, opts); + } + } + + logOne(stream: ReporterStream, tty: string, opts: LogOptions = {}) { + const msg = + stream.format !== 'none' || opts.nonTTY === undefined ? tty : opts.nonTTY; + const {lines} = this.markupify(stream, msg); + for (const line of lines) { + this.logOneNoMarkup(stream, line, opts); + } + } + + logOneNoMarkup(stream: ReporterStream, tty: string, opts: LogOptions = {}) { + if (!this.isEnabled(opts.stderr)) { + return; + } + + let msg = this.normalizeMessage(stream, tty, opts); + if (opts.newline !== false) { + msg += '\n'; + } + + // Track if there's going to be a completely empty line + const hasDoubleNewline = msg === '\n' || msg.endsWith('\n\n'); + if (hasDoubleNewline) { + this.streamsWithDoubleNewlineEnd.add(stream); + } else { + this.streamsWithDoubleNewlineEnd.delete(stream); + } + if (msg.endsWith('\n')) { + this.streamsWithNewlineEnd.add(stream); + } else { + this.streamsWithNewlineEnd.delete(stream); + } + + this.writeSpecific(stream, msg, opts); + } + + logAllWithCategory( + rawInner: string, + args: Array, + opts: LogCategoryOptions, + ) { + if (!this.isEnabled(opts.stderr)) { + return; + } + + const inner = markupTag(opts.markupTag, rawInner); + + for (const stream of this.getStreams(opts.stderr)) { + // Format the prefix, selecting it depending on if we're a unicode stream + const prefixInner = stream.unicode ? opts.unicodePrefix : opts.rawPrefix; + const prefix = markupTag( + 'emphasis', + markupTag(opts.markupTag, this.getMessagePrefix() + prefixInner), + ); + + // Should only be one line + const {lines: prefixLines, width: prefixWidth} = this.markupify( + stream, + prefix, + ); + const prefixLine = prefixLines[0]; + if (prefixLines.length !== 1) { + throw new Error('Expected 1 prefix line'); + } + + const {lines} = this.markupify(stream, inner, prefixWidth); + for (let i = 0; i < lines.length; i++) { + let line = lines[i]; + if (i === 0) { + line = `${prefixLine}${line}`; + } else { + line = `${' '.repeat(prefixWidth)}${line}`; + } + this.logOneNoMarkup( + stream, + line, + { + noPrefix: true, + ...opts, + }, + ); + } + } + } + + success(msg: string, ...args: Array) { + this.logAllWithCategory( + msg, + args, + { + unicodePrefix: '\u2714 ', + rawPrefix: '\u221a ', + markupTag: 'success', + }, + ); + } + + error(msg: string, ...args: Array) { + this.logAllWithCategory( + msg, + args, + { + markupTag: 'error', + unicodePrefix: '\u2716 ', + rawPrefix: '\xd7 ', + stderr: true, + }, + ); + } + + errorObj(err: Error) { + this.error(err.stack || err.message || err.name || 'Unknown Error'); + } + + info(msg: string, ...args: Array) { + this.logAllWithCategory( + msg, + args, + { + unicodePrefix: '\u2139 ', + rawPrefix: 'i ', + markupTag: 'info', + }, + ); + } + + warn(msg: string, ...args: Array) { + this.logAllWithCategory( + msg, + args, + { + unicodePrefix: '\u26a0 ', + rawPrefix: '! ', + markupTag: 'warn', + stderr: true, + }, + ); + } + + verbose(msg: string, ...args: Array) { + if (this.isVerbose) { + this.verboseForce(msg, args); + } + } + + verboseForce(msg: string, ...args: Array) { + this.logAllWithCategory( + msg, + args, + { + unicodePrefix: '\u26a1 ', + rawPrefix: '* ', + markupTag: 'dim', + }, + ); + } + + command(command: string) { + this.logAll( + `$ ${command}`, + { + nonTTY: `$ ${command}`, + }, + ); + } + + processedList( + items: Array, + callback: (reporter: Reporter, item: T) => void, + opts: ListOptions = {}, + ): { + truncated: boolean; + } { + if (items.length === 0) { + // We make some assumptions that there's at least one item + return {truncated: false}; + } + + let truncatedCount = 0; + + let start = opts.start || 0; + if (opts.truncate !== undefined && items.length > opts.truncate) { + truncatedCount = items.length - opts.truncate; + items = items.slice(0, opts.truncate); + start += truncatedCount; + } + + let buff = ''; + + for (const item of items) { + const reporter = this.fork({ + streams: [], + }); + const stream = reporter.attachCaptureStream('markup'); + callback(reporter, item); + stream.remove(); + buff += `
  • ${stream.read()}
  • `; + } + + if (opts.ordered) { + this.logAll(markupTag('ol', buff, {start, reversed: opts.reverse})); + } else { + this.logAll(`
      ${buff}
    `); + } + + if (truncatedCount > 0) { + this.logAll(`and ${truncatedCount} others...`); + return {truncated: true}; + } else { + return {truncated: false}; + } + } + + list(items: Array, opts: ListOptions = {}) { + return this.processedList( + items, + (reporter, str) => { + reporter.logAll(str, {newline: false}); + }, + opts, + ); + } + + progress(opts?: ReporterProgressOptions): ReporterProgress { + if (this.isRemote) { + return this.progressRemote(opts); + } else { + return this.progressLocal(opts); + } + } + + progressLocal(opts?: ReporterProgressOptions, onEnd?: () => void): Progress { + const bar = new Progress( + this, + opts, + () => { + this.activeElements.delete(bar); + if (onEnd !== undefined) { + onEnd(); + } + }, + ); + this.activeElements.add(bar); + return bar; + } + + progressRemote(opts?: ReporterProgressOptions): ReporterProgress { + const id: string = `${process.pid}:${remoteProgressIdCounter++}`; + + this.sendRemoteClientMessage.send({ + type: 'PROGRESS_CREATE', + opts, + id, + }); + + let closed = false; + + const dispatch = (message: RemoteReporterClientMessage) => { + if (!closed) { + this.sendRemoteClientMessage.send(message); + } + }; + + const end = () => { + this.activeElements.delete(progress); + this.remoteServerProgressBars.delete(id); + closed = true; + }; + + const progress: ReporterProgress = { + render() { + // Don't do anything + // This is called when columns have updated and we want to force a rerender + }, + setCurrent: (current: number) => { + dispatch({ + type: 'PROGRESS_SET_CURRENT', + current, + id, + }); + }, + setTotal: (total: number, approximate: boolean = false) => { + dispatch({ + type: 'PROGRESS_SET_TOTAL', + total, + approximate, + id, + }); + }, + setText: (text: string) => { + dispatch({ + type: 'PROGRESS_SET_TEXT', + text, + id, + }); + }, + setApproximateETA: (duration: number) => { + dispatch({ + type: 'PROGRESS_SET_APPROXIMATE_ETA', + duration, + id, + }); + }, + pushText: (text: string) => { + dispatch({ + type: 'PROGRESS_PUSH_TEXT', + text, + id, + }); + }, + popText: (text: string) => { + dispatch({ + type: 'PROGRESS_POP_TEXT', + text, + id, + }); + }, + tick: () => { + dispatch({ + type: 'PROGRESS_TICK', + id, + }); + }, + end: () => { + dispatch({ + type: 'PROGRESS_END', + id, + }); + }, + pause: () => { + dispatch({ + type: 'PROGRESS_PAUSE', + id, + }); + }, + resume: () => { + dispatch({ + type: 'PROGRESS_RESUME', + id, + }); + }, + }; + + this.remoteServerProgressBars.set( + id, + { + end, + }, + ); + + this.activeElements.add(progress); + + return progress; + } } diff --git a/packages/@romejs/cli-reporter/select.ts b/packages/@romejs/cli-reporter/select.ts index 7d8a9908443..117db26a01d 100644 --- a/packages/@romejs/cli-reporter/select.ts +++ b/packages/@romejs/cli-reporter/select.ts @@ -11,230 +11,230 @@ import {SelectArguments, SelectOption, SelectOptions} from './types'; import {onKeypress, setRawMode} from './util'; function formatShortcut({shortcut}: SelectOption): string { - if (shortcut === undefined) { - return ''; - } else { - return ` (shortcut ${shortcut})`; - } + if (shortcut === undefined) { + return ''; + } else { + return ` (shortcut ${shortcut})`; + } } export default async function select( - reporter: Reporter, - message: string, - { - options, - defaults = [], - radio = false, - yes = false, - }: SelectArguments, + reporter: Reporter, + message: string, + { + options, + defaults = [], + radio = false, + yes = false, + }: SelectArguments, ): Promise> { - const optionNames: Array = []; - const seenShortcuts: Set = new Set(); - - // Verify there's no shortcut collisions and remove empty options - for (const key in options) { - const option: undefined | SelectOption = options[key]; - - if (option !== undefined) { - optionNames.push(key); - - const {shortcut} = option; - if (shortcut !== undefined) { - if (seenShortcuts.has(shortcut)) { - throw new Error(`Multiple options have the shortcut ${shortcut}`); - } else { - seenShortcuts.add(shortcut); - } - } - } - } - - let optionCount = optionNames.length; - if (optionCount === 0) { - return new Set(); - } - - if (yes) { - return new Set(defaults); - } - - let prompt = ` ${message}`; - reporter.logAll(prompt); - - if (radio) { - reporter.info( - 'Use arrow keys and then enter to select an option', - ); - } else { - reporter.info( - 'Use arrow keys and space to select or deselect options and then enter to confirm', - ); - } - - const selectedOptions: Set = new Set(defaults); - let activeOption = 0; - - // Set first option if this is a radio - if (radio && !defaults.length) { - selectedOptions.add(optionNames[0]); - } - - function boundActive() { - activeOption = Math.min(activeOption, optionCount - 1); - activeOption = Math.max(activeOption, 0); - - if (radio) { - selectedOptions.clear(); - selectedOptions.add(optionNames[activeOption]); - } - } - - // If we aren't a radio then set the active option to the bottom of any that are enabled - if (!radio) { - while (selectedOptions.has(optionNames[activeOption])) { - activeOption++; - } - } - - function render() { - const optionNames = Object.keys(options); - for (let i = 0; i < optionNames.length; i++) { - const key = optionNames[i]; - const option = options[key]!; - const {label} = option; - const shortcut = formatShortcut(option); - - let formattedLabel = - optionNames.indexOf(key) === activeOption - ? `${label}` - : label; - - let symbol = ''; - if (radio) { - symbol = selectedOptions.has(key) ? '\u25c9' : '\u25ef'; - } else { - symbol = selectedOptions.has(key) ? '\u2611' : '\u2610'; - } - - reporter.logAll( - ` ${symbol} ${formattedLabel}${shortcut}`, - { - // Don't put a newline on the last option - newline: i !== optionNames.length - 1, - }, - ); - } - } - function cleanup() { - for (let i = 0; i < optionCount; i++) { - reporter.writeAll(ansiEscapes.eraseLine); - - // Don't move above the top line - if (i !== optionCount - 1) { - reporter.writeAll(ansiEscapes.cursorUp()); - } - } - reporter.writeAll(ansiEscapes.cursorTo(0)); - } - function toggleOption(optionName: string) { - if (selectedOptions.has(optionName)) { - selectedOptions.delete(optionName); - } else { - selectedOptions.add(optionName); - } - } - - const stdin = reporter.getStdin(); - - render(); - - setRawMode(stdin, true); - - await new Promise((resolve) => { - const keypress = onKeypress( - reporter, - (key) => { - // Check if this is an option shortcut - if (!key.ctrl) { - for (const optionName in options) { - const option: undefined | SelectOption = options[optionName]; - if (option === undefined) { - continue; - } - - const {shortcut} = option; - if (shortcut === key.name) { - if (radio) { - selectedOptions.clear(); - selectedOptions.add(optionName); - finish(); - } else { - toggleOption(optionName); - } - return; - } - } - } - - switch (key.name) { - case 'up': { - activeOption--; - break; - } - - case 'down': { - activeOption++; - break; - } - - case 'space': { - if (!radio) { - toggleOption((optionNames[activeOption] as string)); - } - break; - } - - case 'return': { - finish(); - return; - } - - default: - return; - } - - boundActive(); - cleanup(); - render(); - }, - ); - - function finish() { - cleanup(); - - // Remove initial help message - reporter.writeAll(ansiEscapes.cursorUp()); - reporter.writeAll(ansiEscapes.eraseLine); - - // Remove initial log message - reporter.writeAll(ansiEscapes.cursorUp()); - reporter.writeAll(ansiEscapes.eraseLine); - - prompt += ': '; - if (selectedOptions.size > 0) { - prompt += Array.from(selectedOptions, (key) => options[key]!.label).join( - ', ', - ); - } else { - prompt += 'none'; - } - reporter.logAll(prompt); - - // Stop listening for keypress - keypress.finish(); - resolve(); - } - }); - - return selectedOptions; + const optionNames: Array = []; + const seenShortcuts: Set = new Set(); + + // Verify there's no shortcut collisions and remove empty options + for (const key in options) { + const option: undefined | SelectOption = options[key]; + + if (option !== undefined) { + optionNames.push(key); + + const {shortcut} = option; + if (shortcut !== undefined) { + if (seenShortcuts.has(shortcut)) { + throw new Error(`Multiple options have the shortcut ${shortcut}`); + } else { + seenShortcuts.add(shortcut); + } + } + } + } + + let optionCount = optionNames.length; + if (optionCount === 0) { + return new Set(); + } + + if (yes) { + return new Set(defaults); + } + + let prompt = ` ${message}`; + reporter.logAll(prompt); + + if (radio) { + reporter.info( + 'Use arrow keys and then enter to select an option', + ); + } else { + reporter.info( + 'Use arrow keys and space to select or deselect options and then enter to confirm', + ); + } + + const selectedOptions: Set = new Set(defaults); + let activeOption = 0; + + // Set first option if this is a radio + if (radio && !defaults.length) { + selectedOptions.add(optionNames[0]); + } + + function boundActive() { + activeOption = Math.min(activeOption, optionCount - 1); + activeOption = Math.max(activeOption, 0); + + if (radio) { + selectedOptions.clear(); + selectedOptions.add(optionNames[activeOption]); + } + } + + // If we aren't a radio then set the active option to the bottom of any that are enabled + if (!radio) { + while (selectedOptions.has(optionNames[activeOption])) { + activeOption++; + } + } + + function render() { + const optionNames = Object.keys(options); + for (let i = 0; i < optionNames.length; i++) { + const key = optionNames[i]; + const option = options[key]!; + const {label} = option; + const shortcut = formatShortcut(option); + + let formattedLabel = + optionNames.indexOf(key) === activeOption + ? `${label}` + : label; + + let symbol = ''; + if (radio) { + symbol = selectedOptions.has(key) ? '\u25c9' : '\u25ef'; + } else { + symbol = selectedOptions.has(key) ? '\u2611' : '\u2610'; + } + + reporter.logAll( + ` ${symbol} ${formattedLabel}${shortcut}`, + { + // Don't put a newline on the last option + newline: i !== optionNames.length - 1, + }, + ); + } + } + function cleanup() { + for (let i = 0; i < optionCount; i++) { + reporter.writeAll(ansiEscapes.eraseLine); + + // Don't move above the top line + if (i !== optionCount - 1) { + reporter.writeAll(ansiEscapes.cursorUp()); + } + } + reporter.writeAll(ansiEscapes.cursorTo(0)); + } + function toggleOption(optionName: string) { + if (selectedOptions.has(optionName)) { + selectedOptions.delete(optionName); + } else { + selectedOptions.add(optionName); + } + } + + const stdin = reporter.getStdin(); + + render(); + + setRawMode(stdin, true); + + await new Promise((resolve) => { + const keypress = onKeypress( + reporter, + (key) => { + // Check if this is an option shortcut + if (!key.ctrl) { + for (const optionName in options) { + const option: undefined | SelectOption = options[optionName]; + if (option === undefined) { + continue; + } + + const {shortcut} = option; + if (shortcut === key.name) { + if (radio) { + selectedOptions.clear(); + selectedOptions.add(optionName); + finish(); + } else { + toggleOption(optionName); + } + return; + } + } + } + + switch (key.name) { + case 'up': { + activeOption--; + break; + } + + case 'down': { + activeOption++; + break; + } + + case 'space': { + if (!radio) { + toggleOption((optionNames[activeOption] as string)); + } + break; + } + + case 'return': { + finish(); + return; + } + + default: + return; + } + + boundActive(); + cleanup(); + render(); + }, + ); + + function finish() { + cleanup(); + + // Remove initial help message + reporter.writeAll(ansiEscapes.cursorUp()); + reporter.writeAll(ansiEscapes.eraseLine); + + // Remove initial log message + reporter.writeAll(ansiEscapes.cursorUp()); + reporter.writeAll(ansiEscapes.eraseLine); + + prompt += ': '; + if (selectedOptions.size > 0) { + prompt += Array.from(selectedOptions, (key) => options[key]!.label).join( + ', ', + ); + } else { + prompt += 'none'; + } + reporter.logAll(prompt); + + // Stop listening for keypress + keypress.finish(); + resolve(); + } + }); + + return selectedOptions; } diff --git a/packages/@romejs/cli-reporter/types.ts b/packages/@romejs/cli-reporter/types.ts index b71991c2123..9aab2d32427 100644 --- a/packages/@romejs/cli-reporter/types.ts +++ b/packages/@romejs/cli-reporter/types.ts @@ -8,130 +8,130 @@ import {Event} from '@romejs/events'; export type SelectOption = { - label: string; - shortcut?: string; + label: string; + shortcut?: string; }; export type SelectOptions = { - [key: string]: undefined | SelectOption; + [key: string]: undefined | SelectOption; }; export type SelectArguments = { - options: Options; - defaults?: Array; - radio?: boolean; - yes?: boolean; + options: Options; + defaults?: Array; + radio?: boolean; + yes?: boolean; }; export type Package = { - name: string; - version?: string; + name: string; + version?: string; }; export type ReporterTableField = - | number - | string - | { - align: 'left' | 'right'; - value: number | string; - }; + | number + | string + | { + align: 'left' | 'right'; + value: number | string; + }; export type ReporterStreamMeta = { - type: 'out' | 'error' | 'all'; - columns: number; - unicode: boolean; - format: 'markup' | 'ansi' | 'html' | 'none'; + type: 'out' | 'error' | 'all'; + columns: number; + unicode: boolean; + format: 'markup' | 'ansi' | 'html' | 'none'; }; export type ReporterStream = ReporterStreamMeta & { - write: (chunk: string) => void; - teardown?: () => void; + write: (chunk: string) => void; + teardown?: () => void; }; export type ReporterDerivedStreams = { - columnsUpdated: Event; - stdout: ReporterStream; - stderr: ReporterStream; + columnsUpdated: Event; + stdout: ReporterStream; + stderr: ReporterStream; }; export type ReporterProgressOptions = { - name?: string; - title?: string; - initDelay?: number; - elapsed?: boolean; - eta?: boolean; - persistent?: boolean; + name?: string; + title?: string; + initDelay?: number; + elapsed?: boolean; + eta?: boolean; + persistent?: boolean; }; export type ReporterProgress = { - render: () => void; - setCurrent: (current: number) => void; - setTotal: (total: number, approximate?: boolean) => void; - setText: (text: string) => void; - pushText: (text: string) => void; - popText: (text: string) => void; - setApproximateETA: (duration: number) => void; - tick: () => void; - end: () => void; - pause: () => void; - resume: () => void; + render: () => void; + setCurrent: (current: number) => void; + setTotal: (total: number, approximate?: boolean) => void; + setText: (text: string) => void; + pushText: (text: string) => void; + popText: (text: string) => void; + setApproximateETA: (duration: number) => void; + tick: () => void; + end: () => void; + pause: () => void; + resume: () => void; }; export type RemoteReporterReceiveMessage = { - type: 'ENDED'; - id: string; + type: 'ENDED'; + id: string; }; export type RemoteReporterClientMessage = - | { - type: 'PROGRESS_CREATE'; - id: string; - opts: undefined | ReporterProgressOptions; - } - | { - type: 'PROGRESS_SET_CURRENT'; - current: number; - id: string; - } - | { - type: 'PROGRESS_SET_APPROXIMATE_ETA'; - duration: number; - id: string; - } - | { - type: 'PROGRESS_SET_TOTAL'; - total: number; - id: string; - approximate: boolean; - } - | { - type: 'PROGRESS_SET_TEXT'; - text: string; - id: string; - } - | { - type: 'PROGRESS_PUSH_TEXT'; - text: string; - id: string; - } - | { - type: 'PROGRESS_POP_TEXT'; - text: string; - id: string; - } - | { - type: 'PROGRESS_TICK'; - id: string; - } - | { - type: 'PROGRESS_END'; - id: string; - } - | { - type: 'PROGRESS_PAUSE'; - id: string; - } - | { - type: 'PROGRESS_RESUME'; - id: string; - }; + | { + type: 'PROGRESS_CREATE'; + id: string; + opts: undefined | ReporterProgressOptions; + } + | { + type: 'PROGRESS_SET_CURRENT'; + current: number; + id: string; + } + | { + type: 'PROGRESS_SET_APPROXIMATE_ETA'; + duration: number; + id: string; + } + | { + type: 'PROGRESS_SET_TOTAL'; + total: number; + id: string; + approximate: boolean; + } + | { + type: 'PROGRESS_SET_TEXT'; + text: string; + id: string; + } + | { + type: 'PROGRESS_PUSH_TEXT'; + text: string; + id: string; + } + | { + type: 'PROGRESS_POP_TEXT'; + text: string; + id: string; + } + | { + type: 'PROGRESS_TICK'; + id: string; + } + | { + type: 'PROGRESS_END'; + id: string; + } + | { + type: 'PROGRESS_PAUSE'; + id: string; + } + | { + type: 'PROGRESS_RESUME'; + id: string; + }; diff --git a/packages/@romejs/cli-reporter/util.ts b/packages/@romejs/cli-reporter/util.ts index fbba713f72c..ab578a9099e 100644 --- a/packages/@romejs/cli-reporter/util.ts +++ b/packages/@romejs/cli-reporter/util.ts @@ -10,127 +10,127 @@ import Reporter from './Reporter'; import readline = require('readline'); export function mergeProgresses( - progresses: Array, + progresses: Array, ): ReporterProgress { - if (progresses.length === 1) { - return progresses[0]; - } + if (progresses.length === 1) { + return progresses[0]; + } - return { - render: () => { - for (const progress of progresses) { - progress.render(); - } - }, - setCurrent: (current: number) => { - for (const progress of progresses) { - progress.setCurrent(current); - } - }, - setTotal: (total: number, approximate?: boolean) => { - for (const progress of progresses) { - progress.setTotal(total, approximate); - } - }, - setText: (text: string) => { - for (const progress of progresses) { - progress.setText(text); - } - }, - pushText: (text: string) => { - for (const progress of progresses) { - progress.pushText(text); - } - }, - popText: (text: string) => { - for (const progress of progresses) { - progress.popText(text); - } - }, - setApproximateETA: (duration: number) => { - for (const progress of progresses) { - progress.setApproximateETA(duration); - } - }, - tick: () => { - for (const progress of progresses) { - progress.tick(); - } - }, - end: () => { - for (const progress of progresses) { - progress.end(); - } - }, - pause: () => { - for (const progress of progresses) { - progress.pause(); - } - }, - resume: () => { - for (const progress of progresses) { - progress.resume(); - } - }, - }; + return { + render: () => { + for (const progress of progresses) { + progress.render(); + } + }, + setCurrent: (current: number) => { + for (const progress of progresses) { + progress.setCurrent(current); + } + }, + setTotal: (total: number, approximate?: boolean) => { + for (const progress of progresses) { + progress.setTotal(total, approximate); + } + }, + setText: (text: string) => { + for (const progress of progresses) { + progress.setText(text); + } + }, + pushText: (text: string) => { + for (const progress of progresses) { + progress.pushText(text); + } + }, + popText: (text: string) => { + for (const progress of progresses) { + progress.popText(text); + } + }, + setApproximateETA: (duration: number) => { + for (const progress of progresses) { + progress.setApproximateETA(duration); + } + }, + tick: () => { + for (const progress of progresses) { + progress.tick(); + } + }, + end: () => { + for (const progress of progresses) { + progress.end(); + } + }, + pause: () => { + for (const progress of progresses) { + progress.pause(); + } + }, + resume: () => { + for (const progress of progresses) { + progress.resume(); + } + }, + }; } type Key = { - name: string; - ctrl: boolean; + name: string; + ctrl: boolean; }; export function onKeypress( - reporter: Reporter, - callback: (key: Key) => void, + reporter: Reporter, + callback: (key: Key) => void, ): { - finish: () => void; + finish: () => void; } { - const stdin = reporter.getStdin(); + const stdin = reporter.getStdin(); - setRawMode(stdin, true); - readline.emitKeypressEvents(stdin); + setRawMode(stdin, true); + readline.emitKeypressEvents(stdin); - function onkeypress(chunk: Buffer, key: Key) { - switch (key.name) { - case 'c': { - if (key.ctrl) { - reporter.br(true); - reporter.warn('Cancelled by user'); - process.exit(1); - } - return; - } + function onkeypress(chunk: Buffer, key: Key) { + switch (key.name) { + case 'c': { + if (key.ctrl) { + reporter.br(true); + reporter.warn('Cancelled by user'); + process.exit(1); + } + return; + } - case 'escape': { - reporter.br(true); - reporter.warn('Cancelled by user'); - process.exit(1); - return; - } - } + case 'escape': { + reporter.br(true); + reporter.warn('Cancelled by user'); + process.exit(1); + return; + } + } - callback(key); - } + callback(key); + } - stdin.addListener('keypress', onkeypress); + stdin.addListener('keypress', onkeypress); - return { - finish() { - stdin.removeListener('keypress', onkeypress); - setRawMode(stdin, false); - }, - }; + return { + finish() { + stdin.removeListener('keypress', onkeypress); + setRawMode(stdin, false); + }, + }; } export function setRawMode(stdin: NodeJS.ReadStream, raw: boolean) { - if (stdin.isTTY && stdin.setRawMode !== undefined) { - stdin.setRawMode(raw); - } + if (stdin.isTTY && stdin.setRawMode !== undefined) { + stdin.setRawMode(raw); + } - if (raw) { - stdin.resume(); - } else { - stdin.pause(); - } + if (raw) { + stdin.resume(); + } else { + stdin.pause(); + } } diff --git a/packages/@romejs/cli/bin/rome.ts b/packages/@romejs/cli/bin/rome.ts index acf1c128ab9..bcdeef300d5 100644 --- a/packages/@romejs/cli/bin/rome.ts +++ b/packages/@romejs/cli/bin/rome.ts @@ -7,7 +7,7 @@ import {catchDiagnostics} from '@romejs/diagnostics'; import {printDiagnostics} from '@romejs/cli-diagnostics'; -import {getErrorStructure, sourceMapManager} from '@romejs/v8'; +import {getErrorStructure, initErrorHooks, sourceMapManager} from '@romejs/v8'; import {Reporter} from '@romejs/cli-reporter'; import {BIN, MAP, VERSION} from '@romejs/core'; import cli from '../cli'; @@ -18,43 +18,46 @@ import {readFileTextSync} from '@romejs/fs'; import {SourceMapConsumer} from '@romejs/codec-source-map'; async function main(): Promise { - switch ( - process.env.ROME_PROCESS_VERSION === VERSION && - process.env.ROME_PROCESS_TYPE - ) { - case 'master': - return master(); + switch ( + process.env.ROME_PROCESS_VERSION === VERSION && + process.env.ROME_PROCESS_TYPE + ) { + case 'master': + return master(); - case 'worker': - return worker(); + case 'worker': + return worker(); - case 'test-worker': - return testWorker(); + case 'test-worker': + return testWorker(); - default: - return cli(); - } + default: + return cli(); + } } -sourceMapManager.init(); -sourceMapManager.addSourceMap( - BIN.join(), - () => SourceMapConsumer.fromJSON(JSON.parse(readFileTextSync(MAP))), +initErrorHooks(); +sourceMapManager.add( + BIN.join(), + SourceMapConsumer.fromJSONLazy( + BIN.join(), + () => JSON.parse(readFileTextSync(MAP)), + ), ); catchDiagnostics(main).then(({diagnostics}) => { - if (diagnostics !== undefined) { - const reporter = Reporter.fromProcess(); - printDiagnostics({ - diagnostics, - suppressions: [], - printerOptions: { - reporter, - }, - }); - process.exit(1); - } + if (diagnostics !== undefined) { + const reporter = Reporter.fromProcess(); + printDiagnostics({ + diagnostics, + suppressions: [], + printerOptions: { + reporter, + }, + }); + process.exit(1); + } }).catch((err: Error) => { - console.error('Error thrown inside the CLI handler'); - console.error(getErrorStructure(err).stack); + console.error('Error thrown inside the CLI handler'); + console.error(getErrorStructure(err).stack); }); diff --git a/packages/@romejs/cli/cli.ts b/packages/@romejs/cli/cli.ts index f32c0ab1b84..3ee5dae7c18 100644 --- a/packages/@romejs/cli/cli.ts +++ b/packages/@romejs/cli/cli.ts @@ -6,496 +6,496 @@ */ import { - Client, - ClientFlags, - ClientRequestFlags, - DEFAULT_CLIENT_FLAGS, - DEFAULT_CLIENT_REQUEST_FLAGS, - PLATFORMS, - VERSION, - localCommands, - masterCommands, + Client, + ClientFlags, + ClientRequestFlags, + DEFAULT_CLIENT_FLAGS, + DEFAULT_CLIENT_REQUEST_FLAGS, + PLATFORMS, + VERSION, + localCommands, + masterCommands, } from '@romejs/core'; import setProcessTitle from './utils/setProcessTitle'; import {parseCLIFlagsFromProcess} from '@romejs/cli-flags'; import {UnknownFilePath, createAbsoluteFilePath} from '@romejs/path'; import {Consumer} from '@romejs/consume'; import { - ClientProfileOptions, - getFilenameTimestamp, + ClientProfileOptions, + getFilenameTimestamp, } from '@romejs/core/client/Client'; import {commandCategories} from '@romejs/core/common/commands'; import {writeFile} from '@romejs/fs'; import fs = require('fs'); import {markup} from '@romejs/string-markup'; -import {JSONObject} from '@romejs/codec-json'; +import {JSONObject, stringifyJSON} from '@romejs/codec-json'; type CLIFlags = { - logs: boolean; - logWorkers: undefined | boolean; - logPath: undefined | UnknownFilePath; - markersPath: undefined | UnknownFilePath; - rage: boolean; - ragePath: undefined | UnknownFilePath; - profile: boolean; - profilePath: undefined | UnknownFilePath; - profileTimeout: undefined | number; - profileSampling: number; - profileWorkers: boolean; - temporaryDaemon: boolean; + logs: boolean; + logWorkers: undefined | boolean; + logPath: undefined | UnknownFilePath; + markersPath: undefined | UnknownFilePath; + rage: boolean; + ragePath: undefined | UnknownFilePath; + profile: boolean; + profilePath: undefined | UnknownFilePath; + profileTimeout: undefined | number; + profileSampling: number; + profileWorkers: boolean; + temporaryDaemon: boolean; }; export default async function cli() { - setProcessTitle('cli'); - const p = parseCLIFlagsFromProcess({ - programName: process.env.ROME_DEV === '1' ? 'dev-rome' : 'rome', - usage: '[command] [flags]', - version: VERSION, - defineFlags( - c: Consumer, - ): { - cliFlags: CLIFlags; - clientFlags: ClientFlags; - requestFlags: ClientRequestFlags; - } { - // We need this to resolve other flags relative to - // We do the word `void ||` nonsense to avoid setting a default flag value - const cwd = - c.get( - 'cwd', - { - description: 'Specify a different working directory', - }, - ).asAbsoluteFilePathOrVoid() || createAbsoluteFilePath(process.cwd()); - - return { - clientFlags: { - clientName: 'cli', - cwd, - verbose: c.get( - 'verbose', - { - description: 'Output verbose logs', - }, - ).asBoolean(DEFAULT_CLIENT_FLAGS.verbose), - silent: c.get( - 'silent', - { - description: "Don't write anything to the console", - }, - ).asBoolean(DEFAULT_CLIENT_FLAGS.silent), - ...overrideClientFlags, - }, - cliFlags: { - markersPath: c.get( - 'markersPath', - { - description: 'Path where to write markers. When ommitted defaults to Marker-TIMESTAMP.json', - }, - ).asAbsoluteFilePathOrVoid(undefined, cwd), - profile: c.get( - 'profile', - { - description: 'Collect and write profile to disk. Includes profiles for all processes.', - }, - ).asBoolean(false), - profilePath: c.get( - 'profilePath', - { - description: 'Path where to write profile. When omitted defaults to Profile-TIMESTAMP.json', - }, - ).asAbsoluteFilePathOrVoid(undefined, cwd), - profileTimeout: c.get( - 'profileTimeout', - { - inputName: 'millisec', - description: 'Stop the profile after the milliseconds specified. When omitted the profile is of the whole command', - }, - ).asNumberOrVoid(), - profileWorkers: c.get( - 'profileWorkers', - { - description: 'Exclude workers from profile', - }, - ).asBoolean(true), - profileSampling: c.get( - 'profileSampling', - { - description: 'Profiler sampling interval in microseconds', - inputName: 'microsec', - }, - ).asNumber(100), - temporaryDaemon: c.get( - 'temporaryDaemon', - { - description: "Start a daemon, if one isn't already running, for the lifetime of this command", - }, - ).asBoolean(false), - rage: c.get( - 'rage', - { - description: 'Create a rage tarball of debug information', - }, - ).asBoolean(false), - ragePath: c.get( - 'ragePath', - { - description: 'Path where to write rage tarball. When omitted defaults to Rage-TIMESTAMP.tgz', - }, - ).asAbsoluteFilePathOrVoid(undefined, cwd), - logs: c.get( - 'logs', - { - description: 'Output master logs', - }, - ).asBoolean(false), - logWorkers: c.get( - 'logWorkers', - { - description: 'Output worker logs', - }, - ).asBooleanOrVoid(), - logPath: c.get( - 'logPath', - { - description: 'Path where to output logs. When omitted logs are not written anywhere', - }, - ).asAbsoluteFilePathOrVoid(undefined, cwd), - ...overrideCLIFlags, - }, - requestFlags: { - benchmark: c.get( - 'benchmark', - { - description: 'Run a command multiple times, calculating average', - }, - ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.benchmark), - benchmarkIterations: c.get( - 'benchmarkIterations', - { - description: 'The amount of benchmark iterations to perform', - }, - ).asNumber(DEFAULT_CLIENT_REQUEST_FLAGS.benchmarkIterations), - collectMarkers: c.get( - 'collectMarkers', - { - description: 'Collect and write performance markers to disk', - }, - ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.collectMarkers), - timing: c.get( - 'timing', - { - description: 'Dump timing information after running the command', - }, - ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.timing), - review: c.get( - 'review', - { - description: 'Display and perform actions on diagnostics. Only some commands support this.', - }, - ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.review), - watch: c.get( - 'watch', - { - description: 'Keep running command and update on file changes. Only some commands support this.', - }, - ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.watch), - fieri: c.get( - 'fieri', - { - description: 'Head to flavortown', - }, - ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.fieri), - grep: c.get( - 'grep', - { - description: 'Only display diagnostics with messages containing this string', - }, - ).asString(DEFAULT_CLIENT_REQUEST_FLAGS.grep), - inverseGrep: c.get( - 'inverseGrep', - { - description: 'Flip grep match. Only display diagnostics with messages that do NOT contain the grep string', - }, - ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.inverseGrep), - maxDiagnostics: c.get( - 'maxDiagnostics', - { - description: 'Cap the amount of diagnostics displayed', - }, - ).asNumber(DEFAULT_CLIENT_REQUEST_FLAGS.maxDiagnostics), - verboseDiagnostics: c.get( - 'verboseDiagnostics', - { - description: 'Display hidden and truncated diagnostic information', - }, - ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.verboseDiagnostics), - showAllDiagnostics: c.get( - 'showAllDiagnostics', - { - description: 'Display all diagnostics ignoring caps', - }, - ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.showAllDiagnostics), - resolverPlatform: c.get( - 'resolverPlatform', - { - description: 'Specify the platform for module resolution', - inputName: 'platform', - }, - ).asStringSetOrVoid(PLATFORMS), - resolverScale: c.get( - 'resolverScale', - { - description: 'Specify the image scale for module resolution', - }, - ).asNumberOrVoid(), - resolverMocks: c.get( - 'resolverMocks', - { - description: 'Enable mocks for module resolution', - }, - ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.resolverMocks), - ...overrideRequestFlags, - }, - }; - }, - }); - - let command = ''; - let overrideClientFlags: undefined | Partial; - let overrideRequestFlags: undefined | Partial; - let overrideCLIFlags: Partial = {}; - let commandFlags: JSONObject = {}; - let args: Array = []; - - // Create command handlers. We use a set here since we may have some conflicting master and local command names. We always want the local command to take precedence. - const commandNames = new Set([ - ...localCommands.keys(), - ...masterCommands.keys(), - ]); - for (const cmd of commandNames) { - const local = localCommands.get(cmd); - if (local !== undefined) { - p.command({ - name: cmd, - category: local.category, - description: local.description, - defineFlags: local.defineFlags, - ignoreFlags: local.ignoreFlags, - examples: local.examples, - usage: local.usage, - callback(_commandFlags) { - commandFlags = _commandFlags; - args = p.getArgs(); - command = cmd; - }, - }); - continue; - } - - const master = masterCommands.get(cmd); - if (master !== undefined) { - p.command({ - name: cmd, - category: master.category, - description: master.description, - defineFlags: master.defineFlags, - ignoreFlags: master.ignoreFlags, - usage: master.usage, - examples: master.examples, - callback(_commandFlags) { - commandFlags = _commandFlags; - overrideClientFlags = master.overrideClientFlags; - overrideRequestFlags = master.overrideRequestFlags; - - args = p.getArgs(); - command = cmd; - }, - }); - } - } - - // Mock `rage` command that just uses the master noop command and adds the --rage flag - p.command({ - name: 'rage', - category: commandCategories.INTERNAL, - description: 'TODO', - callback() { - overrideCLIFlags = { - rage: true, - }; - - command = '_noop'; - }, - }); - - // Mock `logs` command that just uses the master noop command and adds the --logs flag - p.command({ - name: 'logs', - category: commandCategories.INTERNAL, - description: 'TODO', - callback() { - overrideCLIFlags = { - logs: true, - }; - - command = '_noop'; - }, - }); - - // Initialize flags - let {clientFlags, cliFlags, requestFlags} = await p.init(); - - // Force collection of markers if markersPath or we are raging - if (cliFlags.markersPath || cliFlags.rage) { - requestFlags.collectMarkers = true; - } - - // Force logs when logPath or logWorkers is set - if (cliFlags.logPath !== undefined || cliFlags.logWorkers === true) { - cliFlags.logs = true; - } - - p.commandRequired(); - - const client = new Client({ - globalErrorHandlers: true, - flags: clientFlags, - stdin: process.stdin, - stdout: process.stdout, - stderr: process.stderr, - }); - - client.bridgeAttachedEvent.subscribe(async () => { - const profileOptions: ClientProfileOptions = { - samplingInterval: cliFlags.profileSampling, - timeoutInterval: cliFlags.profileTimeout, - includeWorkers: cliFlags.profileWorkers, - }; - - if (cliFlags.rage) { - const {ragePath} = cliFlags; - const filename = clientFlags.cwd.resolve( - ragePath === undefined ? `Rage-${getFilenameTimestamp()}.tgz` : ragePath, - ).join(); - await client.rage(filename, profileOptions); - return; - } - - if (cliFlags.profile) { - await client.profile( - profileOptions, - async (events) => { - const {cwd} = clientFlags; - const {profilePath} = cliFlags; - - const resolvedProfilePath = cwd.resolve( - profilePath === undefined - ? `Profile-${getFilenameTimestamp()}.json` - : profilePath, - ); - - const str = JSON.stringify(events, undefined, ' '); - await writeFile(resolvedProfilePath, str); - - client.reporter.success( - markup`Wrote CPU profile to `, - ); - }, - ); - } - - if (cliFlags.logs) { - let fileout: undefined | fs.WriteStream; - if (cliFlags.logPath !== undefined) { - fileout = fs.createWriteStream( - clientFlags.cwd.resolve(cliFlags.logPath).join(), - ); - - client.endEvent.subscribe(() => { - if (fileout !== undefined) { - fileout.end(); - } - }); - } - - await client.subscribeLogs( - cliFlags.logWorkers === true, - (chunk) => { - if (fileout === undefined) { - client.reporter.writeAll(chunk); - } else { - fileout.write(chunk); - } - }, - ); - } - }); - - if (cliFlags.temporaryDaemon) { - await client.forceStartDaemon(); - } - - const res = await client.query({ - commandName: command, - commandFlags, - args, - requestFlags, - // Daemon would have been started before, so terminate when we complete - terminateWhenIdle: cliFlags.temporaryDaemon, - // We don't use the data result, so no point transporting it over the bridge - noData: true, - }); - await client.end(); - - if (res.type === 'SUCCESS') { - // Write markers if we were collecting them - if (requestFlags.collectMarkers) { - const markersPath = clientFlags.cwd.resolve( - cliFlags.markersPath === undefined - ? `Markers-${getFilenameTimestamp()}.json` - : cliFlags.markersPath, - ); - - await writeFile(markersPath, JSON.stringify(res.markers, null, ' ')); - - client.reporter.success( - markup`Wrote markers to `, - ); - } - } - - switch (res.type) { - case 'ERROR': { - if (!res.handled) { - console.error('Unhandled CLI query error'); - console.error(res.stack); - } - process.exit(1); - break; - } - - case 'INVALID_REQUEST': { - if (res.showHelp) { - await p.showHelp(); - } - process.exit(1); - break; - } - - case 'DIAGNOSTICS': { - process.exit(res.diagnostics.length === 0 ? 0 : 1); - break; - } - - case 'CANCELLED': { - process.exit(0); - break; - } - - case 'SUCCESS': { - process.exit(0); - break; - } - } + setProcessTitle('cli'); + const p = parseCLIFlagsFromProcess({ + programName: process.env.ROME_DEV === '1' ? 'dev-rome' : 'rome', + usage: '[command] [flags]', + version: VERSION, + defineFlags( + c: Consumer, + ): { + cliFlags: CLIFlags; + clientFlags: ClientFlags; + requestFlags: ClientRequestFlags; + } { + // We need this to resolve other flags relative to + // We do the word `void ||` nonsense to avoid setting a default flag value + const cwd = + c.get( + 'cwd', + { + description: 'Specify a different working directory', + }, + ).asAbsoluteFilePathOrVoid() || createAbsoluteFilePath(process.cwd()); + + return { + clientFlags: { + clientName: 'cli', + cwd, + verbose: c.get( + 'verbose', + { + description: 'Output verbose logs', + }, + ).asBoolean(DEFAULT_CLIENT_FLAGS.verbose), + silent: c.get( + 'silent', + { + description: "Don't write anything to the console", + }, + ).asBoolean(DEFAULT_CLIENT_FLAGS.silent), + ...overrideClientFlags, + }, + cliFlags: { + markersPath: c.get( + 'markersPath', + { + description: 'Path where to write markers. When ommitted defaults to Marker-TIMESTAMP.json', + }, + ).asAbsoluteFilePathOrVoid(undefined, cwd), + profile: c.get( + 'profile', + { + description: 'Collect and write profile to disk. Includes profiles for all processes.', + }, + ).asBoolean(false), + profilePath: c.get( + 'profilePath', + { + description: 'Path where to write profile. When omitted defaults to Profile-TIMESTAMP.json', + }, + ).asAbsoluteFilePathOrVoid(undefined, cwd), + profileTimeout: c.get( + 'profileTimeout', + { + inputName: 'millisec', + description: 'Stop the profile after the milliseconds specified. When omitted the profile is of the whole command', + }, + ).asNumberOrVoid(), + profileWorkers: c.get( + 'profileWorkers', + { + description: 'Exclude workers from profile', + }, + ).asBoolean(true), + profileSampling: c.get( + 'profileSampling', + { + description: 'Profiler sampling interval in microseconds', + inputName: 'microsec', + }, + ).asNumber(100), + temporaryDaemon: c.get( + 'temporaryDaemon', + { + description: "Start a daemon, if one isn't already running, for the lifetime of this command", + }, + ).asBoolean(false), + rage: c.get( + 'rage', + { + description: 'Create a rage tarball of debug information', + }, + ).asBoolean(false), + ragePath: c.get( + 'ragePath', + { + description: 'Path where to write rage tarball. When omitted defaults to Rage-TIMESTAMP.tgz', + }, + ).asAbsoluteFilePathOrVoid(undefined, cwd), + logs: c.get( + 'logs', + { + description: 'Output master logs', + }, + ).asBoolean(false), + logWorkers: c.get( + 'logWorkers', + { + description: 'Output worker logs', + }, + ).asBooleanOrVoid(), + logPath: c.get( + 'logPath', + { + description: 'Path where to output logs. When omitted logs are not written anywhere', + }, + ).asAbsoluteFilePathOrVoid(undefined, cwd), + ...overrideCLIFlags, + }, + requestFlags: { + benchmark: c.get( + 'benchmark', + { + description: 'Run a command multiple times, calculating average', + }, + ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.benchmark), + benchmarkIterations: c.get( + 'benchmarkIterations', + { + description: 'The amount of benchmark iterations to perform', + }, + ).asNumber(DEFAULT_CLIENT_REQUEST_FLAGS.benchmarkIterations), + collectMarkers: c.get( + 'collectMarkers', + { + description: 'Collect and write performance markers to disk', + }, + ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.collectMarkers), + timing: c.get( + 'timing', + { + description: 'Dump timing information after running the command', + }, + ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.timing), + review: c.get( + 'review', + { + description: 'Display and perform actions on diagnostics. Only some commands support this.', + }, + ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.review), + watch: c.get( + 'watch', + { + description: 'Keep running command and update on file changes. Only some commands support this.', + }, + ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.watch), + fieri: c.get( + 'fieri', + { + description: 'Head to flavortown', + }, + ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.fieri), + grep: c.get( + 'grep', + { + description: 'Only display diagnostics with messages containing this string', + }, + ).asString(DEFAULT_CLIENT_REQUEST_FLAGS.grep), + inverseGrep: c.get( + 'inverseGrep', + { + description: 'Flip grep match. Only display diagnostics with messages that do NOT contain the grep string', + }, + ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.inverseGrep), + maxDiagnostics: c.get( + 'maxDiagnostics', + { + description: 'Cap the amount of diagnostics displayed', + }, + ).asNumber(DEFAULT_CLIENT_REQUEST_FLAGS.maxDiagnostics), + verboseDiagnostics: c.get( + 'verboseDiagnostics', + { + description: 'Display hidden and truncated diagnostic information', + }, + ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.verboseDiagnostics), + showAllDiagnostics: c.get( + 'showAllDiagnostics', + { + description: 'Display all diagnostics ignoring caps', + }, + ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.showAllDiagnostics), + resolverPlatform: c.get( + 'resolverPlatform', + { + description: 'Specify the platform for module resolution', + inputName: 'platform', + }, + ).asStringSetOrVoid(PLATFORMS), + resolverScale: c.get( + 'resolverScale', + { + description: 'Specify the image scale for module resolution', + }, + ).asNumberOrVoid(), + resolverMocks: c.get( + 'resolverMocks', + { + description: 'Enable mocks for module resolution', + }, + ).asBoolean(DEFAULT_CLIENT_REQUEST_FLAGS.resolverMocks), + ...overrideRequestFlags, + }, + }; + }, + }); + + let command = ''; + let overrideClientFlags: undefined | Partial; + let overrideRequestFlags: undefined | Partial; + let overrideCLIFlags: Partial = {}; + let commandFlags: JSONObject = {}; + let args: Array = []; + + // Create command handlers. We use a set here since we may have some conflicting master and local command names. We always want the local command to take precedence. + const commandNames = new Set([ + ...localCommands.keys(), + ...masterCommands.keys(), + ]); + for (const cmd of commandNames) { + const local = localCommands.get(cmd); + if (local !== undefined) { + p.command({ + name: cmd, + category: local.category, + description: local.description, + defineFlags: local.defineFlags, + ignoreFlags: local.ignoreFlags, + examples: local.examples, + usage: local.usage, + callback(_commandFlags) { + commandFlags = _commandFlags; + args = p.getArgs(); + command = cmd; + }, + }); + continue; + } + + const master = masterCommands.get(cmd); + if (master !== undefined) { + p.command({ + name: cmd, + category: master.category, + description: master.description, + defineFlags: master.defineFlags, + ignoreFlags: master.ignoreFlags, + usage: master.usage, + examples: master.examples, + callback(_commandFlags) { + commandFlags = _commandFlags; + overrideClientFlags = master.overrideClientFlags; + overrideRequestFlags = master.overrideRequestFlags; + + args = p.getArgs(); + command = cmd; + }, + }); + } + } + + // Mock `rage` command that just uses the master noop command and adds the --rage flag + p.command({ + name: 'rage', + category: commandCategories.INTERNAL, + description: 'TODO', + callback() { + overrideCLIFlags = { + rage: true, + }; + + command = '_noop'; + }, + }); + + // Mock `logs` command that just uses the master noop command and adds the --logs flag + p.command({ + name: 'logs', + category: commandCategories.INTERNAL, + description: 'TODO', + callback() { + overrideCLIFlags = { + logs: true, + }; + + command = '_noop'; + }, + }); + + // Initialize flags + let {clientFlags, cliFlags, requestFlags} = await p.init(); + + // Force collection of markers if markersPath or we are raging + if (cliFlags.markersPath || cliFlags.rage) { + requestFlags.collectMarkers = true; + } + + // Force logs when logPath or logWorkers is set + if (cliFlags.logPath !== undefined || cliFlags.logWorkers === true) { + cliFlags.logs = true; + } + + p.commandRequired(); + + const client = new Client({ + globalErrorHandlers: true, + flags: clientFlags, + stdin: process.stdin, + stdout: process.stdout, + stderr: process.stderr, + }); + + client.bridgeAttachedEvent.subscribe(async () => { + const profileOptions: ClientProfileOptions = { + samplingInterval: cliFlags.profileSampling, + timeoutInterval: cliFlags.profileTimeout, + includeWorkers: cliFlags.profileWorkers, + }; + + if (cliFlags.rage) { + const {ragePath} = cliFlags; + const filename = clientFlags.cwd.resolve( + ragePath === undefined ? `Rage-${getFilenameTimestamp()}.tgz` : ragePath, + ).join(); + await client.rage(filename, profileOptions); + return; + } + + if (cliFlags.profile) { + await client.profile( + profileOptions, + async (events) => { + const {cwd} = clientFlags; + const {profilePath} = cliFlags; + + const resolvedProfilePath = cwd.resolve( + profilePath === undefined + ? `Profile-${getFilenameTimestamp()}.json` + : profilePath, + ); + + const str = stringifyJSON(events); + await writeFile(resolvedProfilePath, str); + + client.reporter.success( + markup`Wrote CPU profile to `, + ); + }, + ); + } + + if (cliFlags.logs) { + let fileout: undefined | fs.WriteStream; + if (cliFlags.logPath !== undefined) { + fileout = fs.createWriteStream( + clientFlags.cwd.resolve(cliFlags.logPath).join(), + ); + + client.endEvent.subscribe(() => { + if (fileout !== undefined) { + fileout.end(); + } + }); + } + + await client.subscribeLogs( + cliFlags.logWorkers === true, + (chunk) => { + if (fileout === undefined) { + client.reporter.writeAll(chunk); + } else { + fileout.write(chunk); + } + }, + ); + } + }); + + if (cliFlags.temporaryDaemon) { + await client.forceStartDaemon(); + } + + const res = await client.query({ + commandName: command, + commandFlags, + args, + requestFlags, + // Daemon would have been started before, so terminate when we complete + terminateWhenIdle: cliFlags.temporaryDaemon, + // We don't use the data result, so no point transporting it over the bridge + noData: true, + }); + await client.end(); + + if (res.type === 'SUCCESS') { + // Write markers if we were collecting them + if (requestFlags.collectMarkers) { + const markersPath = clientFlags.cwd.resolve( + cliFlags.markersPath === undefined + ? `Markers-${getFilenameTimestamp()}.json` + : cliFlags.markersPath, + ); + + await writeFile(markersPath, stringifyJSON(res.markers)); + + client.reporter.success( + markup`Wrote markers to `, + ); + } + } + + switch (res.type) { + case 'ERROR': { + if (!res.handled) { + console.error('Unhandled CLI query error'); + console.error(res.stack); + } + process.exit(1); + break; + } + + case 'INVALID_REQUEST': { + if (res.showHelp) { + await p.showHelp(); + } + process.exit(1); + break; + } + + case 'DIAGNOSTICS': { + process.exit(res.diagnostics.length === 0 ? 0 : 1); + break; + } + + case 'CANCELLED': { + process.exit(0); + break; + } + + case 'SUCCESS': { + process.exit(0); + break; + } + } } diff --git a/packages/@romejs/cli/master.ts b/packages/@romejs/cli/master.ts index 26ea95e5a4e..5f382db1b1f 100644 --- a/packages/@romejs/cli/master.ts +++ b/packages/@romejs/cli/master.ts @@ -13,49 +13,49 @@ import net = require('net'); import {exists, unlink} from '@romejs/fs'; export default async function master() { - setProcessTitle('master'); - - const master = new Master({ - dedicated: true, - globalErrorHandlers: true, - }); - - await master.init(); - - const socketServer = net.createServer(function(socket) { - const client = createBridgeFromSocket( - MasterBridge, - socket, - { - type: 'client', - }, - ); - master.attachToBridge(client); - }); - - if (await exists(SOCKET_PATH)) { - await unlink(SOCKET_PATH); - } - - socketServer.listen( - SOCKET_PATH.join(), - () => { - const socket = net.createConnection( - CLI_SOCKET_PATH.join(), - () => { - socket.end(); - }, - ); - - socket.on( - 'error', - (err) => { - // Socket error occured, cli could have died before it caught us - err; - console.log(err); - process.exit(); - }, - ); - }, - ); + setProcessTitle('master'); + + const master = new Master({ + dedicated: true, + globalErrorHandlers: true, + }); + + await master.init(); + + const socketServer = net.createServer(function(socket) { + const client = createBridgeFromSocket( + MasterBridge, + socket, + { + type: 'client', + }, + ); + master.attachToBridge(client); + }); + + if (await exists(SOCKET_PATH)) { + await unlink(SOCKET_PATH); + } + + socketServer.listen( + SOCKET_PATH.join(), + () => { + const socket = net.createConnection( + CLI_SOCKET_PATH.join(), + () => { + socket.end(); + }, + ); + + socket.on( + 'error', + (err) => { + // Socket error occured, cli could have died before it caught us + err; + console.log(err); + process.exit(); + }, + ); + }, + ); } diff --git a/packages/@romejs/cli/testWorker.ts b/packages/@romejs/cli/testWorker.ts index b032a91a3da..c6ef6756290 100644 --- a/packages/@romejs/cli/testWorker.ts +++ b/packages/@romejs/cli/testWorker.ts @@ -11,18 +11,18 @@ import {parseCLIFlagsFromProcess} from '@romejs/cli-flags'; import {TestWorkerFlags} from '@romejs/core/test-worker/TestWorker'; export default async function testWorker() { - setProcessTitle('test-worker'); + setProcessTitle('test-worker'); - const parser = parseCLIFlagsFromProcess({ - programName: 'rome test-worker', - defineFlags(c): TestWorkerFlags { - return { - inspectorPort: c.get('inspectorPort').asNumberFromString(), - }; - }, - }); - const flags: TestWorkerFlags = await parser.init(); + const parser = parseCLIFlagsFromProcess({ + programName: 'rome test-worker', + defineFlags(c): TestWorkerFlags { + return { + inspectorPort: c.get('inspectorPort').asNumberFromString(), + }; + }, + }); + const flags: TestWorkerFlags = await parser.init(); - const worker = new TestWorker(); - worker.init(flags); + const worker = new TestWorker(); + worker.init(flags); } diff --git a/packages/@romejs/cli/utils/setProcessTitle.ts b/packages/@romejs/cli/utils/setProcessTitle.ts index b463388a57b..2227c65a43f 100644 --- a/packages/@romejs/cli/utils/setProcessTitle.ts +++ b/packages/@romejs/cli/utils/setProcessTitle.ts @@ -6,5 +6,5 @@ */ export default function setProcessTitle(title: string) { - process.title = `rome-${title}`; + process.title = `rome-${title}`; } diff --git a/packages/@romejs/cli/worker.ts b/packages/@romejs/cli/worker.ts index 56d5733b737..1b78b2d2835 100644 --- a/packages/@romejs/cli/worker.ts +++ b/packages/@romejs/cli/worker.ts @@ -10,17 +10,17 @@ import {createBridgeFromParentProcess} from '@romejs/events'; import {Worker, WorkerBridge} from '@romejs/core'; export default async function worker() { - setProcessTitle('worker'); - const bridge = createBridgeFromParentProcess( - WorkerBridge, - { - type: 'server', - }, - ); - const worker = new Worker({ - bridge, - globalErrorHandlers: true, - }); - await worker.init(); - bridge.handshake(); + setProcessTitle('worker'); + const bridge = createBridgeFromParentProcess( + WorkerBridge, + { + type: 'server', + }, + ); + const worker = new Worker({ + bridge, + globalErrorHandlers: true, + }); + await worker.init(); + bridge.handshake(); } diff --git a/packages/@romejs/codec-js-manifest/convert.ts b/packages/@romejs/codec-js-manifest/convert.ts index 256d9e2290b..94eb7022611 100644 --- a/packages/@romejs/codec-js-manifest/convert.ts +++ b/packages/@romejs/codec-js-manifest/convert.ts @@ -8,10 +8,10 @@ import {stringifySPDXLicense} from '@romejs/codec-spdx-license'; import {ManifestDependencies, stringifyDependencyPattern} from './dependencies'; import { - JSONManifest, - JSONManifestExports, - Manifest, - ManifestExports, + JSONManifest, + JSONManifestExports, + Manifest, + ManifestExports, } from './types'; import {stringifySemver} from '@romejs/codec-semver'; import {Dict} from '@romejs/typescript-helpers'; @@ -19,119 +19,119 @@ import {stringifyPathPattern} from '@romejs/path-match'; import {manifestNameToString} from './name'; export function convertManifestToJSON(manifest: Manifest): JSONManifest { - return { - // Include unknown properties from the initial package.json - ...manifest.raw, - name: manifestNameToString(manifest.name), - description: manifest.description, - private: manifest.private, - type: manifest.type, - homepage: manifest.homepage, - repository: manifest.repository, - bugs: manifest.bugs, - main: manifest.main, - // TODO we now support fallbacks which means manifest.exports is lossy - //exports: exportsToObject(manifest.exports), - // rome-ignore lint/noExplicitAny - exports: (manifest.raw.exports as any), - author: manifest.author, - contributors: manifest.contributors, - maintainers: manifest.maintainers, - version: manifest.version === undefined - ? undefined - : stringifySemver(manifest.version), - license: manifest.license === undefined - ? undefined - : stringifySPDXLicense(manifest.license), - files: maybeArray( - manifest.files.map((pattern) => stringifyPathPattern(pattern)), - ), - keywords: maybeArray(manifest.keywords), - cpu: maybeArray(manifest.cpu), - os: maybeArray(manifest.os), - bin: mapToObject(manifest.bin), - scripts: mapToObject(manifest.scripts), - engines: mapToObject(manifest.engines), - dependencies: dependencyMapToObject(manifest.dependencies), - devDependencies: dependencyMapToObject(manifest.devDependencies), - optionalDependencies: dependencyMapToObject(manifest.optionalDependencies), - peerDependencies: dependencyMapToObject(manifest.peerDependencies), - // Common misspelling. If this existed then it was turned into bundledDependencies - bundleDependencies: undefined, - bundledDependencies: maybeArray(manifest.bundledDependencies), - }; + return { + // Include unknown properties from the initial package.json + ...manifest.raw, + name: manifestNameToString(manifest.name), + description: manifest.description, + private: manifest.private, + type: manifest.type, + homepage: manifest.homepage, + repository: manifest.repository, + bugs: manifest.bugs, + main: manifest.main, + // TODO we now support fallbacks which means manifest.exports is lossy + //exports: exportsToObject(manifest.exports), + // rome-ignore lint/noExplicitAny + exports: (manifest.raw.exports as any), + author: manifest.author, + contributors: manifest.contributors, + maintainers: manifest.maintainers, + version: manifest.version === undefined + ? undefined + : stringifySemver(manifest.version), + license: manifest.license === undefined + ? undefined + : stringifySPDXLicense(manifest.license), + files: maybeArray( + manifest.files.map((pattern) => stringifyPathPattern(pattern)), + ), + keywords: maybeArray(manifest.keywords), + cpu: maybeArray(manifest.cpu), + os: maybeArray(manifest.os), + bin: mapToObject(manifest.bin), + scripts: mapToObject(manifest.scripts), + engines: mapToObject(manifest.engines), + dependencies: dependencyMapToObject(manifest.dependencies), + devDependencies: dependencyMapToObject(manifest.devDependencies), + optionalDependencies: dependencyMapToObject(manifest.optionalDependencies), + peerDependencies: dependencyMapToObject(manifest.peerDependencies), + // Common misspelling. If this existed then it was turned into bundledDependencies + bundleDependencies: undefined, + bundledDependencies: maybeArray(manifest.bundledDependencies), + }; } function exportsToObject( - exports: boolean | ManifestExports, + exports: boolean | ManifestExports, ): undefined | false | JSONManifestExports { - if (exports === false) { - return false; - } + if (exports === false) { + return false; + } - if (exports === true) { - return; - } + if (exports === true) { + return; + } - if (exports.size === 0) { - return {}; - } + if (exports.size === 0) { + return {}; + } - const obj: JSONManifestExports = {}; + const obj: JSONManifestExports = {}; - for (const [key, entries] of exports) { - if (entries.size === 1) { - const def = entries.get('default'); - if (def !== undefined) { - obj[key.join()] = def.relative.join(); - continue; - } - } + for (const [key, entries] of exports) { + if (entries.size === 1) { + const def = entries.get('default'); + if (def !== undefined) { + obj[key.join()] = def.relative.join(); + continue; + } + } - const entriesObj: Dict = {}; - for (const [type, alias] of entries) { - entriesObj[type] = alias.relative.join(); - } - obj[key.join()] = entriesObj; - } + const entriesObj: Dict = {}; + for (const [type, alias] of entries) { + entriesObj[type] = alias.relative.join(); + } + obj[key.join()] = entriesObj; + } - return obj; + return obj; } exportsToObject; function maybeArray(items: Array): undefined | Array { - if (items.length === 0) { - return undefined; - } else { - return items; - } + if (items.length === 0) { + return undefined; + } else { + return items; + } } function mapToObject(map: Map): undefined | Dict { - if (map.size === 0) { - return; - } + if (map.size === 0) { + return; + } - const obj: Dict = {}; - for (const [key, value] of map) { - obj[key] = value; - } - return obj; + const obj: Dict = {}; + for (const [key, value] of map) { + obj[key] = value; + } + return obj; } function dependencyMapToObject( - map: ManifestDependencies, + map: ManifestDependencies, ): undefined | Dict { - if (map.size === 0) { - return; - } + if (map.size === 0) { + return; + } - const obj: Dict = {}; - for (const [name, pattern] of map) { - const key = manifestNameToString(name); - if (key !== undefined) { - obj[key] = stringifyDependencyPattern(pattern); - } - } - return obj; + const obj: Dict = {}; + for (const [name, pattern] of map) { + const key = manifestNameToString(name); + if (key !== undefined) { + obj[key] = stringifyDependencyPattern(pattern); + } + } + return obj; } diff --git a/packages/@romejs/codec-js-manifest/dependencies.test.md b/packages/@romejs/codec-js-manifest/dependencies.test.md index 36144627dd8..b51aeaa191f 100644 --- a/packages/@romejs/codec-js-manifest/dependencies.test.md +++ b/packages/@romejs/codec-js-manifest/dependencies.test.md @@ -8,11 +8,11 @@ ```javascript npm { - name: Object { - org: undefined - packageName: 'foo' - } - range: undefined + name: Object { + org: undefined + packageName: 'foo' + } + range: undefined } ``` @@ -20,11 +20,11 @@ npm { ```javascript npm { - name: Object { - org: undefined - packageName: '@foo/bar' - } - range: undefined + name: Object { + org: undefined + packageName: '@foo/bar' + } + range: undefined } ``` @@ -32,30 +32,30 @@ npm { ```javascript npm { - name: Object { - org: undefined - packageName: 'foo' - } - range: AbsoluteVersion { - build: Array [] - major: 1 - minor: 0 - patch: 0 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - } + name: Object { + org: undefined + packageName: 'foo' + } + range: AbsoluteVersion { + build: Array [] + major: 1 + minor: 0 + patch: 0 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + } } ``` @@ -63,29 +63,29 @@ npm { ```javascript npm { - name: Object { - org: undefined - packageName: '@foo/bar' - } - range: AbsoluteVersion { - build: Array [] - major: 1 - minor: 0 - patch: 0 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - } + name: Object { + org: undefined + packageName: '@foo/bar' + } + range: AbsoluteVersion { + build: Array [] + major: 1 + minor: 0 + patch: 0 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + } } ``` diff --git a/packages/@romejs/codec-js-manifest/dependencies.test.ts b/packages/@romejs/codec-js-manifest/dependencies.test.ts index a94df499511..8ae4928b259 100644 --- a/packages/@romejs/codec-js-manifest/dependencies.test.ts +++ b/packages/@romejs/codec-js-manifest/dependencies.test.ts @@ -10,28 +10,22 @@ import {parseDependencyPattern} from './dependencies'; import {consumeUnknown} from '@romejs/consume'; test( - 'can parse npm dependency patterns', - async (t) => { - t.snapshot( - parseDependencyPattern(consumeUnknown('npm:foo', 'parse/json'), false), - ); - t.snapshot( - parseDependencyPattern( - consumeUnknown('npm:@foo/bar', 'parse/json'), - false, - ), - ); - t.snapshot( - parseDependencyPattern( - consumeUnknown('npm:foo@1.0.0', 'parse/json'), - false, - ), - ); - t.snapshot( - parseDependencyPattern( - consumeUnknown('npm:@foo/bar@1.0.0', 'parse/json'), - false, - ), - ); - }, + 'can parse npm dependency patterns', + async (t) => { + t.snapshot( + parseDependencyPattern(consumeUnknown('npm:foo', 'parse/json'), false), + ); + t.snapshot( + parseDependencyPattern(consumeUnknown('npm:@foo/bar', 'parse/json'), false), + ); + t.snapshot( + parseDependencyPattern(consumeUnknown('npm:foo@1.0.0', 'parse/json'), false), + ); + t.snapshot( + parseDependencyPattern( + consumeUnknown('npm:@foo/bar@1.0.0', 'parse/json'), + false, + ), + ); + }, ); diff --git a/packages/@romejs/codec-js-manifest/dependencies.ts b/packages/@romejs/codec-js-manifest/dependencies.ts index 97aa6f63c89..8baed9a46cc 100644 --- a/packages/@romejs/codec-js-manifest/dependencies.ts +++ b/packages/@romejs/codec-js-manifest/dependencies.ts @@ -7,9 +7,9 @@ import {Consumer} from '@romejs/consume'; import { - SemverRangeNode, - parseSemverRange, - stringifySemver, + SemverRangeNode, + parseSemverRange, + stringifySemver, } from '@romejs/codec-semver'; import {tryParseWithOptionalOffsetPosition} from '@romejs/parser-core'; import {UnknownFilePath, createUnknownFilePath} from '@romejs/path'; @@ -19,242 +19,242 @@ import {descriptions} from '@romejs/diagnostics'; import {ManifestName} from './types'; export type DependencyPattern = - | HostedGitPattern - | HTTPTarballPattern - | SemverPattern - | GitPattern - | FilePattern - | TagPattern - | NpmPattern - | LinkPattern; + | HostedGitPattern + | HTTPTarballPattern + | SemverPattern + | GitPattern + | FilePattern + | TagPattern + | NpmPattern + | LinkPattern; export type ManifestDependencies = Map; type UrlWithHash = { - url: string; - hash: string | undefined; + url: string; + hash: string | undefined; }; export function stringifyDependencyPattern(pattern: DependencyPattern): string { - switch (pattern.type) { - case 'hosted-git': { - let str = `${pattern.host}:${pattern.user}/${pattern.repo}`; - if (pattern.commitish !== undefined) { - str += `#${pattern.commitish}`; - } - return str; - } - - case 'file': - return `file:${pattern.path}`; - - case 'semver': - return stringifySemver(pattern.range); - - case 'tag': - return pattern.tag; - - case 'git': - case 'http-tarball': - if (pattern.hash === undefined) { - return pattern.url; - } else { - return `${pattern.url}#${pattern.hash}`; - } - - case 'npm': { - let str = `${NPM_PREFIX}${pattern.name}`; - if (pattern.range !== undefined) { - str += `@${stringifySemver(pattern.range)}`; - } - return str; - } - - case 'link': - return `${LINK_PREFIX}${pattern.path.join()}`; - } + switch (pattern.type) { + case 'hosted-git': { + let str = `${pattern.host}:${pattern.user}/${pattern.repo}`; + if (pattern.commitish !== undefined) { + str += `#${pattern.commitish}`; + } + return str; + } + + case 'file': + return `file:${pattern.path}`; + + case 'semver': + return stringifySemver(pattern.range); + + case 'tag': + return pattern.tag; + + case 'git': + case 'http-tarball': + if (pattern.hash === undefined) { + return pattern.url; + } else { + return `${pattern.url}#${pattern.hash}`; + } + + case 'npm': { + let str = `${NPM_PREFIX}${pattern.name}`; + if (pattern.range !== undefined) { + str += `@${stringifySemver(pattern.range)}`; + } + return str; + } + + case 'link': + return `${LINK_PREFIX}${pattern.path.join()}`; + } } function explodeHashUrl(pattern: string, consumer: Consumer): UrlWithHash { - const parts = pattern.split('#'); + const parts = pattern.split('#'); - if (parts.length > 2) { - consumer.unexpected(descriptions.MANIFEST.TOO_MANY_HASH_PARTS); - } + if (parts.length > 2) { + consumer.unexpected(descriptions.MANIFEST.TOO_MANY_HASH_PARTS); + } - return { - hash: parts[1], - url: parts[0], - }; + return { + hash: parts[1], + url: parts[0], + }; } function removePrefix(prefix: string, value: string): string { - if (value.startsWith(prefix)) { - return value.slice(prefix.length); - } else { - return value; - } + if (value.startsWith(prefix)) { + return value.slice(prefix.length); + } else { + return value; + } } //# HOSTED GIT export type HostedGitHost = 'bitbucket' | 'github' | 'gist' | 'gitlab'; type IncompleteHostedGitPattern = { - type: 'hosted-git'; - host: HostedGitHost; - user: string; - repo: string; - commitish: undefined | string; + type: 'hosted-git'; + host: HostedGitHost; + user: string; + repo: string; + commitish: undefined | string; }; type HostedGitPattern = IncompleteHostedGitPattern & { - url: string; + url: string; }; const GITHUB_SHORTHAND = /^[^:@%\/\s.\-][^:@%\/\s]*[\/][^:@\s\/%]+(?:#.*)?$/; const HOSTED_GIT_PREFIXES: Array = [ - 'bitbucket', - 'github', - 'gist', - 'gitlab', + 'bitbucket', + 'github', + 'gist', + 'gitlab', ]; function parseHostedGit( - host: HostedGitHost, - pattern: string, - consumer: Consumer, + host: HostedGitHost, + pattern: string, + consumer: Consumer, ): HostedGitPattern { - // Extract and trim hash - let commitish: undefined | string; - if (pattern.includes('#')) { - const hashIndex = pattern.indexOf('#'); - commitish = pattern.slice(hashIndex + 1); - pattern = pattern.slice(0, hashIndex - 1); - } - - const parts = pattern.split('/'); - if (parts.length > 2) { - consumer.unexpected(descriptions.MANIFEST.TOO_MANY_HOSTED_GIT_PARTS); - } - - let user = parts[0]; - if (user === undefined) { - consumer.unexpected(descriptions.MANIFEST.MISSING_HOSTED_GIT_USER); - user = 'unknown'; - } - - let repo = parts[1]; - if (repo === undefined) { - consumer.unexpected(descriptions.MANIFEST.MISSING_HOSTED_GIT_REPO); - repo = 'unknown'; - } - - const incomplete: IncompleteHostedGitPattern = { - type: 'hosted-git', - host, - user, - repo, - commitish, - }; - - return { - ...incomplete, - url: getHostedGitURL(incomplete), - }; + // Extract and trim hash + let commitish: undefined | string; + if (pattern.includes('#')) { + const hashIndex = pattern.indexOf('#'); + commitish = pattern.slice(hashIndex + 1); + pattern = pattern.slice(0, hashIndex - 1); + } + + const parts = pattern.split('/'); + if (parts.length > 2) { + consumer.unexpected(descriptions.MANIFEST.TOO_MANY_HOSTED_GIT_PARTS); + } + + let user = parts[0]; + if (user === undefined) { + consumer.unexpected(descriptions.MANIFEST.MISSING_HOSTED_GIT_USER); + user = 'unknown'; + } + + let repo = parts[1]; + if (repo === undefined) { + consumer.unexpected(descriptions.MANIFEST.MISSING_HOSTED_GIT_REPO); + repo = 'unknown'; + } + + const incomplete: IncompleteHostedGitPattern = { + type: 'hosted-git', + host, + user, + repo, + commitish, + }; + + return { + ...incomplete, + url: getHostedGitURL(incomplete), + }; } export function getHostedGitURL(pattern: IncompleteHostedGitPattern): string { - switch (pattern.host) { - case 'bitbucket': - return ''; + switch (pattern.host) { + case 'bitbucket': + return ''; - case 'gitlab': - case 'gist': - return ''; + case 'gitlab': + case 'gist': + return ''; - case 'github': - return ''; - } + case 'github': + return ''; + } } //# REGULAR GIT type GitPattern = UrlWithHash & { - type: 'git'; + type: 'git'; }; const GIT_PATTERN_MATCHERS = [ - /^git:/, - /^git\+.+:/, - /^ssh:/, - /^https?:.+\.git$/, - /^https?:.+\.git#.+/, + /^git:/, + /^git\+.+:/, + /^ssh:/, + /^https?:.+\.git$/, + /^https?:.+\.git#.+/, ]; function parseGit(pattern: string, consumer: Consumer): GitPattern { - return { - type: 'git', - ...explodeHashUrl(pattern, consumer), - }; + return { + type: 'git', + ...explodeHashUrl(pattern, consumer), + }; } //# TARBALL type HTTPTarballPattern = UrlWithHash & { - type: 'http-tarball'; + type: 'http-tarball'; }; function parseHttpTarball( - pattern: string, - consumer: Consumer, + pattern: string, + consumer: Consumer, ): HTTPTarballPattern { - return { - type: 'http-tarball', - ...explodeHashUrl(pattern, consumer), - }; + return { + type: 'http-tarball', + ...explodeHashUrl(pattern, consumer), + }; } //# SEMVER type SemverPattern = { - type: 'semver'; - range: SemverRangeNode; + type: 'semver'; + range: SemverRangeNode; }; function parseSemver( - pattern: string, - consumer: Consumer, - loose: boolean, + pattern: string, + consumer: Consumer, + loose: boolean, ): SemverPattern { - const ast = tryParseWithOptionalOffsetPosition( - { - loose, - path: consumer.path, - input: pattern, - }, - { - getOffsetPosition: () => consumer.getLocation('inner-value').start, - parse: (opts) => parseSemverRange(opts), - }, - ); - - return { - type: 'semver', - range: ast, - }; + const ast = tryParseWithOptionalOffsetPosition( + { + loose, + path: consumer.path, + input: pattern, + }, + { + getOffsetPosition: () => consumer.getLocation('inner-value').start, + parse: (opts) => parseSemverRange(opts), + }, + ); + + return { + type: 'semver', + range: ast, + }; } //# FILE const FILE_PREFIX_REGEX = /^\.{1,2}\//; type FilePattern = { - type: 'file'; - path: string; + type: 'file'; + path: string; }; function parseFile(pattern: string): FilePattern { - return { - type: 'file', - path: removePrefix('file:', pattern), - }; + return { + type: 'file', + path: removePrefix('file:', pattern), + }; } //# TAG @@ -263,234 +263,234 @@ function parseFile(pattern: string): FilePattern { const TAG_REGEX = /^[a-z]+$/g; type TagPattern = { - type: 'tag'; - tag: string; + type: 'tag'; + tag: string; }; function parseTag(pattern: string): TagPattern { - return { - type: 'tag', - tag: pattern, - }; + return { + type: 'tag', + tag: pattern, + }; } //# LINK const LINK_PREFIX = 'link:'; type LinkPattern = { - type: 'link'; - path: UnknownFilePath; + type: 'link'; + path: UnknownFilePath; }; function parseLink(pattern: string): LinkPattern { - return { - type: 'link', - path: createUnknownFilePath(pattern.slice(LINK_PREFIX.length)), - }; + return { + type: 'link', + path: createUnknownFilePath(pattern.slice(LINK_PREFIX.length)), + }; } //# NPM const NPM_PREFIX = 'npm:'; type NpmPattern = { - type: 'npm'; - name: ManifestName; - range: undefined | SemverRangeNode; + type: 'npm'; + name: ManifestName; + range: undefined | SemverRangeNode; }; function parseNpm( - pattern: string, - consumer: Consumer, - loose: boolean, + pattern: string, + consumer: Consumer, + loose: boolean, ): NpmPattern { - // Prune prefix - let offset = NPM_PREFIX.length; - pattern = pattern.slice(NPM_PREFIX.length); - - if (pattern === '') { - consumer.unexpected(descriptions.MANIFEST.EMPTY_NPM_PATTERN); - return { - type: 'npm', - name: { - org: undefined, - packageName: undefined, - }, - range: undefined, - }; - } - - // Split and verify count - const parts = pattern.split('@'); - let nameRaw = ''; - let rangeRaw: undefined | string; - - // Org signifier - if (parts[0] === '') { - nameRaw += '@'; - parts.shift(); - } - - // Name - We know there'll be at least two due to the empty string conditional - nameRaw = String(parts.shift()); - - // Range - rangeRaw = parts.shift(); - - if (parts.length > 0) { - consumer.unexpected(descriptions.MANIFEST.TOO_MANY_NPM_PARTS); - } - - const name = normalizeName({ - name: nameRaw, - loose, - unexpected({description, at, start, end}) { - consumer.unexpected( - description, - { - at, - loc: start === undefined - ? undefined - : consumer.getLocationRange( - ob1Add(start, offset), - end === undefined ? undefined : ob1Add(end, offset), - 'inner-value', - ), - }, - ); - }, - }); - - // Increase offset passed name - offset += nameRaw.length; - offset++; - - let range: undefined | SemverRangeNode; - if (rangeRaw !== undefined) { - range = tryParseWithOptionalOffsetPosition( - { - loose, - path: consumer.path, - input: rangeRaw, - }, - { - getOffsetPosition: () => { - const pos = consumer.getLocation('inner-value').start; - return { - ...pos, - column: ob1Add(pos.column, offset), - }; - }, - parse: (opts) => parseSemverRange(opts), - }, - ); - } - - return { - type: 'npm', - name, - range, - }; + // Prune prefix + let offset = NPM_PREFIX.length; + pattern = pattern.slice(NPM_PREFIX.length); + + if (pattern === '') { + consumer.unexpected(descriptions.MANIFEST.EMPTY_NPM_PATTERN); + return { + type: 'npm', + name: { + org: undefined, + packageName: undefined, + }, + range: undefined, + }; + } + + // Split and verify count + const parts = pattern.split('@'); + let nameRaw = ''; + let rangeRaw: undefined | string; + + // Org signifier + if (parts[0] === '') { + nameRaw += '@'; + parts.shift(); + } + + // Name - We know there'll be at least two due to the empty string conditional + nameRaw = String(parts.shift()); + + // Range + rangeRaw = parts.shift(); + + if (parts.length > 0) { + consumer.unexpected(descriptions.MANIFEST.TOO_MANY_NPM_PARTS); + } + + const name = normalizeName({ + name: nameRaw, + loose, + unexpected({description, at, start, end}) { + consumer.unexpected( + description, + { + at, + loc: start === undefined + ? undefined + : consumer.getLocationRange( + ob1Add(start, offset), + end === undefined ? undefined : ob1Add(end, offset), + 'inner-value', + ), + }, + ); + }, + }); + + // Increase offset passed name + offset += nameRaw.length; + offset++; + + let range: undefined | SemverRangeNode; + if (rangeRaw !== undefined) { + range = tryParseWithOptionalOffsetPosition( + { + loose, + path: consumer.path, + input: rangeRaw, + }, + { + getOffsetPosition: () => { + const pos = consumer.getLocation('inner-value').start; + return { + ...pos, + column: ob1Add(pos.column, offset), + }; + }, + parse: (opts) => parseSemverRange(opts), + }, + ); + } + + return { + type: 'npm', + name, + range, + }; } //# export function parseGitDependencyPattern( - consumer: Consumer, + consumer: Consumer, ): undefined | GitPattern | HostedGitPattern { - const pattern = consumer.asString(); - - for (const host of HOSTED_GIT_PREFIXES) { - const prefix = `${host}:`; - if (pattern.startsWith(prefix)) { - return parseHostedGit(host, removePrefix(prefix, pattern), consumer); - } - } - - for (const matcher of GIT_PATTERN_MATCHERS) { - if (matcher.test(pattern)) { - return parseGit(pattern, consumer); - } - } - - if (GITHUB_SHORTHAND.test(pattern)) { - return parseHostedGit('github', pattern, consumer); - } - - return undefined; + const pattern = consumer.asString(); + + for (const host of HOSTED_GIT_PREFIXES) { + const prefix = `${host}:`; + if (pattern.startsWith(prefix)) { + return parseHostedGit(host, removePrefix(prefix, pattern), consumer); + } + } + + for (const matcher of GIT_PATTERN_MATCHERS) { + if (matcher.test(pattern)) { + return parseGit(pattern, consumer); + } + } + + if (GITHUB_SHORTHAND.test(pattern)) { + return parseHostedGit('github', pattern, consumer); + } + + return undefined; } export function parseDependencyPattern( - consumer: Consumer, - loose: boolean, + consumer: Consumer, + loose: boolean, ): DependencyPattern { - const pattern = consumer.asString(); - - const gitPattern = parseGitDependencyPattern(consumer); - if (gitPattern !== undefined) { - return gitPattern; - } - - if (pattern.startsWith('http://') || pattern.startsWith('https://')) { - return parseHttpTarball(pattern, consumer); - } - - if (pattern.startsWith(NPM_PREFIX)) { - return parseNpm(pattern, consumer, loose); - } - - if (pattern.startsWith(LINK_PREFIX)) { - return parseLink(pattern); - } - - if ( - FILE_PREFIX_REGEX.test(pattern) || - createUnknownFilePath(pattern).isAbsolute() || - pattern.startsWith('file:') - ) { - return parseFile(pattern); - } - - if (pattern.match(TAG_REGEX)) { - return parseTag(pattern); - } - - return parseSemver(pattern, consumer, loose); + const pattern = consumer.asString(); + + const gitPattern = parseGitDependencyPattern(consumer); + if (gitPattern !== undefined) { + return gitPattern; + } + + if (pattern.startsWith('http://') || pattern.startsWith('https://')) { + return parseHttpTarball(pattern, consumer); + } + + if (pattern.startsWith(NPM_PREFIX)) { + return parseNpm(pattern, consumer, loose); + } + + if (pattern.startsWith(LINK_PREFIX)) { + return parseLink(pattern); + } + + if ( + FILE_PREFIX_REGEX.test(pattern) || + createUnknownFilePath(pattern).isAbsolute() || + pattern.startsWith('file:') + ) { + return parseFile(pattern); + } + + if (pattern.match(TAG_REGEX)) { + return parseTag(pattern); + } + + return parseSemver(pattern, consumer, loose); } export function normalizeDependencies( - root: Consumer, - key: string, - loose: boolean, + root: Consumer, + key: string, + loose: boolean, ): ManifestDependencies { - const map: ManifestDependencies = new Map(); - - if (!root.has(key)) { - return map; - } - - const consumer = root.get(key); - - // Some ridiculous code has the dependencies property as an empty array - if (Array.isArray(consumer.asUnknown()) && loose) { - return map; - } - - for (const [rawName, value] of consumer.asMap()) { - const name = normalizeName({ - name: rawName, - loose, - unexpected: ({description, at}) => { - value.unexpected( - description, - { - at, - target: 'key', - }, - ); - }, - }); - - map.set(name, parseDependencyPattern(value, loose)); - } - - return map; + const map: ManifestDependencies = new Map(); + + if (!root.has(key)) { + return map; + } + + const consumer = root.get(key); + + // Some ridiculous code has the dependencies property as an empty array + if (Array.isArray(consumer.asUnknown()) && loose) { + return map; + } + + for (const [rawName, value] of consumer.asMap()) { + const name = normalizeName({ + name: rawName, + loose, + unexpected: ({description, at}) => { + value.unexpected( + description, + { + at, + target: 'key', + }, + ); + }, + }); + + map.set(name, parseDependencyPattern(value, loose)); + } + + return map; } diff --git a/packages/@romejs/codec-js-manifest/index.ts b/packages/@romejs/codec-js-manifest/index.ts index e651ff5fcb5..9b1b1230192 100644 --- a/packages/@romejs/codec-js-manifest/index.ts +++ b/packages/@romejs/codec-js-manifest/index.ts @@ -10,23 +10,23 @@ import {SemverVersionNode, parseSemverVersion} from '@romejs/codec-semver'; import {SPDXExpressionNode, parseSPDXLicense} from '@romejs/codec-spdx-license'; import {normalizeDependencies, parseGitDependencyPattern} from './dependencies'; import { - MBoolean, - MString, - Manifest, - ManifestExportConditions, - ManifestExports, - ManifestMap, - ManifestName, - ManifestPerson, - ManifestRepository, + MBoolean, + MString, + Manifest, + ManifestExportConditions, + ManifestExports, + ManifestMap, + ManifestName, + ManifestPerson, + ManifestRepository, } from './types'; import {tryParseWithOptionalOffsetPosition} from '@romejs/parser-core'; import {normalizeName} from './name'; import {Diagnostics, descriptions} from '@romejs/diagnostics'; import { - AbsoluteFilePath, - RelativeFilePathMap, - createRelativeFilePath, + AbsoluteFilePath, + RelativeFilePathMap, + createRelativeFilePath, } from '@romejs/path'; import {toCamelCase} from '@romejs/string-utils'; import {PathPatterns, parsePathPattern} from '@romejs/path-match'; @@ -38,657 +38,657 @@ export * from './convert'; export {manifestNameToString} from './name'; const TYPO_KEYS: Map = new Map([ - ['autohr', 'author'], - ['autor', 'author'], - ['contributers', 'contributors'], - ['depends', 'dependencies'], - ['hampage', 'homepage'], - ['hompage', 'homepage'], - ['prefereGlobal', 'preferGlobal'], - ['publicationConfig', 'publishConfig'], - ['repo', 'repository'], - ['repostitory', 'repository'], - ['script', 'scripts'], + ['autohr', 'author'], + ['autor', 'author'], + ['contributers', 'contributors'], + ['depends', 'dependencies'], + ['hampage', 'homepage'], + ['hompage', 'homepage'], + ['prefereGlobal', 'preferGlobal'], + ['publicationConfig', 'publishConfig'], + ['repo', 'repository'], + ['repostitory', 'repository'], + ['script', 'scripts'], ]); function normalizeBoolean(consumer: Consumer, key: string): MBoolean { - if (consumer.has(key)) { - return consumer.get(key).asBoolean(); - } else { - return undefined; - } + if (consumer.has(key)) { + return consumer.get(key).asBoolean(); + } else { + return undefined; + } } function normalizeString(consumer: Consumer, key: string): MString { - if (consumer.has(key)) { - return consumer.get(key).asString(); - } else { - return undefined; - } + if (consumer.has(key)) { + return consumer.get(key).asString(); + } else { + return undefined; + } } function normalizePathPatterns(consumer: Consumer, loose: boolean): PathPatterns { - return normalizeStringArray(consumer, loose).map((str) => - parsePathPattern({ - input: str, - }) - ); + return normalizeStringArray(consumer, loose).map((str) => + parsePathPattern({ + input: str, + }) + ); } function normalizeStringArray(consumer: Consumer, loose: boolean): Array { - if (consumer.exists()) { - // When we are loose and expect an array but got a string, consider it to be a single element - if (loose) { - const val = consumer.asUnknown(); - - if (typeof val === 'string') { - return [consumer.asString()]; - } - - // npm for some reason sometimes populates bundleDependencies as false? Despite it being a misspelling? - if (val === false) { - return []; - } - } - - return consumer.asArray().map((item) => item.asString()); - } else { - return []; - } + if (consumer.exists()) { + // When we are loose and expect an array but got a string, consider it to be a single element + if (loose) { + const val = consumer.asUnknown(); + + if (typeof val === 'string') { + return [consumer.asString()]; + } + + // npm for some reason sometimes populates bundleDependencies as false? Despite it being a misspelling? + if (val === false) { + return []; + } + } + + return consumer.asArray().map((item) => item.asString()); + } else { + return []; + } } function normalizeStringMap( - root: Consumer, - key: string, - loose: boolean, + root: Consumer, + key: string, + loose: boolean, ): ManifestMap { - const map: ManifestMap = new Map(); + const map: ManifestMap = new Map(); - if (!root.has(key)) { - return map; - } + if (!root.has(key)) { + return map; + } - const consumer = root.get(key); + const consumer = root.get(key); - // Some code uses arrays for this case... Maybe we can normalize them. A `engines` array becomes an object with '*' properties etc - if (Array.isArray(consumer.asUnknown()) && loose) { - return map; - } + // Some code uses arrays for this case... Maybe we can normalize them. A `engines` array becomes an object with '*' properties etc + if (Array.isArray(consumer.asUnknown()) && loose) { + return map; + } - for (const [name, value] of consumer.asMap()) { - // In loose mode let's be really generous - if (loose && typeof value.asUnknown() !== 'string') { - continue; - } + for (const [name, value] of consumer.asMap()) { + // In loose mode let's be really generous + if (loose && typeof value.asUnknown() !== 'string') { + continue; + } - map.set(name, value.asString()); - } + map.set(name, value.asString()); + } - return map; + return map; } function normalizeBin( - consumer: Consumer, - name: MString, - loose: boolean, + consumer: Consumer, + name: MString, + loose: boolean, ): ManifestMap { - const map: ManifestMap = new Map(); - if (!consumer.has('bin')) { - return map; - } - - // Allow a `bin` string - const obj = consumer.get('bin'); - if (typeof obj.asUnknown() === 'string') { - if (name === undefined) { - obj.unexpected(descriptions.MANIFEST.STRING_BIN_WITHOUT_NAME); - } else { - map.set(name, obj.asString()); - return map; - } - } - - // Otherwise expect it to be an object - return normalizeStringMap(consumer, 'bin', loose); + const map: ManifestMap = new Map(); + if (!consumer.has('bin')) { + return map; + } + + // Allow a `bin` string + const obj = consumer.get('bin'); + if (typeof obj.asUnknown() === 'string') { + if (name === undefined) { + obj.unexpected(descriptions.MANIFEST.STRING_BIN_WITHOUT_NAME); + } else { + map.set(name, obj.asString()); + return map; + } + } + + // Otherwise expect it to be an object + return normalizeStringMap(consumer, 'bin', loose); } function extractLicenseFromObjectConsumer( - consumer: Consumer, + consumer: Consumer, ): [string, Consumer] { - const prop = consumer.get('type'); - const value = prop.asString(); - return [value, prop]; + const prop = consumer.get('type'); + const value = prop.asString(); + return [value, prop]; } // These are all licenses I found that are wrong, we should eventually remove this as we update those deps const INVALID_IGNORE_LICENSES = [ - 'UNLICENSED', - 'none', - 'Facebook Platform License', - 'BSD', - 'MIT/X11', - 'Public Domain', - 'MIT License', - 'BSD-like', + 'UNLICENSED', + 'none', + 'Facebook Platform License', + 'BSD', + 'MIT/X11', + 'Public Domain', + 'MIT License', + 'BSD-like', ]; function normalizeLicense( - consumer: Consumer, - loose: boolean, + consumer: Consumer, + loose: boolean, ): undefined | SPDXExpressionNode { - if (!consumer.has('license')) { - return undefined; - } - - let licenseProp = consumer.get('license'); - - let licenseId; - - // Support some legacy ways of specifying licenses: https://docs.npmjs.com/files/package.json#license - const raw = licenseProp.asUnknown(); - if (loose && Array.isArray(raw)) { - const licenseIds = licenseProp.asArray().map((consumer) => - extractLicenseFromObjectConsumer(consumer)[0] - ); - licenseId = `(${licenseIds.join(' OR ')})`; - } else if (loose && typeof raw === 'object') { - [licenseId, licenseProp] = extractLicenseFromObjectConsumer(licenseProp); - } else { - licenseId = licenseProp.asString(); - } - - // Allow referring to a custom license - if (licenseId.startsWith('SEE LICENSE IN ')) { - return undefined; - } - - // Not valid licenses... - if (INVALID_IGNORE_LICENSES.includes(licenseId)) { - return undefined; - } - - // Parse as a SPDX expression - return tryParseWithOptionalOffsetPosition( - { - loose, - path: consumer.path, - input: licenseId, - }, - { - getOffsetPosition: () => licenseProp.getLocation('inner-value').start, - parse: (opts) => parseSPDXLicense(opts), - }, - ); + if (!consumer.has('license')) { + return undefined; + } + + let licenseProp = consumer.get('license'); + + let licenseId; + + // Support some legacy ways of specifying licenses: https://docs.npmjs.com/files/package.json#license + const raw = licenseProp.asUnknown(); + if (loose && Array.isArray(raw)) { + const licenseIds = licenseProp.asArray().map((consumer) => + extractLicenseFromObjectConsumer(consumer)[0] + ); + licenseId = `(${licenseIds.join(' OR ')})`; + } else if (loose && typeof raw === 'object') { + [licenseId, licenseProp] = extractLicenseFromObjectConsumer(licenseProp); + } else { + licenseId = licenseProp.asString(); + } + + // Allow referring to a custom license + if (licenseId.startsWith('SEE LICENSE IN ')) { + return undefined; + } + + // Not valid licenses... + if (INVALID_IGNORE_LICENSES.includes(licenseId)) { + return undefined; + } + + // Parse as a SPDX expression + return tryParseWithOptionalOffsetPosition( + { + loose, + path: consumer.path, + input: licenseId, + }, + { + getOffsetPosition: () => licenseProp.getLocation('inner-value').start, + parse: (opts) => parseSPDXLicense(opts), + }, + ); } function normalizeVersion( - consumer: Consumer, - loose: boolean, + consumer: Consumer, + loose: boolean, ): undefined | SemverVersionNode { - if (!consumer.has('version')) { - return undefined; - } - - const prop = consumer.get('version'); - const rawVersion = prop.asString(); - - // Used in some package.json templates - if (rawVersion === 'VERSION_STRING') { - return undefined; - } - - const ast = tryParseWithOptionalOffsetPosition( - { - path: consumer.path, - input: rawVersion, - // Some node_modules have bogus versions, like being prefixed with a v like: - // https://github.com/itinance/react-native-fs/commit/6232d4e392d5b52cca0792fdfe5903b7fb6b1c5c#diff-b9cfc7f2cdf78a7f4b91a753d10865a2R3 - loose, - }, - { - getOffsetPosition: () => prop.getLocation('inner-value').start, - parse: (opts) => parseSemverVersion(opts), - }, - ); - return ast; + if (!consumer.has('version')) { + return undefined; + } + + const prop = consumer.get('version'); + const rawVersion = prop.asString(); + + // Used in some package.json templates + if (rawVersion === 'VERSION_STRING') { + return undefined; + } + + const ast = tryParseWithOptionalOffsetPosition( + { + path: consumer.path, + input: rawVersion, + // Some node_modules have bogus versions, like being prefixed with a v like: + // https://github.com/itinance/react-native-fs/commit/6232d4e392d5b52cca0792fdfe5903b7fb6b1c5c#diff-b9cfc7f2cdf78a7f4b91a753d10865a2R3 + loose, + }, + { + getOffsetPosition: () => prop.getLocation('inner-value').start, + parse: (opts) => parseSemverVersion(opts), + }, + ); + return ast; } function normalizePerson(consumer: Consumer, loose: boolean): ManifestPerson { - if (typeof consumer.asUnknown() === 'string') { - // Parse the string. Format: name (url) - const str = consumer.asString(); - - const nameMatch = str.match(/^([^(<]+)/); - let name: string | undefined; - if (nameMatch) { - name = nameMatch[0].trim(); - } - - const person: ManifestPerson = { - name, - url: undefined, - email: undefined, - twitter: undefined, - github: undefined, - }; - - const emailMatch = str.match(/<([^>]+)>/); - if (emailMatch) { - person.email = emailMatch[1]; - } - - const urlMatch = str.match(/\(([^)]+)\)/); - if (urlMatch) { - person.url = urlMatch[1]; - } - - return person; - } else { - // Validate as an object - let url: string | undefined = consumer.get('url').asStringOrVoid(); - - // Some packages use "web" or "website" instead of "url" - if (loose) { - if (url === undefined) { - url = consumer.get('web').asStringOrVoid(); - } - - if (url === undefined) { - url = consumer.get('website').asStringOrVoid(); - } - } - - let github = consumer.get('github').asStringOrVoid(); - - if (loose && github === undefined) { - // Some rando packages use this - github = - consumer.get('githubUsername').asStringOrVoid() || - consumer.get('github-username').asStringOrVoid(); - } - - const person: ManifestPerson = { - name: consumer.get('name').asString(loose ? '' : undefined), - email: consumer.get('email').asStringOrVoid(), - twitter: consumer.get('twitter').asStringOrVoid(), - github, - url, - }; - if (!loose) { - consumer.enforceUsedProperties(); - } - return person; - } + if (typeof consumer.asUnknown() === 'string') { + // Parse the string. Format: name (url) + const str = consumer.asString(); + + const nameMatch = str.match(/^([^(<]+)/); + let name: string | undefined; + if (nameMatch) { + name = nameMatch[0].trim(); + } + + const person: ManifestPerson = { + name, + url: undefined, + email: undefined, + twitter: undefined, + github: undefined, + }; + + const emailMatch = str.match(/<([^>]+)>/); + if (emailMatch) { + person.email = emailMatch[1]; + } + + const urlMatch = str.match(/\(([^)]+)\)/); + if (urlMatch) { + person.url = urlMatch[1]; + } + + return person; + } else { + // Validate as an object + let url: string | undefined = consumer.get('url').asStringOrVoid(); + + // Some packages use "web" or "website" instead of "url" + if (loose) { + if (url === undefined) { + url = consumer.get('web').asStringOrVoid(); + } + + if (url === undefined) { + url = consumer.get('website').asStringOrVoid(); + } + } + + let github = consumer.get('github').asStringOrVoid(); + + if (loose && github === undefined) { + // Some rando packages use this + github = + consumer.get('githubUsername').asStringOrVoid() || + consumer.get('github-username').asStringOrVoid(); + } + + const person: ManifestPerson = { + name: consumer.get('name').asString(loose ? '' : undefined), + email: consumer.get('email').asStringOrVoid(), + twitter: consumer.get('twitter').asStringOrVoid(), + github, + url, + }; + if (!loose) { + consumer.enforceUsedProperties(); + } + return person; + } } function normalizePeople( - consumer: Consumer, - loose: boolean, + consumer: Consumer, + loose: boolean, ): undefined | Array { - if (!consumer.exists()) { - return; - } + if (!consumer.exists()) { + return; + } - // Some packages have a single maintainer object instead of an array - if (loose && consumer.isObject()) { - return [normalizePerson(consumer, loose)]; - } + // Some packages have a single maintainer object instead of an array + if (loose && consumer.isObject()) { + return [normalizePerson(consumer, loose)]; + } - // If it's not an array then just leave it. Some people put a URL here. - if (loose && !Array.isArray(consumer.asUnknown())) { - return; - } + // If it's not an array then just leave it. Some people put a URL here. + if (loose && !Array.isArray(consumer.asUnknown())) { + return; + } - const people: Array = []; + const people: Array = []; - for (const item of consumer.asArray()) { - people.push(normalizePerson(item, loose)); - } + for (const item of consumer.asArray()) { + people.push(normalizePerson(item, loose)); + } - return people; + return people; } function normalizeRepo( - consumer: Consumer, - loose: boolean, + consumer: Consumer, + loose: boolean, ): undefined | ManifestRepository { - if (!consumer.exists()) { - return; - } - - if (typeof consumer.asUnknown() === 'string') { - let url = consumer.asString(); - - // If this is a hosted git shorthand then explode it - const parsed = parseGitDependencyPattern(consumer); - if (parsed?.type === 'hosted-git') { - url = parsed.url; - } - - return { - type: 'git', - url, - directory: undefined, - }; - } else { - let url: string; - let type: string; - - if (loose) { - // A lot of packages omit the "type" - type = consumer.get('type').asString('git'); - - // thanks i hate it - consumer.markUsedProperty('web'); - consumer.markUsedProperty('git'); - consumer.markUsedProperty('dist'); - - // Some gross packages use "repository" instead of "url" - let looseUrl = consumer.get('url').asStringOrVoid(); - - if (looseUrl === undefined) { - looseUrl = consumer.get('repository').asStringOrVoid(); - } - - if (looseUrl === undefined) { - consumer.unexpected(descriptions.MANIFEST.MISSING_REPO_URL); - url = ''; - } else { - url = looseUrl; - } - } else { - url = consumer.get('url').asString(); - type = consumer.get('type').asString(); - } - - const repo: Manifest['repository'] = { - type, - url, - directory: consumer.get('directory').asStringOrVoid(), - }; - if (!loose) { - consumer.enforceUsedProperties(); - } - return repo; - } + if (!consumer.exists()) { + return; + } + + if (typeof consumer.asUnknown() === 'string') { + let url = consumer.asString(); + + // If this is a hosted git shorthand then explode it + const parsed = parseGitDependencyPattern(consumer); + if (parsed?.type === 'hosted-git') { + url = parsed.url; + } + + return { + type: 'git', + url, + directory: undefined, + }; + } else { + let url: string; + let type: string; + + if (loose) { + // A lot of packages omit the "type" + type = consumer.get('type').asString('git'); + + // thanks i hate it + consumer.markUsedProperty('web'); + consumer.markUsedProperty('git'); + consumer.markUsedProperty('dist'); + + // Some gross packages use "repository" instead of "url" + let looseUrl = consumer.get('url').asStringOrVoid(); + + if (looseUrl === undefined) { + looseUrl = consumer.get('repository').asStringOrVoid(); + } + + if (looseUrl === undefined) { + consumer.unexpected(descriptions.MANIFEST.MISSING_REPO_URL); + url = ''; + } else { + url = looseUrl; + } + } else { + url = consumer.get('url').asString(); + type = consumer.get('type').asString(); + } + + const repo: Manifest['repository'] = { + type, + url, + directory: consumer.get('directory').asStringOrVoid(), + }; + if (!loose) { + consumer.enforceUsedProperties(); + } + return repo; + } } function normalizeExports(consumer: Consumer): boolean | ManifestExports { - const unknown = consumer.asUnknown(); - - // "exports": false - if (typeof unknown === 'boolean') { - return consumer.asBoolean(); - } - - if (!consumer.exists()) { - return true; - } - - const exports: ManifestExports = new RelativeFilePathMap(); - - // "exports": "./index.js" - if (typeof unknown === 'string') { - exports.set( - createRelativeFilePath('.'), - new Map([ - [ - 'default', - { - consumer, - relative: consumer.asExplicitRelativeFilePath(), - }, - ], - ]), - ); - return exports; - } - - const dotConditions: ManifestExportConditions = new Map(); - - for (const [relative, value] of consumer.asMap()) { - // If it's not a relative path then it's a platform for the root - if (relative[0] !== '.') { - if (exports.size > 0) { - value.unexpected(descriptions.MANIFEST.MIXED_EXPORTS_PATHS); - } - - dotConditions.set( - relative, - { - consumer: value, - relative: value.asExplicitRelativeFilePath(), - }, - ); - continue; - } - - if (dotConditions.size > 0) { - value.unexpected(descriptions.MANIFEST.MIXED_EXPORTS_PATHS); - } - - const conditions = normalizeExportsConditions(value); - exports.set(value.getKey().asExplicitRelativeFilePath(), conditions); - } - - if (dotConditions.size > 0) { - exports.set(createRelativeFilePath('.'), dotConditions); - } - - return exports; + const unknown = consumer.asUnknown(); + + // "exports": false + if (typeof unknown === 'boolean') { + return consumer.asBoolean(); + } + + if (!consumer.exists()) { + return true; + } + + const exports: ManifestExports = new RelativeFilePathMap(); + + // "exports": "./index.js" + if (typeof unknown === 'string') { + exports.set( + createRelativeFilePath('.'), + new Map([ + [ + 'default', + { + consumer, + relative: consumer.asExplicitRelativeFilePath(), + }, + ], + ]), + ); + return exports; + } + + const dotConditions: ManifestExportConditions = new Map(); + + for (const [relative, value] of consumer.asMap()) { + // If it's not a relative path then it's a platform for the root + if (relative[0] !== '.') { + if (exports.size > 0) { + value.unexpected(descriptions.MANIFEST.MIXED_EXPORTS_PATHS); + } + + dotConditions.set( + relative, + { + consumer: value, + relative: value.asExplicitRelativeFilePath(), + }, + ); + continue; + } + + if (dotConditions.size > 0) { + value.unexpected(descriptions.MANIFEST.MIXED_EXPORTS_PATHS); + } + + const conditions = normalizeExportsConditions(value); + exports.set(value.getKey().asExplicitRelativeFilePath(), conditions); + } + + if (dotConditions.size > 0) { + exports.set(createRelativeFilePath('.'), dotConditions); + } + + return exports; } function normalizeExportsConditions(value: Consumer): ManifestExportConditions { - const conditions: ManifestExportConditions = new Map(); - const unknown = value.asUnknown(); - - if (typeof unknown === 'string') { - conditions.set( - 'default', - { - consumer: value, - relative: value.asExplicitRelativeFilePath(), - }, - ); - } else if (Array.isArray(unknown)) { - // Find the first item that passes validation - for (const elem of value.asArray()) { - const {consumer, diagnostics} = elem.capture(); - const result = normalizeExportsConditions(consumer); - if (diagnostics.length === 0) { - return result; - } - } - } else { - for (const [type, relativeAlias] of value.asMap()) { - conditions.set( - type, - { - consumer: relativeAlias, - relative: relativeAlias.asExplicitRelativeFilePath(), - }, - ); - } - } - - return conditions; + const conditions: ManifestExportConditions = new Map(); + const unknown = value.asUnknown(); + + if (typeof unknown === 'string') { + conditions.set( + 'default', + { + consumer: value, + relative: value.asExplicitRelativeFilePath(), + }, + ); + } else if (Array.isArray(unknown)) { + // Find the first item that passes validation + for (const elem of value.asArray()) { + const {consumer, diagnostics} = elem.capture(); + const result = normalizeExportsConditions(consumer); + if (diagnostics.length === 0) { + return result; + } + } + } else { + for (const [type, relativeAlias] of value.asMap()) { + conditions.set( + type, + { + consumer: relativeAlias, + relative: relativeAlias.asExplicitRelativeFilePath(), + }, + ); + } + } + + return conditions; } function normalizeBugs( - consumer: Consumer, - loose: boolean, + consumer: Consumer, + loose: boolean, ): undefined | Manifest['bugs'] { - if (!consumer.exists()) { - return; - } - - if (typeof consumer.asUnknown() === 'string') { - return { - email: undefined, - url: consumer.asString(), - }; - } else { - let email = consumer.get('email').asStringOrVoid(); - - // Some use a `mail` property - if (loose && email === undefined) { - email = consumer.get('mail').asStringOrVoid(); - } - - // TODO remove this - consumer.markUsedProperty('type'); - - const bugs: Manifest['bugs'] = { - email, - url: consumer.get('url').asStringOrVoid(), - }; - if (!loose) { - consumer.enforceUsedProperties(); - } - return bugs; - } + if (!consumer.exists()) { + return; + } + + if (typeof consumer.asUnknown() === 'string') { + return { + email: undefined, + url: consumer.asString(), + }; + } else { + let email = consumer.get('email').asStringOrVoid(); + + // Some use a `mail` property + if (loose && email === undefined) { + email = consumer.get('mail').asStringOrVoid(); + } + + // TODO remove this + consumer.markUsedProperty('type'); + + const bugs: Manifest['bugs'] = { + email, + url: consumer.get('url').asStringOrVoid(), + }; + if (!loose) { + consumer.enforceUsedProperties(); + } + return bugs; + } } function normalizeRootName(consumer: Consumer, loose: boolean): ManifestName { - if (!consumer.has('name')) { - return { - packageName: undefined, - org: undefined, - }; - } - - const prop = consumer.get('name'); - - return normalizeName({ - name: prop.asString(), - loose, - unexpected: ({description, at, start, end}) => { - prop.unexpected( - description, - { - at, - loc: start === undefined - ? undefined - : prop.getLocationRange(start, end, 'inner-value'), - }, - ); - }, - }); + if (!consumer.has('name')) { + return { + packageName: undefined, + org: undefined, + }; + } + + const prop = consumer.get('name'); + + return normalizeName({ + name: prop.asString(), + loose, + unexpected: ({description, at, start, end}) => { + prop.unexpected( + description, + { + at, + loc: start === undefined + ? undefined + : prop.getLocationRange(start, end, 'inner-value'), + }, + ); + }, + }); } const DEPENDENCIES_KEYS = ['', 'dev', 'peer', 'optional']; const INCORRECT_DEPENDENCIES_SUFFIXES = [ - 'depdenencies', - 'dependancies', - 'dependecies', + 'depdenencies', + 'dependancies', + 'dependecies', ]; function checkDependencyKeyTypo(key: string, prop: Consumer) { - for (const depPrefixKey of DEPENDENCIES_KEYS) { - // Ignore if the key is a valid dependency key - const depKey = - depPrefixKey === '' ? 'dependencies' : `${depPrefixKey}Dependencies`; - if (key === depKey) { - return; - } - - // Check for casing issues - const lowerKey = key.toLowerCase(); - if (lowerKey === depKey) { - prop.unexpected(descriptions.MANIFEST.INCORRECT_CAMEL_CASING(key, depKey)); - } - - // Check for common suffix misspellings - for (const suffix of INCORRECT_DEPENDENCIES_SUFFIXES) { - if (lowerKey === `${depPrefixKey}${suffix}`) { - prop.unexpected(descriptions.MANIFEST.TYPO(key, depKey)); - } - } - - // Check for kebab casing - if (toCamelCase(depKey) === lowerKey) { - prop.unexpected(descriptions.MANIFEST.INCORRECT_CAMEL_CASING(key, depKey)); - } - } + for (const depPrefixKey of DEPENDENCIES_KEYS) { + // Ignore if the key is a valid dependency key + const depKey = + depPrefixKey === '' ? 'dependencies' : `${depPrefixKey}Dependencies`; + if (key === depKey) { + return; + } + + // Check for casing issues + const lowerKey = key.toLowerCase(); + if (lowerKey === depKey) { + prop.unexpected(descriptions.MANIFEST.INCORRECT_CAMEL_CASING(key, depKey)); + } + + // Check for common suffix misspellings + for (const suffix of INCORRECT_DEPENDENCIES_SUFFIXES) { + if (lowerKey === `${depPrefixKey}${suffix}`) { + prop.unexpected(descriptions.MANIFEST.TYPO(key, depKey)); + } + } + + // Check for kebab casing + if (toCamelCase(depKey) === lowerKey) { + prop.unexpected(descriptions.MANIFEST.INCORRECT_CAMEL_CASING(key, depKey)); + } + } } export async function normalizeManifest( - path: AbsoluteFilePath, - rawConsumer: Consumer, + path: AbsoluteFilePath, + rawConsumer: Consumer, ): Promise<{ - manifest: Manifest; - diagnostics: Diagnostics; + manifest: Manifest; + diagnostics: Diagnostics; }> { - const loose = path.getSegments().includes('node_modules'); - - const {consumer, diagnostics} = rawConsumer.capture(); - - // FIXME: There's this ridiculous node module that includes it's tests... which deliberately includes an invalid package.json - if (path.join().includes('resolve/test/resolver/invalid_main')) { - consumer.setValue({}); - } - - // - if (!loose) { - for (const [key, prop] of consumer.asMap()) { - // Check for typos for dependencies - checkDependencyKeyTypo(key, prop); - - // Check for other typos - const correctKey = TYPO_KEYS.get(key); - if (correctKey !== undefined) { - prop.unexpected(descriptions.MANIFEST.TYPO(key, correctKey)); - } - } - } - - const name = normalizeRootName(consumer, loose); - - const manifest: Manifest = { - name, - version: normalizeVersion(consumer, loose), - private: normalizeBoolean(consumer, 'private') === true, - description: normalizeString(consumer, 'description'), - license: normalizeLicense(consumer, loose), - type: consumer.get('type').asStringSetOrVoid(['module', 'commonjs']), - bin: normalizeBin(consumer, name.packageName, loose), - scripts: normalizeStringMap(consumer, 'scripts', loose), - homepage: normalizeString(consumer, 'homepage'), - repository: normalizeRepo(consumer.get('repository'), loose), - bugs: normalizeBugs(consumer.get('bugs'), loose), - engines: normalizeStringMap(consumer, 'engines', loose), - files: normalizePathPatterns(consumer.get('files'), loose), - keywords: normalizeStringArray(consumer.get('keywords'), loose), - cpu: normalizeStringArray(consumer.get('cpu'), loose), - os: normalizeStringArray(consumer.get('os'), loose), - main: normalizeString(consumer, 'main'), - exports: normalizeExports(consumer.get('exports')), - // Dependency fields - dependencies: normalizeDependencies(consumer, 'dependencies', loose), - devDependencies: normalizeDependencies(consumer, 'devDependencies', loose), - optionalDependencies: normalizeDependencies( - consumer, - 'optionalDependencies', - loose, - ), - peerDependencies: normalizeDependencies(consumer, 'peerDependencies', loose), - bundledDependencies: [ - ...normalizeStringArray(consumer.get('bundledDependencies'), loose), - // Common misspelling. We error on the existence of this for strict manifests already. - ...normalizeStringArray(consumer.get('bundleDependencies'), loose), - ], - // People fields - author: consumer.has('author') - ? normalizePerson(consumer.get('author'), loose) - : undefined, - contributors: normalizePeople(consumer.get('contributors'), loose), - maintainers: normalizePeople(consumer.get('maintainers'), loose), - raw: consumer.asJSONObject(), - }; - - return { - manifest, - diagnostics, - }; + const loose = path.getSegments().includes('node_modules'); + + const {consumer, diagnostics} = rawConsumer.capture(); + + // FIXME: There's this ridiculous node module that includes it's tests... which deliberately includes an invalid package.json + if (path.join().includes('resolve/test/resolver/invalid_main')) { + consumer.setValue({}); + } + + // + if (!loose) { + for (const [key, prop] of consumer.asMap()) { + // Check for typos for dependencies + checkDependencyKeyTypo(key, prop); + + // Check for other typos + const correctKey = TYPO_KEYS.get(key); + if (correctKey !== undefined) { + prop.unexpected(descriptions.MANIFEST.TYPO(key, correctKey)); + } + } + } + + const name = normalizeRootName(consumer, loose); + + const manifest: Manifest = { + name, + version: normalizeVersion(consumer, loose), + private: normalizeBoolean(consumer, 'private') === true, + description: normalizeString(consumer, 'description'), + license: normalizeLicense(consumer, loose), + type: consumer.get('type').asStringSetOrVoid(['module', 'commonjs']), + bin: normalizeBin(consumer, name.packageName, loose), + scripts: normalizeStringMap(consumer, 'scripts', loose), + homepage: normalizeString(consumer, 'homepage'), + repository: normalizeRepo(consumer.get('repository'), loose), + bugs: normalizeBugs(consumer.get('bugs'), loose), + engines: normalizeStringMap(consumer, 'engines', loose), + files: normalizePathPatterns(consumer.get('files'), loose), + keywords: normalizeStringArray(consumer.get('keywords'), loose), + cpu: normalizeStringArray(consumer.get('cpu'), loose), + os: normalizeStringArray(consumer.get('os'), loose), + main: normalizeString(consumer, 'main'), + exports: normalizeExports(consumer.get('exports')), + // Dependency fields + dependencies: normalizeDependencies(consumer, 'dependencies', loose), + devDependencies: normalizeDependencies(consumer, 'devDependencies', loose), + optionalDependencies: normalizeDependencies( + consumer, + 'optionalDependencies', + loose, + ), + peerDependencies: normalizeDependencies(consumer, 'peerDependencies', loose), + bundledDependencies: [ + ...normalizeStringArray(consumer.get('bundledDependencies'), loose), + // Common misspelling. We error on the existence of this for strict manifests already. + ...normalizeStringArray(consumer.get('bundleDependencies'), loose), + ], + // People fields + author: consumer.has('author') + ? normalizePerson(consumer.get('author'), loose) + : undefined, + contributors: normalizePeople(consumer.get('contributors'), loose), + maintainers: normalizePeople(consumer.get('maintainers'), loose), + raw: consumer.asJSONObject(), + }; + + return { + manifest, + diagnostics, + }; } diff --git a/packages/@romejs/codec-js-manifest/name.ts b/packages/@romejs/codec-js-manifest/name.ts index 07a64ff57d3..fa5d60c5a66 100644 --- a/packages/@romejs/codec-js-manifest/name.ts +++ b/packages/@romejs/codec-js-manifest/name.ts @@ -7,49 +7,49 @@ import {Number0, ob1Add, ob1Coerce0, ob1Inc, ob1Number0} from '@romejs/ob1'; import { - DiagnosticDescriptionOptionalCategory, - descriptions, + DiagnosticDescriptionOptionalCategory, + descriptions, } from '@romejs/diagnostics'; import {ManifestName} from './types'; type NormalizeNameUnexpected = ( - opts: { - description: DiagnosticDescriptionOptionalCategory; - start?: Number0; - end?: Number0; - at?: 'prefix'; - }, + opts: { + description: DiagnosticDescriptionOptionalCategory; + start?: Number0; + end?: Number0; + at?: 'prefix'; + }, ) => void; type ValidateNamePartOptions = { - name: string; - isOrg: boolean; - isOrgPart: boolean; - offset: Number0; + name: string; + isOrg: boolean; + isOrgPart: boolean; + offset: Number0; }; type NormalizeNameOptions = { - name: string; - loose: boolean; - unexpected: NormalizeNameUnexpected; + name: string; + loose: boolean; + unexpected: NormalizeNameUnexpected; }; function validateNamePart( - {loose, unexpected}: NormalizeNameOptions, - {name, isOrg, isOrgPart, offset}: ValidateNamePartOptions, + {loose, unexpected}: NormalizeNameOptions, + {name, isOrg, isOrgPart, offset}: ValidateNamePartOptions, ) { - let normalizedName: string = ''; - - for (let i = 0; i < name.length; i++) { - const char = name[i]; - - if (isOrg && char === '@' && i === 0) { - unexpected({ - description: descriptions.MANIFEST.REDUNDANT_ORG_NAME_START, - start: ob1Add(offset, i), - }); - } else if (!isOrgPart && char === '/') { - /*unexpected({ + let normalizedName: string = ''; + + for (let i = 0; i < name.length; i++) { + const char = name[i]; + + if (isOrg && char === '@' && i === 0) { + unexpected({ + description: descriptions.MANIFEST.REDUNDANT_ORG_NAME_START, + start: ob1Add(offset, i), + }); + } else if (!isOrgPart && char === '/') { + /*unexpected({ at: 'prefix', message: `cannot contain any slashes`, start: add(offset, i), @@ -62,125 +62,125 @@ function validateNamePart( }, ], });*/ - normalizedName = `@${normalizedName}/`; - } else if (!loose && char.match(/[A-Z]/)) { - /*unexpected({ + normalizedName = `@${normalizedName}/`; + } else if (!loose && char.match(/[A-Z]/)) { + /*unexpected({ at: 'prefix', message: `cannot contain uppercase letters`, start: add(offset, i), });*/ - normalizedName += char.toLowerCase(); - } else if (char.match(/[A-Za-z0-9\-_.]/)) { - normalizedName += char; - } else { - unexpected({ - description: descriptions.MANIFEST.INVALID_NAME_CHAR(char), - start: ob1Add(offset, i), - }); - } - } - - return normalizedName; + normalizedName += char.toLowerCase(); + } else if (char.match(/[A-Za-z0-9\-_.]/)) { + normalizedName += char; + } else { + unexpected({ + description: descriptions.MANIFEST.INVALID_NAME_CHAR(char), + start: ob1Add(offset, i), + }); + } + } + + return normalizedName; } export function manifestNameToString(name: ManifestName): undefined | string { - const {packageName, org} = name; + const {packageName, org} = name; - if (org === undefined) { - return packageName; - } + if (org === undefined) { + return packageName; + } - return `@${org}/${packageName}`; + return `@${org}/${packageName}`; } export function normalizeName(opts: NormalizeNameOptions): ManifestName { - const {unexpected} = opts; - let {name} = opts; - - let org: undefined | string; - let packageName: undefined | string; - - if (name.length > 214) { - unexpected({ - at: 'prefix', - description: descriptions.MANIFEST.NAME_EXCEEDS, - }); - name = name.slice(0, 214); - } - - if (name[0] === '.' || name[0] === '_') { - unexpected({ - at: 'prefix', - description: descriptions.MANIFEST.INVALID_NAME_START, - start: ob1Number0, - }); - name = name.slice(1); - } - - if (name[0] === '@') { - // Validate org and package name separately - const [rawOrg, rawPackageName, ...other] = name.slice(1).split('/'); - - // Leading @ - let offset: Number0 = ob1Coerce0(1); - - // Org - const sanitizedOrg = validateNamePart( - opts, - { - isOrg: true, - isOrgPart: true, - name: rawOrg, - offset, - }, - ); - offset = ob1Add(offset, rawOrg.length); - org = sanitizedOrg; - - if (rawPackageName === undefined) { - unexpected({ - at: 'prefix', - description: descriptions.MANIFEST.ORG_WITH_NO_PACKAGE_NAME, - start: offset, - }); - } else { - // Forward slashSeparator - offset = ob1Inc(offset); - - // Package name - const sanitizedPackageName = validateNamePart( - opts, - { - isOrg: false, - isOrgPart: true, - name: rawPackageName, - offset, - }, - ); - offset = ob1Add(offset, rawPackageName.length); - - // Complain on excess separators - if (other.length > 0) { - unexpected({ - at: 'prefix', - description: descriptions.MANIFEST.ORG_TOO_MANY_PARTS, - start: offset, - }); - } - - packageName = sanitizedPackageName; - } - } else { - packageName = validateNamePart( - opts, - { - name, - offset: ob1Number0, - isOrg: false, - isOrgPart: false, - }, - ); - } - - return {org, packageName}; + const {unexpected} = opts; + let {name} = opts; + + let org: undefined | string; + let packageName: undefined | string; + + if (name.length > 214) { + unexpected({ + at: 'prefix', + description: descriptions.MANIFEST.NAME_EXCEEDS, + }); + name = name.slice(0, 214); + } + + if (name[0] === '.' || name[0] === '_') { + unexpected({ + at: 'prefix', + description: descriptions.MANIFEST.INVALID_NAME_START, + start: ob1Number0, + }); + name = name.slice(1); + } + + if (name[0] === '@') { + // Validate org and package name separately + const [rawOrg, rawPackageName, ...other] = name.slice(1).split('/'); + + // Leading @ + let offset: Number0 = ob1Coerce0(1); + + // Org + const sanitizedOrg = validateNamePart( + opts, + { + isOrg: true, + isOrgPart: true, + name: rawOrg, + offset, + }, + ); + offset = ob1Add(offset, rawOrg.length); + org = sanitizedOrg; + + if (rawPackageName === undefined) { + unexpected({ + at: 'prefix', + description: descriptions.MANIFEST.ORG_WITH_NO_PACKAGE_NAME, + start: offset, + }); + } else { + // Forward slashSeparator + offset = ob1Inc(offset); + + // Package name + const sanitizedPackageName = validateNamePart( + opts, + { + isOrg: false, + isOrgPart: true, + name: rawPackageName, + offset, + }, + ); + offset = ob1Add(offset, rawPackageName.length); + + // Complain on excess separators + if (other.length > 0) { + unexpected({ + at: 'prefix', + description: descriptions.MANIFEST.ORG_TOO_MANY_PARTS, + start: offset, + }); + } + + packageName = sanitizedPackageName; + } + } else { + packageName = validateNamePart( + opts, + { + name, + offset: ob1Number0, + isOrg: false, + isOrgPart: false, + }, + ); + } + + return {org, packageName}; } diff --git a/packages/@romejs/codec-js-manifest/types.ts b/packages/@romejs/codec-js-manifest/types.ts index c44fddad5b2..c8ec5267516 100644 --- a/packages/@romejs/codec-js-manifest/types.ts +++ b/packages/@romejs/codec-js-manifest/types.ts @@ -10,9 +10,9 @@ import {SPDXExpressionNode} from '@romejs/codec-spdx-license'; import {SemverVersionNode} from '@romejs/codec-semver'; import {Consumer} from '@romejs/consume'; import { - AbsoluteFilePath, - RelativeFilePath, - RelativeFilePathMap, + AbsoluteFilePath, + RelativeFilePath, + RelativeFilePathMap, } from '@romejs/path'; import {JSONObject, JSONPropertyValue} from '@romejs/codec-json'; import {Dict} from '@romejs/typescript-helpers'; @@ -31,107 +31,107 @@ export type MBoolean = undefined | boolean; export type ManifestMap = Map; export type ManifestPerson = { - name: MString; - email: MString; - twitter: MString; - github: MString; - url: MString; + name: MString; + email: MString; + twitter: MString; + github: MString; + url: MString; }; export type ManifestRepository = { - type: string; - url: string; - directory: MString; + type: string; + url: string; + directory: MString; }; export type ManifestBugs = { - url: MString; - email: MString; + url: MString; + email: MString; }; export type ManifestExports = RelativeFilePathMap; export type ManifestExportConditions = Map< - string, - { - consumer: Consumer; - relative: RelativeFilePath; - } + string, + { + consumer: Consumer; + relative: RelativeFilePath; + } >; export type ManifestName = { - org?: string; - packageName?: string; + org?: string; + packageName?: string; }; export type Manifest = { - name: ManifestName; - description: MString; - version: undefined | SemverVersionNode; - license: undefined | SPDXExpressionNode; - private: boolean; - type: undefined | 'module' | 'commonjs'; - homepage: MString; - repository: undefined | ManifestRepository; - bugs: undefined | ManifestBugs; - main: MString; - exports: boolean | ManifestExports; - author: undefined | ManifestPerson; - contributors: undefined | Array; - maintainers: undefined | Array; - files: PathPatterns; - keywords: Array; - cpu: Array; - os: Array; - bin: ManifestMap; - scripts: ManifestMap; - engines: ManifestMap; - dependencies: ManifestDependencies; - devDependencies: ManifestDependencies; - optionalDependencies: ManifestDependencies; - peerDependencies: ManifestDependencies; - bundledDependencies: Array; - raw: JSONObject; + name: ManifestName; + description: MString; + version: undefined | SemverVersionNode; + license: undefined | SPDXExpressionNode; + private: boolean; + type: undefined | 'module' | 'commonjs'; + homepage: MString; + repository: undefined | ManifestRepository; + bugs: undefined | ManifestBugs; + main: MString; + exports: boolean | ManifestExports; + author: undefined | ManifestPerson; + contributors: undefined | Array; + maintainers: undefined | Array; + files: PathPatterns; + keywords: Array; + cpu: Array; + os: Array; + bin: ManifestMap; + scripts: ManifestMap; + engines: ManifestMap; + dependencies: ManifestDependencies; + devDependencies: ManifestDependencies; + optionalDependencies: ManifestDependencies; + peerDependencies: ManifestDependencies; + bundledDependencies: Array; + raw: JSONObject; }; // Serialized version of a Manifest export type JSONManifest = { - name: MString; - description: Manifest['description']; - version: MString; - license: MString; - private: Manifest['private']; - type: Manifest['type']; - homepage: Manifest['homepage']; - repository: Manifest['repository']; - bugs: Manifest['bugs']; - main: Manifest['main']; - exports: undefined | false | JSONManifestExports; - author: Manifest['author']; - contributors: Manifest['contributors']; - maintainers: Manifest['maintainers']; - files: MStringArray; - keywords: MStringArray; - cpu: MStringArray; - os: MStringArray; - bin: MStringObject; - scripts: MStringObject; - engines: MStringObject; - dependencies: MStringObject; - devDependencies: MStringObject; - optionalDependencies: MStringObject; - peerDependencies: MStringObject; - bundledDependencies: MStringArray; - [key: string]: JSONPropertyValue; + name: MString; + description: Manifest['description']; + version: MString; + license: MString; + private: Manifest['private']; + type: Manifest['type']; + homepage: Manifest['homepage']; + repository: Manifest['repository']; + bugs: Manifest['bugs']; + main: Manifest['main']; + exports: undefined | false | JSONManifestExports; + author: Manifest['author']; + contributors: Manifest['contributors']; + maintainers: Manifest['maintainers']; + files: MStringArray; + keywords: MStringArray; + cpu: MStringArray; + os: MStringArray; + bin: MStringObject; + scripts: MStringObject; + engines: MStringObject; + dependencies: MStringObject; + devDependencies: MStringObject; + optionalDependencies: MStringObject; + peerDependencies: MStringObject; + bundledDependencies: MStringArray; + [key: string]: JSONPropertyValue; }; export type JSONManifestExports = Dict | string>; export type ManifestDefinition = { - path: AbsoluteFilePath; - folder: AbsoluteFilePath; - id: number; - consumer: Consumer; - manifest: Manifest; - hash: string; + path: AbsoluteFilePath; + folder: AbsoluteFilePath; + id: number; + consumer: Consumer; + manifest: Manifest; + hash: string; }; diff --git a/packages/@romejs/codec-js-regexp/index.ts b/packages/@romejs/codec-js-regexp/index.ts index a86d875d24d..d7f0b161195 100644 --- a/packages/@romejs/codec-js-regexp/index.ts +++ b/packages/@romejs/codec-js-regexp/index.ts @@ -6,1168 +6,1152 @@ */ import { - BaseTokens, - ComplexToken, - ParserOptions, - ParserUnexpectedOptions, - Position, - TokenValues, - ValueToken, - createParser, - isDigit, - isESIdentifierChar, - isESIdentifierStart, + BaseTokens, + ComplexToken, + ParserOptions, + ParserUnexpectedOptions, + Position, + TokenValues, + ValueToken, + createParser, + isDigit, + isESIdentifierChar, + isESIdentifierStart, } from '@romejs/parser-core'; import { - AnyRegExpBodyItem, - AnyRegExpEscapedCharacter, - AnyRegExpExpression, - RegExpAlternation, - RegExpCharSet, - RegExpCharSetRange, - RegExpGroupCapture, - RegExpGroupNonCapture, - RegExpQuantified, - RegExpSubExpression, + AnyRegExpBodyItem, + AnyRegExpEscapedCharacter, + AnyRegExpExpression, + RegExpAlternation, + RegExpCharSet, + RegExpCharSetRange, + RegExpGroupCapture, + RegExpGroupNonCapture, + RegExpQuantified, + RegExpSubExpression, } from '@romejs/js-ast'; import {Diagnostics, descriptions} from '@romejs/diagnostics'; import {Number0, ob1Add, ob1Coerce0, ob1Get0} from '@romejs/ob1'; type Operator = - | '^' - | '$' - | '.' - | '[' - | ']' - | '(' - | ')' - | '?' - | '{' - | '}' - | '+' - | '*' - | '|'; + | '^' + | '$' + | '.' + | '[' + | ']' + | '(' + | ')' + | '?' + | '{' + | '}' + | '+' + | '*' + | '|'; type Tokens = BaseTokens & { - Operator: ValueToken<'Operator', Operator>; - Character: ComplexToken< - 'Character', - { - value: string; - escaped: boolean; - } - >; - EscapedCharacter: ValueToken< - 'EscapedCharacter', - 'd' | 'D' | 'b' | 'B' | 's' | 'S' | 'w' | 'W' - >; - NumericBackReferenceCharacter: ComplexToken< - 'NumericBackReferenceCharacter', - { - value: number; - escaped: boolean; - } - >; - NamedBackReferenceCharacter: ComplexToken< - 'NamedBackReferenceCharacter', - { - value: string; - escaped: boolean; - } - >; + Operator: ValueToken<'Operator', Operator>; + Character: ComplexToken< + 'Character', + { + value: string; + escaped: boolean; + } + >; + EscapedCharacter: ValueToken< + 'EscapedCharacter', + 'd' | 'D' | 'b' | 'B' | 's' | 'S' | 'w' | 'W' + >; + NumericBackReferenceCharacter: ComplexToken< + 'NumericBackReferenceCharacter', + { + value: number; + escaped: boolean; + } + >; + NamedBackReferenceCharacter: ComplexToken< + 'NamedBackReferenceCharacter', + { + value: string; + escaped: boolean; + } + >; }; type GroupModifiers = - | { - type: 'NON_CAPTURE'; - kind: RegExpGroupNonCapture['kind']; - } - | { - type: 'NAMED_CAPTURE'; - name: string; - }; + | { + type: 'NON_CAPTURE'; + kind: RegExpGroupNonCapture['kind']; + } + | { + type: 'NAMED_CAPTURE'; + name: string; + }; type RegExpParserOptions = ParserOptions & { - unicode: boolean; + unicode: boolean; }; function isHex(str: string): boolean { - return !/[^0-9a-fA-F]/.test(str); + return !/[^0-9a-fA-F]/.test(str); } function isOct(str: string): boolean { - const OCT_REGEX = /^[0-7]+$/; - return OCT_REGEX.test(str); + const OCT_REGEX = /^[0-7]+$/; + return OCT_REGEX.test(str); } function getCodePoint(char: string): number { - if (char.length === 1) { - const point = char.codePointAt(0); - if (point !== undefined) { - return point; - } - } - - throw new Error('Input was not 1 character long'); + if (char.length === 1) { + const point = char.codePointAt(0); + if (point !== undefined) { + return point; + } + } + + throw new Error('Input was not 1 character long'); } function readOctalCode( - input: string, - index: Number0, - nextChar: string, + input: string, + index: Number0, + nextChar: string, ): { - octalValue: number | undefined; - end: Number0; + octalValue: number | undefined; + end: Number0; } { - let char = nextChar; - let octal = ''; - let nextIndex: Number0 = ob1Add(index, 1); - while (isDigit(char)) { - octal += char; - // stop at max octal ascii in case of octal escape - if (parseInt(octal) > 377) { - octal = octal.slice(0, octal.length - 1); - break; - } - nextIndex = ob1Add(nextIndex, 1); - char = input[ob1Get0(nextIndex)]; - } - if (octal === '') { - return {octalValue: undefined, end: nextIndex}; - } - const octalValue = parseInt(octal, 10); - return {octalValue, end: nextIndex}; + let char = nextChar; + let octal = ''; + let nextIndex: Number0 = ob1Add(index, 1); + while (isDigit(char)) { + octal += char; + // stop at max octal ascii in case of octal escape + if (parseInt(octal) > 377) { + octal = octal.slice(0, octal.length - 1); + break; + } + nextIndex = ob1Add(nextIndex, 1); + char = input[ob1Get0(nextIndex)]; + } + if (octal === '') { + return {octalValue: undefined, end: nextIndex}; + } + const octalValue = parseInt(octal, 10); + return {octalValue, end: nextIndex}; } export const createRegExpParser = createParser((ParserCore) => - class RegExpParser extends ParserCore { - constructor(opts: RegExpParserOptions) { - super(opts, 'parse/regex'); - this.diagnostics = []; - this.unicode = opts.unicode; - } - - diagnostics: Diagnostics; - unicode: boolean; - - addDiagnostic(opts: ParserUnexpectedOptions): void { - this.diagnostics.push(this.createDiagnostic(opts)); - } - - unexpected(): never { - throw new Error('No throwing'); - } - - tokenize(index: Number0, input: string): TokenValues { - const char = input[ob1Get0(index)]; - - if (char === '\\') { - let end = ob1Add(index, 2); - - const nextChar = input[ob1Get0(index) + 1]; - switch (nextChar) { - case 't': - return this.finishComplexToken( - 'Character', - { - escaped: false, - value: '\t', - }, - end, - ); - - case 'n': - return this.finishComplexToken( - 'Character', - { - escaped: false, - value: '\n', - }, - end, - ); - - case 'r': - return this.finishComplexToken( - 'Character', - { - escaped: false, - value: '\r', - }, - end, - ); - - case 'v': - return this.finishComplexToken( - 'Character', - { - escaped: false, - value: '\x0b', - }, - end, - ); - - case 'f': - return this.finishComplexToken( - 'Character', - { - escaped: false, - value: '\f', - }, - end, - ); - - case 'd': - case 'D': - case 'b': - case 'B': - case 's': - case 'S': - case 'w': - case 'W': - return this.finishValueToken('EscapedCharacter', nextChar, end); - - case 'k': { - if (this.unicode) { - // named group back reference https://github.com/tc39/proposal-regexp-named-groups#backreferences - let namedBackReference = ''; - let namedBackReferenceIndex = ob1Get0(index) + 2; - let namedBackReferenceChar = input[namedBackReferenceIndex]; - if (namedBackReferenceChar === '<') { - namedBackReferenceChar = input[namedBackReferenceIndex]; - while ( - namedBackReferenceChar !== '>' && - namedBackReferenceIndex < input.length - ) { - namedBackReference += namedBackReferenceChar; - namedBackReferenceIndex++; - namedBackReferenceChar = input[namedBackReferenceIndex]; - } - if (namedBackReferenceChar === '>') { - namedBackReference += namedBackReferenceChar; - namedBackReferenceIndex++; - } - return this.finishComplexToken( - 'NamedBackReferenceCharacter', - { - value: namedBackReference, - escaped: true, - }, - ob1Coerce0(namedBackReferenceIndex), - ); - } - } - - return this.finishComplexToken( - 'Character', - { - value: 'k', - escaped: true, - }, - end, - ); - } - - case 'p': { - if (this.unicode) { - // TODO unicode property escapes https://github.com/tc39/proposal-regexp-unicode-property-escapes - } - - return this.finishComplexToken( - 'Character', - { - value: 'p', - escaped: true, - }, - end, - ); - } - - case 'P': { - if (this.unicode) { - // TODO unicode property escapes https://github.com/tc39/proposal-regexp-unicode-property-escapes - } - - return this.finishComplexToken( - 'Character', - { - value: 'P', - escaped: true, - }, - end, - ); - } - - case 'c': - // TODO??? - return this.finishComplexToken( - 'Character', - { - value: 'c', - escaped: true, - }, - end, - ); - - case '0': { - const {octalValue, end: octalEnd} = readOctalCode( - input, - index, - nextChar, - ); - if (octalValue !== undefined && isOct(octalValue.toString())) { - const octal = parseInt(octalValue.toString(), 8); - return this.finishComplexToken( - 'Character', - { - value: String.fromCharCode(octal), - escaped: true, - }, - octalEnd, - ); - } - return this.finishComplexToken( - 'Character', - { - value: String.fromCharCode(0), - escaped: true, - }, - end, - ); - } - - case 'x': { - const possibleHex = input.slice( - ob1Get0(index) + 1, - ob1Get0(index) + 3, - ); - - // \xhh - if (possibleHex.length === 2 && isHex(possibleHex)) { - end = ob1Add(end, 2); - - return this.finishComplexToken( - 'Character', - { - value: String.fromCharCode(parseInt(possibleHex, 16)), - escaped: true, - }, - end, - ); - } - - return this.finishComplexToken( - 'Character', - { - value: 'x', - escaped: true, - }, - end, - ); - } - - case 'u': { - // Get the next 4 characters after \u - const possibleHex = input.slice( - ob1Get0(index) + 2, - ob1Get0(index) + 6, - ); - - // \uhhhh - if (possibleHex.length === 4 && isHex(possibleHex)) { - end = ob1Add(end, 4); - - return this.finishComplexToken( - 'Character', - { - value: String.fromCharCode(parseInt(possibleHex, 16)), - escaped: true, - }, - end, - ); - } - - if (this.unicode) { - // TODO \u{hhhh} or \u{hhhhh} - } - - return this.finishComplexToken( - 'Character', - { - value: 'u', - escaped: true, - }, - end, - ); - } - - // Redundant escaping - default: { - let { - octalValue: referenceValue, - end: referenceEnd, - } = readOctalCode(input, index, nextChar); - if (referenceValue !== undefined) { - let backReference = referenceValue.toString(); - // \8 \9 are treated as escape char - if (referenceValue === 8 || referenceValue === 9) { - return this.finishComplexToken( - 'Character', - { - value: backReference, - escaped: true, - }, - referenceEnd, - ); - } - - if (isOct(backReference)) { - const octal = parseInt(backReference, 8); - return this.finishComplexToken( - 'Character', - { - value: String.fromCharCode(octal), - escaped: true, - }, - referenceEnd, - ); - } - - // back reference allowed are 1 - 99 - if (referenceValue >= 1 && referenceValue <= 99) { - return this.finishComplexToken( - 'NumericBackReferenceCharacter', - { - value: parseInt(backReference, 10), - escaped: true, - }, - referenceEnd, - ); - } else { - backReference = backReference.slice(0, backReference.length - 1); - referenceEnd = ob1Add(referenceEnd, -1); - if (isOct(backReference)) { - return this.finishComplexToken( - 'Character', - { - value: String.fromCharCode(parseInt(backReference, 8)), - escaped: true, - }, - referenceEnd, - ); - } else { - return this.finishComplexToken( - 'NumericBackReferenceCharacter', - { - value: parseInt(backReference, 10), - escaped: true, - }, - referenceEnd, - ); - } - } - } - - return this.finishComplexToken( - 'Character', - { - value: nextChar, - escaped: true, - }, - end, - ); - } - } - } - - switch (char) { - case '$': - case '^': - case '.': - case '?': - case '{': - case '}': - case '+': - case '|': - case '*': - case '[': - case ']': - case '(': - case ')': - return this.finishValueToken('Operator', char); - } - - return this.finishComplexToken( - 'Character', - { - value: char, - escaped: false, - }, - ); - } - - getGroupModifiers(): undefined | GroupModifiers { - const token = this.getToken(); - - if (token.type === 'Character') { - switch (token.value) { - case ':': { - this.nextToken(); - return { - type: 'NON_CAPTURE', - kind: undefined, - }; - } - - case '=': { - this.nextToken(); - return { - type: 'NON_CAPTURE', - kind: 'positive-lookahead', - }; - } - - case '!': { - this.nextToken(); - return { - type: 'NON_CAPTURE', - kind: 'negative-lookahead', - }; - } - - case '<': { - const nextToken = this.lookaheadToken(); - - if (nextToken.type === 'Character') { - switch (nextToken.value) { - case '!': { - this.nextToken(); - this.nextToken(); - return { - type: 'NON_CAPTURE', - kind: 'negative-lookbehind', - }; - } - - case '=': { - this.nextToken(); - this.nextToken(); - return { - type: 'NON_CAPTURE', - kind: 'positive-lookbehind', - }; - } - } - - if (isESIdentifierStart(nextToken.value)) { - let name = ''; - - // 1 is for the < - let skipCount = 1; - let targetToken: TokenValues = nextToken; - while ( - targetToken.type === 'Character' && - isESIdentifierChar(targetToken.value) - ) { - name += targetToken.value; - targetToken = this.lookaheadToken(targetToken.end); - skipCount++; - } - - if ( - targetToken.type === 'Character' && - targetToken.value === '>' - ) { - // Skip through all the name tokens including > - skipCount++; - - // This is kinda a hacky solution, and slower than it could be - for (let i = 0; i < skipCount; i++) { - this.nextToken(); - } - - return { - type: 'NAMED_CAPTURE', - name, - }; - } - } - } - } - } - } - - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.INVALID_CAPTURE_GROUP_MODIFIER, - token, - }); - - return undefined; - } - - matchOperator(op: string): boolean { - const token = this.getToken(); - return token.type === 'Operator' && token.value === op; - } - - eatOperator(op: string): boolean { - if (this.matchOperator(op)) { - this.nextToken(); - return true; - } else { - return false; - } - } - - parseGroupCapture(): RegExpGroupCapture | RegExpGroupNonCapture { - const start = this.getPosition(); - this.nextToken(); - - let modifiers: undefined | GroupModifiers; - if (this.eatOperator('?')) { - modifiers = this.getGroupModifiers(); - } - - const expression = this.parseExpression(() => !this.matchOperator(')')); - - if (!this.eatOperator(')')) { - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.UNCLOSED_GROUP, - start, - }); - } - - if (modifiers !== undefined && modifiers.type === 'NON_CAPTURE') { - return { - type: 'RegExpGroupNonCapture', - expression, - kind: modifiers.kind, - loc: this.finishLoc(start), - }; - } else { - let name = modifiers !== undefined ? modifiers.name : undefined; - return { - type: 'RegExpGroupCapture', - expression, - name, - loc: this.finishLoc(start), - }; - } - } - - parseCharSet(): RegExpCharSet { - const start = this.getPosition(); - this.nextToken(); - - const body: RegExpCharSet['body'] = []; - const invert = this.eatOperator('^'); - - while (!this.matchToken('EOF') && !this.matchOperator(']')) { - const part = this.parseCharacterOrRange(); - body.push(part); - } - - if (!this.eatOperator(']')) { - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.UNCLOSED_CHAR_SET, - start, - }); - } - - return { - type: 'RegExpCharSet', - invert, - body, - loc: this.finishLoc(start), - }; - } - - getCharacterFromToken(token: TokenValues): string { - switch (token.type) { - case 'Character': - case 'Operator': - return token.value; - - case 'SOF': - case 'EOF': - case 'Invalid': - throw new Error('Unnecessary'); - - default: - throw new Error('Never'); - } - } - - parseCharacter(): AnyRegExpEscapedCharacter { - const token = this.getToken(); - - if (token.type === 'Character') { - this.nextToken(); - return { - type: 'RegExpCharacter', - value: token.value, - loc: this.finishLocFromToken(token), - }; - } - - if (token.type === 'NumericBackReferenceCharacter') { - this.nextToken(); - - return { - type: 'RegExpNumericBackReference', - value: token.value, - loc: this.finishLocFromToken(token), - }; - } - - if (token.type === 'NamedBackReferenceCharacter') { - const start = this.input.slice(0, ob1Get0(token.start)); - this.nextToken(); - - if (token.value[token.value.length - 1] !== '>') { - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.UNCLOSED_NAMED_CAPTURE, - loc: this.finishLocFromToken(token), - }); - } - - if (!start.includes(token.value)) { - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.INVALID_NAMED_CAPTURE, - loc: this.finishLocFromToken(token), - }); - } - - const name = token.value.slice(1, token.value.length - 1); - return { - type: 'RegExpNamedBackReference', - name, - loc: this.finishLocFromToken(token), - }; - } - - if (token.type === 'EscapedCharacter') { - this.nextToken(); - - const loc = this.finishLocFromToken(token); - switch (token.value) { - case 'd': - return { - type: 'RegExpDigitCharacter', - loc, - }; - - case 'D': - return { - type: 'RegExpNonDigitCharacter', - loc, - }; - - case 'b': - return { - type: 'RegExpWordBoundaryCharacter', - loc, - }; - - case 'B': - return { - type: 'RegExpNonWordBoundaryCharacter', - loc, - }; - - case 's': - return { - type: 'RegExpWhiteSpaceCharacter', - loc, - }; - - case 'S': - return { - type: 'RegExpNonWhiteSpaceCharacter', - loc, - }; - - case 'w': - return { - type: 'RegExpWordCharacter', - loc, - }; - - case 'W': - return { - type: 'RegExpNonWordCharacter', - loc, - }; - } - } - - this.nextToken(); - return { - type: 'RegExpCharacter', - value: this.getCharacterFromToken(token), - loc: this.finishLocFromToken(token), - }; - } - - parseCharacterOrRange(): AnyRegExpEscapedCharacter | RegExpCharSetRange { - const startPos = this.getPosition(); - let start = this.parseCharacter(); - - // Range - const nextToken = this.getToken(); - if ( - start.type === 'RegExpCharacter' && - nextToken.type === 'Character' && - nextToken.value === '-' && - !nextToken.escaped - ) { - const lookaheadToken = this.lookaheadToken(); - if (lookaheadToken.type === 'Character') { - // Skip dash - this.nextToken(); - - let end = this.parseCharacter(); - - const loc = this.finishLoc(startPos); - - if ( - start.type === 'RegExpCharacter' && - end.type === 'RegExpCharacter' && - getCodePoint(end.value) < getCodePoint(start.value) - ) { - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.REVERSED_CHAR_SET_RANGE, - loc, - }); - const _end = end; - end = start; - start = _end; - } - - return { - type: 'RegExpCharSetRange', - loc, - start, - end, - }; - } - } - - return start; - } - - parseDigits(): undefined | number { - let digits = ''; - let token = this.getToken(); - while (token.type === 'Character' && isDigit(token.value)) { - digits += token.value; - token = this.nextToken(); - } - - if (digits.length === 0) { - return undefined; - } else { - return Number(digits); - } - } - - parseQuantifier(): - | undefined - | { - min: number; - max?: number; - } { - if (this.eatOperator('?')) { - return { - min: 0, - max: 1, - }; - } - - if (this.eatOperator('*')) { - return { - min: 0, - max: undefined, - }; - } - - if (this.eatOperator('+')) { - return { - min: 1, - max: undefined, - }; - } - - if (this.matchOperator('{')) { - const snapshot = this.save(); - - this.nextToken(); - - const start = this.getPosition(); - const min = this.parseDigits(); - - if (min !== undefined) { - const nextToken = this.getToken(); - if (nextToken.type === 'Character' && nextToken.value === ',') { - this.nextToken(); - - const max = this.parseDigits(); - const end = this.getPosition(); - - const endToken = this.getToken(); - if (endToken.type === 'Operator' && endToken.value === '}') { - this.nextToken(); - - if (max !== undefined && min > max) { - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.REVERSED_QUANTIFIER_RANGE, - start, - end, - }); - return { - max: min, - min: max, - }; - } - - return { - min, - max, - }; - } - } else if (nextToken.type === 'Operator' && nextToken.value === '}') { - this.nextToken(); - return { - min, - max: min, - }; - } - } - - this.restore(snapshot); - } - - return undefined; - } - - parseBodyItem(): undefined | AnyRegExpBodyItem { - const start = this.getPosition(); - - const prefix = this.parseBodyItemPrefix(); - if (prefix === undefined) { - return undefined; - } - - let target = prefix; - - while (true) { - const quantifier = this.parseQuantifier(); - if (quantifier === undefined) { - break; - } - - const lazy = this.eatOperator('?'); - - const quantified: RegExpQuantified = { - type: 'RegExpQuantified', - loc: this.finishLoc(start), - target, - lazy, - ...quantifier, - }; - - target = quantified; - } - - return target; - } - - parseOperator(token: Tokens['Operator']): undefined | AnyRegExpBodyItem { - switch (token.value) { - case '$': { - this.nextToken(); - return { - type: 'RegExpEndCharacter', - loc: this.finishLocFromToken(token), - }; - } - - case '^': { - this.nextToken(); - return { - type: 'RegExpStartCharacter', - loc: this.finishLocFromToken(token), - }; - } - - case '.': { - this.nextToken(); - return { - type: 'RegExpAnyCharacter', - loc: this.finishLocFromToken(token), - }; - } - - case '[': - return this.parseCharSet(); - - case '(': - return this.parseGroupCapture(); - - case ')': { - this.nextToken(); - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.UNOPENED_GROUP, - token, - }); - return; - } - - case '{': { - const start = this.getPosition(); - const unmatchedQuantifier = this.parseQuantifier(); - if (unmatchedQuantifier === undefined) { - // Quantifier is undefined and eaten tokens were restored - // Return a '{' token as a RegexpCharacter, parseBodyItem() will handle parsing of subsequent quantifiers - return this.parseCharacter(); - } else { - // If quantifier is defined, then syntax error: Nothing to repeat - const end = this.getPosition(); - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.NO_TARGET_QUANTIFIER, - start, - end, - }); - return; - } - } - - case '?': - case '*': - case '+': { - this.nextToken(); - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.INVALID_QUANTIFIER_TARGET, - token, - }); - return; - } - - case ']': - case '}': - return this.parseCharacter(); - - default: - return undefined; - } - } - - parseBodyItemPrefix(): undefined | AnyRegExpBodyItem { - const token = this.getToken(); - - switch (token.type) { - case 'Operator': - return this.parseOperator(token); - - case 'EscapedCharacter': - case 'Character': - case 'NumericBackReferenceCharacter': - case 'NamedBackReferenceCharacter': - return this.parseCharacter(); - } - - this.addDiagnostic({ - description: descriptions.REGEX_PARSER.UNKNOWN_REGEX_PART, - token, - }); - - return undefined; - } - - parseExpression( - whileCallback?: () => boolean, - ): RegExpSubExpression | RegExpAlternation { - const alternations: Array<{ - start: Position; - end: Position; - body: Array; - }> = []; - let body: Array = []; - - const start = this.getPosition(); - let alternateStart = start; - - while ( - !this.matchToken('EOF') && - (whileCallback === undefined || whileCallback()) - ) { - if (this.eatOperator('|')) { - alternations.push({ - start: alternateStart, - end: this.getPosition(), - body, - }); - alternateStart = this.getPosition(); - body = []; - continue; - } - - const part = this.parseBodyItem(); - if (part !== undefined) { - body.push(part); - } - } - - alternations.push({ - body, - start: alternateStart, - end: this.getPosition(), - }); - - let expression: undefined | RegExpSubExpression | RegExpAlternation; - - while (alternations.length > 0) { - const alternation = alternations.shift()!; - - const sub: RegExpSubExpression = { - type: 'RegExpSubExpression', - body: alternation.body, - loc: this.finishLocAt(alternation.start, alternation.end), - }; - - if (expression === undefined) { - expression = sub; - } else { - const alternationNode: RegExpAlternation = { - type: 'RegExpAlternation', - left: expression, - right: sub, - loc: this.finishLocAt( - this.getLoc(expression).start, - alternation.end, - ), - }; - - expression = alternationNode; - } - } - - if (expression === undefined) { - throw new Error( - 'Impossible. We should always have at least one alternation that will set this.', - ); - } - - return expression; - } - - parse(): { - expression: AnyRegExpExpression; - diagnostics: Diagnostics; - } { - return { - expression: this.parseExpression(), - diagnostics: this.diagnostics, - }; - } - } + class RegExpParser extends ParserCore { + constructor(opts: RegExpParserOptions) { + super(opts, 'parse/regex'); + this.diagnostics = []; + this.unicode = opts.unicode; + } + + diagnostics: Diagnostics; + unicode: boolean; + + addDiagnostic(opts: ParserUnexpectedOptions): void { + this.diagnostics.push(this.createDiagnostic(opts)); + } + + unexpected(): never { + throw new Error('No throwing'); + } + + tokenize(index: Number0, input: string): TokenValues { + const char = input[ob1Get0(index)]; + + if (char === '\\') { + let end = ob1Add(index, 2); + + const nextChar = input[ob1Get0(index) + 1]; + switch (nextChar) { + case 't': + return this.finishComplexToken( + 'Character', + { + escaped: false, + value: '\t', + }, + end, + ); + + case 'n': + return this.finishComplexToken( + 'Character', + { + escaped: false, + value: '\n', + }, + end, + ); + + case 'r': + return this.finishComplexToken( + 'Character', + { + escaped: false, + value: '\r', + }, + end, + ); + + case 'v': + return this.finishComplexToken( + 'Character', + { + escaped: false, + value: '\x0b', + }, + end, + ); + + case 'f': + return this.finishComplexToken( + 'Character', + { + escaped: false, + value: '\f', + }, + end, + ); + + case 'd': + case 'D': + case 'b': + case 'B': + case 's': + case 'S': + case 'w': + case 'W': + return this.finishValueToken('EscapedCharacter', nextChar, end); + + case 'k': { + if (this.unicode) { + // named group back reference https://github.com/tc39/proposal-regexp-named-groups#backreferences + let namedBackReference = ''; + let namedBackReferenceIndex = ob1Get0(index) + 2; + let namedBackReferenceChar = input[namedBackReferenceIndex]; + if (namedBackReferenceChar === '<') { + namedBackReferenceChar = input[namedBackReferenceIndex]; + while ( + namedBackReferenceChar !== '>' && + namedBackReferenceIndex < input.length + ) { + namedBackReference += namedBackReferenceChar; + namedBackReferenceIndex++; + namedBackReferenceChar = input[namedBackReferenceIndex]; + } + if (namedBackReferenceChar === '>') { + namedBackReference += namedBackReferenceChar; + namedBackReferenceIndex++; + } + return this.finishComplexToken( + 'NamedBackReferenceCharacter', + { + value: namedBackReference, + escaped: true, + }, + ob1Coerce0(namedBackReferenceIndex), + ); + } + } + + return this.finishComplexToken( + 'Character', + { + value: 'k', + escaped: true, + }, + end, + ); + } + + case 'p': { + if (this.unicode) { + // TODO unicode property escapes https://github.com/tc39/proposal-regexp-unicode-property-escapes + } + + return this.finishComplexToken( + 'Character', + { + value: 'p', + escaped: true, + }, + end, + ); + } + + case 'P': { + if (this.unicode) { + // TODO unicode property escapes https://github.com/tc39/proposal-regexp-unicode-property-escapes + } + + return this.finishComplexToken( + 'Character', + { + value: 'P', + escaped: true, + }, + end, + ); + } + + case 'c': + // TODO??? + return this.finishComplexToken( + 'Character', + { + value: 'c', + escaped: true, + }, + end, + ); + + case '0': { + const {octalValue, end: octalEnd} = readOctalCode(input, index, nextChar); + if (octalValue !== undefined && isOct(octalValue.toString())) { + const octal = parseInt(octalValue.toString(), 8); + return this.finishComplexToken( + 'Character', + { + value: String.fromCharCode(octal), + escaped: true, + }, + octalEnd, + ); + } + return this.finishComplexToken( + 'Character', + { + value: String.fromCharCode(0), + escaped: true, + }, + end, + ); + } + + case 'x': { + const possibleHex = input.slice(ob1Get0(index) + 1, ob1Get0(index) + 3); + + // \xhh + if (possibleHex.length === 2 && isHex(possibleHex)) { + end = ob1Add(end, 2); + + return this.finishComplexToken( + 'Character', + { + value: String.fromCharCode(parseInt(possibleHex, 16)), + escaped: true, + }, + end, + ); + } + + return this.finishComplexToken( + 'Character', + { + value: 'x', + escaped: true, + }, + end, + ); + } + + case 'u': { + // Get the next 4 characters after \u + const possibleHex = input.slice(ob1Get0(index) + 2, ob1Get0(index) + 6); + + // \uhhhh + if (possibleHex.length === 4 && isHex(possibleHex)) { + end = ob1Add(end, 4); + + return this.finishComplexToken( + 'Character', + { + value: String.fromCharCode(parseInt(possibleHex, 16)), + escaped: true, + }, + end, + ); + } + + if (this.unicode) { + // TODO \u{hhhh} or \u{hhhhh} + } + + return this.finishComplexToken( + 'Character', + { + value: 'u', + escaped: true, + }, + end, + ); + } + + // Redundant escaping + default: { + let { + octalValue: referenceValue, + end: referenceEnd, + } = readOctalCode(input, index, nextChar); + if (referenceValue !== undefined) { + let backReference = referenceValue.toString(); + // \8 \9 are treated as escape char + if (referenceValue === 8 || referenceValue === 9) { + return this.finishComplexToken( + 'Character', + { + value: backReference, + escaped: true, + }, + referenceEnd, + ); + } + + if (isOct(backReference)) { + const octal = parseInt(backReference, 8); + return this.finishComplexToken( + 'Character', + { + value: String.fromCharCode(octal), + escaped: true, + }, + referenceEnd, + ); + } + + // back reference allowed are 1 - 99 + if (referenceValue >= 1 && referenceValue <= 99) { + return this.finishComplexToken( + 'NumericBackReferenceCharacter', + { + value: parseInt(backReference, 10), + escaped: true, + }, + referenceEnd, + ); + } else { + backReference = backReference.slice(0, backReference.length - 1); + referenceEnd = ob1Add(referenceEnd, -1); + if (isOct(backReference)) { + return this.finishComplexToken( + 'Character', + { + value: String.fromCharCode(parseInt(backReference, 8)), + escaped: true, + }, + referenceEnd, + ); + } else { + return this.finishComplexToken( + 'NumericBackReferenceCharacter', + { + value: parseInt(backReference, 10), + escaped: true, + }, + referenceEnd, + ); + } + } + } + + return this.finishComplexToken( + 'Character', + { + value: nextChar, + escaped: true, + }, + end, + ); + } + } + } + + switch (char) { + case '$': + case '^': + case '.': + case '?': + case '{': + case '}': + case '+': + case '|': + case '*': + case '[': + case ']': + case '(': + case ')': + return this.finishValueToken('Operator', char); + } + + return this.finishComplexToken( + 'Character', + { + value: char, + escaped: false, + }, + ); + } + + getGroupModifiers(): undefined | GroupModifiers { + const token = this.getToken(); + + if (token.type === 'Character') { + switch (token.value) { + case ':': { + this.nextToken(); + return { + type: 'NON_CAPTURE', + kind: undefined, + }; + } + + case '=': { + this.nextToken(); + return { + type: 'NON_CAPTURE', + kind: 'positive-lookahead', + }; + } + + case '!': { + this.nextToken(); + return { + type: 'NON_CAPTURE', + kind: 'negative-lookahead', + }; + } + + case '<': { + const nextToken = this.lookaheadToken(); + + if (nextToken.type === 'Character') { + switch (nextToken.value) { + case '!': { + this.nextToken(); + this.nextToken(); + return { + type: 'NON_CAPTURE', + kind: 'negative-lookbehind', + }; + } + + case '=': { + this.nextToken(); + this.nextToken(); + return { + type: 'NON_CAPTURE', + kind: 'positive-lookbehind', + }; + } + } + + if (isESIdentifierStart(nextToken.value)) { + let name = ''; + + // 1 is for the < + let skipCount = 1; + let targetToken: TokenValues = nextToken; + while ( + targetToken.type === 'Character' && + isESIdentifierChar(targetToken.value) + ) { + name += targetToken.value; + targetToken = this.lookaheadToken(targetToken.end); + skipCount++; + } + + if (targetToken.type === 'Character' && targetToken.value === '>') { + // Skip through all the name tokens including > + skipCount++; + + // This is kinda a hacky solution, and slower than it could be + for (let i = 0; i < skipCount; i++) { + this.nextToken(); + } + + return { + type: 'NAMED_CAPTURE', + name, + }; + } + } + } + } + } + } + + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.INVALID_CAPTURE_GROUP_MODIFIER, + token, + }); + + return undefined; + } + + matchOperator(op: string): boolean { + const token = this.getToken(); + return token.type === 'Operator' && token.value === op; + } + + eatOperator(op: string): boolean { + if (this.matchOperator(op)) { + this.nextToken(); + return true; + } else { + return false; + } + } + + parseGroupCapture(): RegExpGroupCapture | RegExpGroupNonCapture { + const start = this.getPosition(); + this.nextToken(); + + let modifiers: undefined | GroupModifiers; + if (this.eatOperator('?')) { + modifiers = this.getGroupModifiers(); + } + + const expression = this.parseExpression(() => !this.matchOperator(')')); + + if (!this.eatOperator(')')) { + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.UNCLOSED_GROUP, + start, + }); + } + + if (modifiers !== undefined && modifiers.type === 'NON_CAPTURE') { + return { + type: 'RegExpGroupNonCapture', + expression, + kind: modifiers.kind, + loc: this.finishLoc(start), + }; + } else { + let name = modifiers !== undefined ? modifiers.name : undefined; + return { + type: 'RegExpGroupCapture', + expression, + name, + loc: this.finishLoc(start), + }; + } + } + + parseCharSet(): RegExpCharSet { + const start = this.getPosition(); + this.nextToken(); + + const body: RegExpCharSet['body'] = []; + const invert = this.eatOperator('^'); + + while (!this.matchToken('EOF') && !this.matchOperator(']')) { + const part = this.parseCharacterOrRange(); + body.push(part); + } + + if (!this.eatOperator(']')) { + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.UNCLOSED_CHAR_SET, + start, + }); + } + + return { + type: 'RegExpCharSet', + invert, + body, + loc: this.finishLoc(start), + }; + } + + getCharacterFromToken(token: TokenValues): string { + switch (token.type) { + case 'Character': + case 'Operator': + return token.value; + + case 'SOF': + case 'EOF': + case 'Invalid': + throw new Error('Unnecessary'); + + default: + throw new Error('Never'); + } + } + + parseCharacter(): AnyRegExpEscapedCharacter { + const token = this.getToken(); + + if (token.type === 'Character') { + this.nextToken(); + return { + type: 'RegExpCharacter', + value: token.value, + loc: this.finishLocFromToken(token), + }; + } + + if (token.type === 'NumericBackReferenceCharacter') { + this.nextToken(); + + return { + type: 'RegExpNumericBackReference', + value: token.value, + loc: this.finishLocFromToken(token), + }; + } + + if (token.type === 'NamedBackReferenceCharacter') { + const start = this.input.slice(0, ob1Get0(token.start)); + this.nextToken(); + + if (token.value[token.value.length - 1] !== '>') { + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.UNCLOSED_NAMED_CAPTURE, + loc: this.finishLocFromToken(token), + }); + } + + if (!start.includes(token.value)) { + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.INVALID_NAMED_CAPTURE, + loc: this.finishLocFromToken(token), + }); + } + + const name = token.value.slice(1, token.value.length - 1); + return { + type: 'RegExpNamedBackReference', + name, + loc: this.finishLocFromToken(token), + }; + } + + if (token.type === 'EscapedCharacter') { + this.nextToken(); + + const loc = this.finishLocFromToken(token); + switch (token.value) { + case 'd': + return { + type: 'RegExpDigitCharacter', + loc, + }; + + case 'D': + return { + type: 'RegExpNonDigitCharacter', + loc, + }; + + case 'b': + return { + type: 'RegExpWordBoundaryCharacter', + loc, + }; + + case 'B': + return { + type: 'RegExpNonWordBoundaryCharacter', + loc, + }; + + case 's': + return { + type: 'RegExpWhiteSpaceCharacter', + loc, + }; + + case 'S': + return { + type: 'RegExpNonWhiteSpaceCharacter', + loc, + }; + + case 'w': + return { + type: 'RegExpWordCharacter', + loc, + }; + + case 'W': + return { + type: 'RegExpNonWordCharacter', + loc, + }; + } + } + + this.nextToken(); + return { + type: 'RegExpCharacter', + value: this.getCharacterFromToken(token), + loc: this.finishLocFromToken(token), + }; + } + + parseCharacterOrRange(): AnyRegExpEscapedCharacter | RegExpCharSetRange { + const startPos = this.getPosition(); + let start = this.parseCharacter(); + + // Range + const nextToken = this.getToken(); + if ( + start.type === 'RegExpCharacter' && + nextToken.type === 'Character' && + nextToken.value === '-' && + !nextToken.escaped + ) { + const lookaheadToken = this.lookaheadToken(); + if (lookaheadToken.type === 'Character') { + // Skip dash + this.nextToken(); + + let end = this.parseCharacter(); + + const loc = this.finishLoc(startPos); + + if ( + start.type === 'RegExpCharacter' && + end.type === 'RegExpCharacter' && + getCodePoint(end.value) < getCodePoint(start.value) + ) { + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.REVERSED_CHAR_SET_RANGE, + loc, + }); + const _end = end; + end = start; + start = _end; + } + + return { + type: 'RegExpCharSetRange', + loc, + start, + end, + }; + } + } + + return start; + } + + parseDigits(): undefined | number { + let digits = ''; + let token = this.getToken(); + while (token.type === 'Character' && isDigit(token.value)) { + digits += token.value; + token = this.nextToken(); + } + + if (digits.length === 0) { + return undefined; + } else { + return Number(digits); + } + } + + parseQuantifier(): + | undefined + | { + min: number; + max?: number; + } { + if (this.eatOperator('?')) { + return { + min: 0, + max: 1, + }; + } + + if (this.eatOperator('*')) { + return { + min: 0, + max: undefined, + }; + } + + if (this.eatOperator('+')) { + return { + min: 1, + max: undefined, + }; + } + + if (this.matchOperator('{')) { + const snapshot = this.save(); + + this.nextToken(); + + const start = this.getPosition(); + const min = this.parseDigits(); + + if (min !== undefined) { + const nextToken = this.getToken(); + if (nextToken.type === 'Character' && nextToken.value === ',') { + this.nextToken(); + + const max = this.parseDigits(); + const end = this.getPosition(); + + const endToken = this.getToken(); + if (endToken.type === 'Operator' && endToken.value === '}') { + this.nextToken(); + + if (max !== undefined && min > max) { + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.REVERSED_QUANTIFIER_RANGE, + start, + end, + }); + return { + max: min, + min: max, + }; + } + + return { + min, + max, + }; + } + } else if (nextToken.type === 'Operator' && nextToken.value === '}') { + this.nextToken(); + return { + min, + max: min, + }; + } + } + + this.restore(snapshot); + } + + return undefined; + } + + parseBodyItem(): undefined | AnyRegExpBodyItem { + const start = this.getPosition(); + + const prefix = this.parseBodyItemPrefix(); + if (prefix === undefined) { + return undefined; + } + + let target = prefix; + + while (true) { + const quantifier = this.parseQuantifier(); + if (quantifier === undefined) { + break; + } + + const lazy = this.eatOperator('?'); + + const quantified: RegExpQuantified = { + type: 'RegExpQuantified', + loc: this.finishLoc(start), + target, + lazy, + ...quantifier, + }; + + target = quantified; + } + + return target; + } + + parseOperator(token: Tokens['Operator']): undefined | AnyRegExpBodyItem { + switch (token.value) { + case '$': { + this.nextToken(); + return { + type: 'RegExpEndCharacter', + loc: this.finishLocFromToken(token), + }; + } + + case '^': { + this.nextToken(); + return { + type: 'RegExpStartCharacter', + loc: this.finishLocFromToken(token), + }; + } + + case '.': { + this.nextToken(); + return { + type: 'RegExpAnyCharacter', + loc: this.finishLocFromToken(token), + }; + } + + case '[': + return this.parseCharSet(); + + case '(': + return this.parseGroupCapture(); + + case ')': { + this.nextToken(); + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.UNOPENED_GROUP, + token, + }); + return; + } + + case '{': { + const start = this.getPosition(); + const unmatchedQuantifier = this.parseQuantifier(); + if (unmatchedQuantifier === undefined) { + // Quantifier is undefined and eaten tokens were restored + // Return a '{' token as a RegexpCharacter, parseBodyItem() will handle parsing of subsequent quantifiers + return this.parseCharacter(); + } else { + // If quantifier is defined, then syntax error: Nothing to repeat + const end = this.getPosition(); + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.NO_TARGET_QUANTIFIER, + start, + end, + }); + return; + } + } + + case '?': + case '*': + case '+': { + this.nextToken(); + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.INVALID_QUANTIFIER_TARGET, + token, + }); + return; + } + + case ']': + case '}': + return this.parseCharacter(); + + default: + return undefined; + } + } + + parseBodyItemPrefix(): undefined | AnyRegExpBodyItem { + const token = this.getToken(); + + switch (token.type) { + case 'Operator': + return this.parseOperator(token); + + case 'EscapedCharacter': + case 'Character': + case 'NumericBackReferenceCharacter': + case 'NamedBackReferenceCharacter': + return this.parseCharacter(); + } + + this.addDiagnostic({ + description: descriptions.REGEX_PARSER.UNKNOWN_REGEX_PART, + token, + }); + + return undefined; + } + + parseExpression( + whileCallback?: () => boolean, + ): RegExpSubExpression | RegExpAlternation { + const alternations: Array<{ + start: Position; + end: Position; + body: Array; + }> = []; + let body: Array = []; + + const start = this.getPosition(); + let alternateStart = start; + + while ( + !this.matchToken('EOF') && + (whileCallback === undefined || whileCallback()) + ) { + if (this.eatOperator('|')) { + alternations.push({ + start: alternateStart, + end: this.getPosition(), + body, + }); + alternateStart = this.getPosition(); + body = []; + continue; + } + + const part = this.parseBodyItem(); + if (part !== undefined) { + body.push(part); + } + } + + alternations.push({ + body, + start: alternateStart, + end: this.getPosition(), + }); + + let expression: undefined | RegExpSubExpression | RegExpAlternation; + + while (alternations.length > 0) { + const alternation = alternations.shift()!; + + const sub: RegExpSubExpression = { + type: 'RegExpSubExpression', + body: alternation.body, + loc: this.finishLocAt(alternation.start, alternation.end), + }; + + if (expression === undefined) { + expression = sub; + } else { + const alternationNode: RegExpAlternation = { + type: 'RegExpAlternation', + left: expression, + right: sub, + loc: this.finishLocAt(this.getLoc(expression).start, alternation.end), + }; + + expression = alternationNode; + } + } + + if (expression === undefined) { + throw new Error( + 'Impossible. We should always have at least one alternation that will set this.', + ); + } + + return expression; + } + + parse(): { + expression: AnyRegExpExpression; + diagnostics: Diagnostics; + } { + return { + expression: this.parseExpression(), + diagnostics: this.diagnostics, + }; + } + } ); diff --git a/packages/@romejs/codec-json/index.ts b/packages/@romejs/codec-json/index.ts index 2d52b61a05e..6a4e4d5b95f 100644 --- a/packages/@romejs/codec-json/index.ts +++ b/packages/@romejs/codec-json/index.ts @@ -12,59 +12,63 @@ import {stringifyRootConsumer} from './stringify'; import {TokenValues} from '@romejs/parser-core'; export { - JSONArray, - JSONObject, - JSONParserOptions, - JSONPropertyValue, - JSONValue, + JSONArray, + JSONObject, + JSONParserOptions, + JSONPropertyValue, + JSONValue, } from './types'; export type ConsumeJSONResult = { - hasExtensions: boolean; - consumer: Consumer; - comments: PathToComments; + hasExtensions: boolean; + consumer: Consumer; + comments: PathToComments; }; export function consumeJSON(opts: JSONParserOptions): Consumer { - return consumeJSONExtra(opts).consumer; + return consumeJSONExtra(opts).consumer; } export function consumeJSONExtra(opts: JSONParserOptions): ConsumeJSONResult { - const parser = createJSONParser(opts); - const {value, context} = parser.parse(); + const parser = createJSONParser(opts); + const {value, context} = parser.parse(); - return { - hasExtensions: parser.hasExtensions, - consumer: consume({ - filePath: parser.path, - context, - objectPath: [], - value, - parent: undefined, - }), - comments: parser.pathToComments, - }; + return { + hasExtensions: parser.hasExtensions, + consumer: consume({ + filePath: parser.path, + context, + objectPath: [], + value, + parent: undefined, + }), + comments: parser.pathToComments, + }; } export function parseJSON(opts: JSONParserOptions): JSONValue { - return createJSONParser(opts).parse().value; + return createJSONParser(opts).parse().value; } export function tokenizeJSON( - opts: JSONParserOptions, + opts: JSONParserOptions, ): Array> { - return createJSONParser(opts).tokenizeAll(); + return createJSONParser(opts).tokenizeAll(); } export function stringifyRJSONFromConsumer( - opts: { - consumer: Consumer; - comments: PathToComments; - }, + opts: { + consumer: Consumer; + comments: PathToComments; + }, ): string { - return stringifyRootConsumer(opts.consumer, opts.comments); + return stringifyRootConsumer(opts.consumer, opts.comments); } export function stringifyRJSON(value: unknown): string { - return stringifyRootConsumer(consumeUnknown(value, 'parse/json'), new Map()); + return stringifyRootConsumer(consumeUnknown(value, 'parse/json'), new Map()); +} + +export function stringifyJSON(value: unknown): string { + return JSON.stringify(value, null, '\t'); } diff --git a/packages/@romejs/codec-json/parse.ts b/packages/@romejs/codec-json/parse.ts index 6be1b3cf48e..221ab7f72e5 100644 --- a/packages/@romejs/codec-json/parse.ts +++ b/packages/@romejs/codec-json/parse.ts @@ -6,917 +6,914 @@ */ import { - DiagnosticCategory, - DiagnosticLocation, - descriptions, + DiagnosticCategory, + DiagnosticLocation, + descriptions, } from '@romejs/diagnostics'; import { - Comments, - JSONObject, - JSONParserOptions, - JSONParserResult, - JSONValue, - PathComments, - PathToComments, - Tokens, + Comments, + JSONObject, + JSONParserOptions, + JSONParserResult, + JSONValue, + PathComments, + PathToComments, + Tokens, } from './types'; import { - ConsumeContext, - ConsumePath, - ConsumeSourceLocationRequestTarget, + ConsumeContext, + ConsumePath, + ConsumeSourceLocationRequestTarget, } from '@romejs/consume'; import {unescapeString} from '@romejs/string-escape'; import { - Position, - SourceLocation, - createParser, - isAlpha, - isDigit, + Position, + SourceLocation, + createParser, + isAlpha, + isDigit, } from '@romejs/parser-core'; import {Number0, ob1Add, ob1Get0, ob1Inc, ob1Sub} from '@romejs/ob1'; import {isEscaped} from '@romejs/string-utils'; // Words can't start with a digit function isWordStartChar(char: string): boolean { - return isAlpha(char) || char === '_' || char === '$'; + return isAlpha(char) || char === '_' || char === '$'; } // But a digit can appear inside of a word function isWordChar(char: string): boolean { - return isWordStartChar(char) || isDigit(char); + return isWordStartChar(char) || isDigit(char); } // Check if an input string is a valid word, this is used by the stringifier to // determine if a property key should be quoted export function isValidWord(word: string): boolean { - if (word.length === 0 || isWordStartChar(word[0]) === false) { - return false; - } + if (word.length === 0 || isWordStartChar(word[0]) === false) { + return false; + } - for (const char of word) { - if (isWordChar(char) === false) { - return false; - } - } + for (const char of word) { + if (isWordChar(char) === false) { + return false; + } + } - return true; + return true; } // Check if a character is a part of a string, returning false for a newline or unescaped quote char function isStringValueChar(char: string, index: Number0, input: string): boolean { - if (char === '\n') { - return false; - } + if (char === '\n') { + return false; + } - if (char === '"' && !isEscaped(index, input)) { - return false; - } + if (char === '"' && !isEscaped(index, input)) { + return false; + } - return true; + return true; } // Turn a path into a string key we can use export function toPathKey(parts: Array) { - // Right now this could conflict weirdly with properties with dots in them if they cause collisions - // We have this method abstracted so we can make changes later if it's necessary (probably not worth it) - return parts.join('.'); + // Right now this could conflict weirdly with properties with dots in them if they cause collisions + // We have this method abstracted so we can make changes later if it's necessary (probably not worth it) + return parts.join('.'); } function isntNewline(char: string): boolean { - return char !== '\n'; + return char !== '\n'; } function isntBlockCommentEnd( - char: string, - index: Number0, - input: string, + char: string, + index: Number0, + input: string, ): boolean { - const nextChar = input[ob1Get0(index) + 1]; - return char !== '*' && nextChar !== '/'; + const nextChar = input[ob1Get0(index) + 1]; + return char !== '*' && nextChar !== '/'; } // Used for Number token validation, allow underscore as a separatore function isNumberChar(char: string): boolean { - return isDigit(char) || char === '_'; + return isDigit(char) || char === '_'; } type PathInfo = { - originalValue: unknown; - keyStart: Position; - keyEnd: Position; - valueStart: Position; - valueEnd: Position; + originalValue: unknown; + keyStart: Position; + keyEnd: Position; + valueStart: Position; + valueEnd: Position; }; export const createJSONParser = createParser((ParserCore) => - class JSONParser extends ParserCore { - constructor(opts: JSONParserOptions) { - super( - { - ...opts, - retainCarriageReturn: true, - }, - 'parse/json', - ); - - this.options = opts; - this.ignoreWhitespaceTokens = true; - - this.hasExtensions = - this.path !== undefined && this.path.getBasename().endsWith('.rjson'); - - this.pathKeys = []; - this.paths = new Map(); - this.pathToComments = new Map(); - this.consumeDiagnosticCategory = - opts.consumeDiagnosticCategory === undefined - ? 'parse/json' - : opts.consumeDiagnosticCategory; - } - - pathToComments: PathToComments; - hasExtensions: boolean; - pathKeys: ConsumePath; - paths: Map; - options: JSONParserOptions; - consumeDiagnosticCategory: DiagnosticCategory; - - getPathInfo(path: ConsumePath): undefined | PathInfo { - return this.paths.get(path.join('.')); - } - - setComments(pathComments: PathComments) { - const key = this.pathKeys.join('.'); - - const existing = this.pathToComments.get(key); - if (existing === undefined) { - this.pathToComments.set(key, pathComments); - } else { - this.pathToComments.set( - key, - { - inner: [...existing.inner, ...pathComments.inner], - outer: [...existing.outer, ...pathComments.outer], - }, - ); - } - } - - setPath(info: PathInfo) { - this.paths.set(this.pathKeys.join('.'), info); - this.pathKeys.pop(); - } - - tokenize(index: Number0, input: string) { - const nextChar = input[ob1Get0(index) + 1]; - const char = input[ob1Get0(index)]; - - // Line comment - if (char === '/' && nextChar === '/') { - const commentValueIndex = ob1Add(index, 2); - const [value] = this.readInputFrom(commentValueIndex, isntNewline); - // (comment content start + comment content length) - return this.finishValueToken( - 'LineComment', - value, - ob1Add(commentValueIndex, value.length), - ); - } - - // BlockComment - if (char === '/' && nextChar === '*') { - const commentValueIndex = ob1Add(index, 2); - const [value] = this.readInputFrom( - commentValueIndex, - isntBlockCommentEnd, - ); - - // (comment content start + comment content length + 2 characters for comment end) - const endIndex = ob1Add(ob1Add(commentValueIndex, value.length), 2); - - // Ensure the comment is closed - if ( - this.input[ob1Get0(endIndex) - 2] !== '*' || - this.input[ob1Get0(endIndex) - 1] !== '/' - ) { - throw this.unexpected({ - description: descriptions.JSON.UNCLOSED_BLOCK_COMMENT, - start: this.getPositionFromIndex(endIndex), - }); - } - - return this.finishValueToken('BlockComment', value, endIndex); - } - - // Single character token starters - switch (char) { - case '"': { - const [value] = this.readInputFrom(ob1Inc(index), isStringValueChar); - - // Check for closed string (index is the current token index + string length + closing quote + 1 for the end char) - const end = ob1Add(ob1Add(index, value.length), 2); - if (input[ob1Get0(end) - 1] !== '"') { - throw this.unexpected({ - description: descriptions.JSON.UNCLOSED_STRING, - start: this.getPositionFromIndex(end), - }); - } - - // Don't allow newlines in JSON - for (let strIndex = 0; strIndex < value.length; strIndex++) { - const char = value[strIndex]; - - if (char === '\n') { - throw this.unexpected({ - description: descriptions.JSON.STRING_NEWLINES_IN_JSON, - start: this.getPositionFromIndex(ob1Add(index, strIndex)), - }); - } - } - - // Unescape the string - const unescaped = unescapeString( - value, - (metadata, strIndex) => { - throw this.unexpected({ - description: metadata, - start: this.getPositionFromIndex(ob1Add(index, strIndex)), - }); - }, - ); - - return this.finishValueToken('String', unescaped, end); - } - - case "'": - throw this.unexpected({ - description: descriptions.JSON.SINGLE_QUOTE_USAGE, - start: this.getPositionFromIndex(index), - }); - - case '/': - throw this.unexpected({ - description: descriptions.JSON.REGEX_IN_JSON, - start: this.getPositionFromIndex(index), - }); - - case ',': - return this.finishToken('Comma'); - - case '.': - return this.finishToken('Dot'); - - case '-': - return this.finishToken('Minus'); - - case '+': - return this.finishToken('Plus'); - - case ':': - return this.finishToken('Colon'); - - case '{': - return this.finishToken('BraceOpen'); - - case '}': - return this.finishToken('BraceClose'); - - case '[': - return this.finishToken('BracketOpen'); - - case ']': - return this.finishToken('BracketClose'); - } - - // Numbers - if (isDigit(char)) { - const value = this.removeUnderscores( - index, - this.readInputFrom(index, isNumberChar)[0], - ); - const num = Number(value); - return this.finishValueToken('Number', num, ob1Add(index, value.length)); - } - - // Word - boolean, undefined etc - if (isWordStartChar(char)) { - const [value] = this.readInputFrom(index, isWordChar); - return this.finishValueToken('Word', value, ob1Add(index, value.length)); - } - - // Unknown character - return undefined; - } - - parseObject(firstKeyStart?: Position, firstKey?: string): JSONObject { - const obj: JSONObject = {}; - - let innerComments: Comments = []; - let isFirstProp = true; - - // These are comments that the next property should take in case the previous accidently took them - let nextLeadingComments; - - do { - if (this.matchToken('BraceClose')) { - break; - } - - // Eat all the comments that appeared before this property, it's the most common and natural place to put them, - - // and is where we'll print all comments for a property. - let leadingComments = this.eatComments(); - - // Take any leading comments that were left by the previous property - if (nextLeadingComments !== undefined) { - leadingComments = [...nextLeadingComments, ...leadingComments]; - nextLeadingComments = undefined; - } - - // Throw a meainingful error for redundant commas - if (this.matchToken('Comma')) { - throw this.unexpected({ - description: descriptions.JSON.REDUNDANT_COMMA, - }); - } - - // If there's no property key indicator then delegate any comments we have to object - const hasKey = isFirstProp && firstKey !== undefined; - if (!hasKey && !this.matchToken('String') && !this.matchToken('Word')) { - innerComments = [...innerComments, ...leadingComments]; - break; - } - - const keyStart = - isFirstProp && firstKeyStart !== undefined - ? firstKeyStart - : this.getPosition(); - - // Parse the property key - let key; - if (isFirstProp && firstKey !== undefined) { - // If this is the first property and we've been given a property key then use it instead - key = firstKey; - } else { - key = this.parsePropertyKey(); - } - isFirstProp = false; - - const keyEnd = this.getPosition(); - this.expectToken('Colon'); - - // Having comments before the value is a really weird place to put them, but we'll handle it - - // anyway to avoid throwing a parser error. When stringified, the comments will all be before - - // the property. - const leadingValueComments = this.eatComments(); - - this.pathKeys.push(key); - - // Parse the value. - const valueStart = this.getPosition(); - const value = this.parseExpression(); - const valueEnd = this.getLastEndPosition(); - - // Eat the comments after the expression and associate the comments with them - let trailingValueComments = this.eatComments(); - - // If the next token isn't a comma or closing brace then we've just stolen - - // the leading comments of the next property - if (!this.matchToken('Comma') && !this.matchToken('BraceClose')) { - nextLeadingComments = trailingValueComments; - trailingValueComments = []; - } - - this.setComments({ - inner: [], - outer: [ - ...leadingComments, - ...leadingValueComments, - ...trailingValueComments, - ], - }); - - this.setPath({ - keyStart, - keyEnd, - valueStart, - valueEnd, - originalValue: value, - }); - - // Set the object correctly, accounting for JS weirdness - if (key === '__proto__') { - // Need to use defineProperty to avoid triggering the Object.prototype.__proto__ setter - Object.defineProperty( - obj, - '__proto__', - { - value, - configurable: true, - writable: true, - enumerable: true, - }, - ); - } else { - obj[key] = value; - } - } while (this.eatPropertySeparator()); - - // Take any loose leading comments - if (nextLeadingComments !== undefined) { - innerComments = [...innerComments, ...nextLeadingComments]; - } - - // If we were passed a first key then this was an implicit object so there's no end token - if (firstKey === undefined) { - this.expectToken('BraceClose'); - } - - this.setComments({ - inner: innerComments, - outer: [], - }); - - return obj; - } - - // Remove underscores from 'a string, this is used for numeric separators eg. 100_000 - removeUnderscores(index: Number0, raw: string): string { - let str = ''; - - for (let i = 0; i < raw.length; i++) { - const char = raw[i]; - - if (char === '_') { - // Don't allow separators in JSON - if (!this.hasExtensions) { - throw this.unexpected({ - description: descriptions.JSON.NUMERIC_SEPARATORS_IN_JSON, - start: this.getPositionFromIndex(ob1Inc(index)), - }); - } - } else { - str += char; - } - } - - return str; - } - - eatComments(): Comments { - const comments: Comments = []; - - while (true) { - const token = this.getToken(); - - if (token.type === 'LineComment') { - comments.push({ - type: 'LineComment', - value: token.value, - }); - } else if (token.type === 'BlockComment') { - comments.push({ - type: 'BlockComment', - value: token.value, - }); - } else { - break; - } - - // Comments aren't allowed in regular JSON - if (!this.hasExtensions) { - throw this.unexpected({ - description: descriptions.JSON.COMMENTS_IN_JSON, - }); - } - - this.nextToken(); - } - - return comments; - } - - parseArray(): Array { - this.expectToken('BracketOpen'); - - const arr = []; - let innerComments: Comments = []; - let i = 0; - - do { - if (this.matchToken('BracketClose')) { - break; - } - - // Eat all the comments before an element - const leadingComments = this.eatComments(); - - if (this.matchToken('Comma')) { - throw this.unexpected({ - description: descriptions.JSON.REDUNDANT_COMMA, - }); - } - - // If we're at the end of the array then associate these comments with the array - if (this.matchToken('BracketClose')) { - innerComments = [...innerComments, ...leadingComments]; - break; - } - - const start = this.getPosition(); - this.pathKeys.push(i); - i++; - - // Parse the value - const item = this.parseExpression(); - arr.push(item); - const end = this.getLastEndPosition(); - - // Trailing comments are really weird, but let's handle them just like object properties - const trailingComments = this.eatComments(); - - this.setComments({ - outer: [...leadingComments, ...trailingComments], - inner: [], - }); - - this.setPath({ - originalValue: item, - keyStart: start, - keyEnd: end, - valueStart: start, - valueEnd: end, - }); - - // Have a meaningful error message when an object is incorrectly using brackets: ["foo": "bar"] - if (this.matchToken('Colon')) { - throw this.unexpected({ - description: descriptions.JSON.MISTAKEN_ARRAY_IDENTITY, - }); - } - } while (this.eatPropertySeparator()); - - this.expectToken('BracketClose'); - - this.setComments({ - inner: innerComments, - outer: [], - }); - - return arr; - } - - // Check if the current token is a property separator and eat it if necessary - eatPropertySeparator(): boolean { - const token = this.getToken(); - - // Implicit commas are only allowed in rjson - if (this.hasExtensions) { - // Eat the token, don't care if we're in RJSON - if (token.type === 'Comma') { - this.nextToken(); - } - - // An object or array close is an instant failure - - // Doesn't matter what we're parsing since the subsequent tokens will be validated - if (token.type === 'BraceClose' || token.type === 'BracketClose') { - return false; - } - - return true; - } else { - if (token.type !== 'Comma') { - return false; - } - - // Make sure this isn't a trailing comma - const lookahead = this.lookaheadToken(); - if (lookahead.type === 'BraceClose' || lookahead.type === 'BracketClose') { - throw this.unexpected({ - description: descriptions.JSON.TRAILING_COMMA_IN_JSON, - }); - } - - this.nextToken(); - return true; - } - } - - parseWord(isStart: boolean): JSONValue { - const start = this.getPosition(); - const token = this.expectToken('Word'); - - switch (token.value) { - case 'true': - return true; - - case 'false': - return false; - - case 'null': - return null; - - case 'undefined': - throw this.unexpected({ - description: descriptions.JSON.UNDEFINED_IN_JSON, - }); - } - - if (isStart && this.matchToken('Colon')) { - if (this.hasExtensions) { - return this.parseObject(start, token.value); - } else { - throw this.unexpected({ - description: descriptions.JSON.IMPLICIT_OBJECT_IN_JSON, - }); - } - } - - throw this.unexpected({ - description: descriptions.JSON.UNKNOWN_WORD_IN_JSON(token.value), - }); - } - - parseNumber(): number { - const isNegative = this.eatToken('Minus') !== undefined; - - // Get a string of the current number that we'll parse later - const token = this.expectToken('Number'); - let value: string = String(token.value); - - // Decimals - if (this.eatToken('Dot')) { - value += '.'; - - const decimal = this.expectToken('Number'); - value += String(decimal.value); - } - - // Scientific notation - const nextToken = this.getToken(); - if ( - nextToken.type === 'Word' && - (nextToken.value === 'e' || nextToken.value === 'E') - ) { - value += 'e'; - - // Operator - const operator = this.nextToken(); - if (operator.type === 'Minus') { - value += '-'; - } else if (operator.type === 'Plus') { - value += '+'; - } else { - throw this.unexpected(); - } - - // Factor - this.nextToken(); - const factor = this.expectToken('Number'); - value += String(factor.value); - } - - // BigInt - const nextToken2 = this.getToken(); - if (nextToken2.type === 'Word' && nextToken2.value === 'n') { - throw this.unexpected({ - description: descriptions.JSON.BIGINT_IN_JSON, - }); - } - - // Turn the string into an actual number - let num = Number(value); - if (isNegative) { - num = -num; - } - return num; - } - - parsePropertyKey() { - const token = this.getToken(); - - switch (token.type) { - case 'String': { - this.nextToken(); - return token.value; - } - - case 'Word': - if (this.hasExtensions) { - this.nextToken(); - return token.value; - } else { - throw this.unexpected({ - description: descriptions.JSON.PROPERTY_KEY_UNQUOTED_IN_JSON, - }); - } - - default: - throw this.unexpected(); - } - } - - parseString(isStart: boolean): string | JSONObject { - const start = this.getPosition(); - const token = this.expectToken('String'); - - if (isStart && this.nextToken().type === 'Colon') { - if (this.hasExtensions) { - return this.parseObject(start, token.value); - } else { - throw this.unexpected({ - description: descriptions.JSON.IMPLICIT_OBJECT_IN_JSON, - }); - } - } else { - return token.value; - } - } - - parseExpression(isStart: boolean = false): JSONValue { - const token = this.getToken(); - - switch (token.type) { - case 'String': - return this.parseString(isStart); - - case 'Minus': - case 'Number': - return this.parseNumber(); - - case 'Word': - return this.parseWord(isStart); - - case 'BracketOpen': - return this.parseArray(); - - case 'BraceOpen': { - this.nextToken(); - return this.parseObject(); - } - - default: - throw this.unexpected(); - } - } - - parseEntry(): JSONValue { - if (this.matchToken('EOF')) { - if (this.hasExtensions) { - // If we're in RJSON mode then an empty input is an implicit object - return {}; - } else { - throw this.unexpected({ - description: descriptions.JSON.EMPTY_INPUT_IN_JSON, - }); - } - } else { - return this.parseExpression(true); - } - } - - parse(): JSONParserResult { - let expectSyntaxError = false; - - if (!this.hasExtensions) { - // If we're in regular JSON, try the native JSON.parse - try { - const value = JSON.parse(this.input); - - // Lazy parse when we need location information - let context: undefined | Required; - const getContext = (): Required => { - if (context === undefined) { - const res = this._parse(); - context = res.context; - return res.context; - } else { - return context; - } - }; - - return { - context: { - category: this.consumeDiagnosticCategory, - normalizeKey(path) { - return getContext().normalizeKey(path); - }, - getOriginalValue(path) { - return getContext().getOriginalValue(path); - }, - getDiagnosticPointer(keys, target) { - return getContext().getDiagnosticPointer(keys, target); - }, - }, - value, - }; - } catch (err) { - // On syntax errors we'll fall back to our parser which is slower, but produces more meaningful errors - if (err instanceof SyntaxError) { - expectSyntaxError = true; - } else { - throw err; - } - } - } - - const res: JSONParserResult = this._parse(); - - if (expectSyntaxError) { - throw new Error( - "JSON.parse failed but our custom JSON parser was successful... That doesn't smell right", - ); - } - - return res; - } - - _parse(): JSONParserResult { - const leadingComments = this.eatComments(); - - const expr = this.parseEntry(); - - const trailingComments = this.eatComments(); - this.setComments({ - inner: [], - outer: [...leadingComments, ...trailingComments], - }); - - this.finalize(); - - const context: Required = { - category: this.consumeDiagnosticCategory, - normalizeKey: (key) => key, - getDiagnosticPointer: ( - keys: ConsumePath, - target: ConsumeSourceLocationRequestTarget, - ): DiagnosticLocation => { - const info = this.getPathInfo(keys); - if (info === undefined) { - return { - filename: this.filename, - }; - } - - let start = info.keyStart; - let end = info.valueEnd; - - if (target === 'key') { - end = info.keyEnd; - } - - if (target === 'value' || target === 'inner-value') { - start = info.valueStart; - } - - let loc: SourceLocation = { - filename: this.filename, - start, - end, - }; - - if (target === 'inner-value') { - const originalValue = context.getOriginalValue(keys); - - // Remove quote marks for strings - if (typeof originalValue === 'string') { - loc = { - ...loc, - start: { - ...loc.start, - column: ob1Add(loc.start.column, 1), - }, - end: { - ...loc.end, - column: ob1Sub(loc.end.column, 1), - }, - }; - } - } - - return { - language: 'json', - ...loc, - mtime: this.mtime, - sourceText: undefined, - }; - }, - getOriginalValue: (keys: ConsumePath) => { - const info = this.getPathInfo(keys); - if (info !== undefined) { - return info.originalValue; - } - }, - }; - - return { - value: expr, - context, - }; - } - } + class JSONParser extends ParserCore { + constructor(opts: JSONParserOptions) { + super( + { + ...opts, + retainCarriageReturn: true, + }, + 'parse/json', + ); + + this.options = opts; + this.ignoreWhitespaceTokens = true; + + this.hasExtensions = + this.path !== undefined && this.path.getBasename().endsWith('.rjson'); + + this.pathKeys = []; + this.paths = new Map(); + this.pathToComments = new Map(); + this.consumeDiagnosticCategory = + opts.consumeDiagnosticCategory === undefined + ? 'parse/json' + : opts.consumeDiagnosticCategory; + } + + pathToComments: PathToComments; + hasExtensions: boolean; + pathKeys: ConsumePath; + paths: Map; + options: JSONParserOptions; + consumeDiagnosticCategory: DiagnosticCategory; + + getPathInfo(path: ConsumePath): undefined | PathInfo { + return this.paths.get(path.join('.')); + } + + setComments(pathComments: PathComments) { + const key = this.pathKeys.join('.'); + + const existing = this.pathToComments.get(key); + if (existing === undefined) { + this.pathToComments.set(key, pathComments); + } else { + this.pathToComments.set( + key, + { + inner: [...existing.inner, ...pathComments.inner], + outer: [...existing.outer, ...pathComments.outer], + }, + ); + } + } + + setPath(info: PathInfo) { + this.paths.set(this.pathKeys.join('.'), info); + this.pathKeys.pop(); + } + + tokenize(index: Number0, input: string) { + const nextChar = input[ob1Get0(index) + 1]; + const char = input[ob1Get0(index)]; + + // Line comment + if (char === '/' && nextChar === '/') { + const commentValueIndex = ob1Add(index, 2); + const [value] = this.readInputFrom(commentValueIndex, isntNewline); + // (comment content start + comment content length) + return this.finishValueToken( + 'LineComment', + value, + ob1Add(commentValueIndex, value.length), + ); + } + + // BlockComment + if (char === '/' && nextChar === '*') { + const commentValueIndex = ob1Add(index, 2); + const [value] = this.readInputFrom(commentValueIndex, isntBlockCommentEnd); + + // (comment content start + comment content length + 2 characters for comment end) + const endIndex = ob1Add(ob1Add(commentValueIndex, value.length), 2); + + // Ensure the comment is closed + if ( + this.input[ob1Get0(endIndex) - 2] !== '*' || + this.input[ob1Get0(endIndex) - 1] !== '/' + ) { + throw this.unexpected({ + description: descriptions.JSON.UNCLOSED_BLOCK_COMMENT, + start: this.getPositionFromIndex(endIndex), + }); + } + + return this.finishValueToken('BlockComment', value, endIndex); + } + + // Single character token starters + switch (char) { + case '"': { + const [value] = this.readInputFrom(ob1Inc(index), isStringValueChar); + + // Check for closed string (index is the current token index + string length + closing quote + 1 for the end char) + const end = ob1Add(ob1Add(index, value.length), 2); + if (input[ob1Get0(end) - 1] !== '"') { + throw this.unexpected({ + description: descriptions.JSON.UNCLOSED_STRING, + start: this.getPositionFromIndex(end), + }); + } + + // Don't allow newlines in JSON + for (let strIndex = 0; strIndex < value.length; strIndex++) { + const char = value[strIndex]; + + if (char === '\n') { + throw this.unexpected({ + description: descriptions.JSON.STRING_NEWLINES_IN_JSON, + start: this.getPositionFromIndex(ob1Add(index, strIndex)), + }); + } + } + + // Unescape the string + const unescaped = unescapeString( + value, + (metadata, strIndex) => { + throw this.unexpected({ + description: metadata, + start: this.getPositionFromIndex(ob1Add(index, strIndex)), + }); + }, + ); + + return this.finishValueToken('String', unescaped, end); + } + + case "'": + throw this.unexpected({ + description: descriptions.JSON.SINGLE_QUOTE_USAGE, + start: this.getPositionFromIndex(index), + }); + + case '/': + throw this.unexpected({ + description: descriptions.JSON.REGEX_IN_JSON, + start: this.getPositionFromIndex(index), + }); + + case ',': + return this.finishToken('Comma'); + + case '.': + return this.finishToken('Dot'); + + case '-': + return this.finishToken('Minus'); + + case '+': + return this.finishToken('Plus'); + + case ':': + return this.finishToken('Colon'); + + case '{': + return this.finishToken('BraceOpen'); + + case '}': + return this.finishToken('BraceClose'); + + case '[': + return this.finishToken('BracketOpen'); + + case ']': + return this.finishToken('BracketClose'); + } + + // Numbers + if (isDigit(char)) { + const value = this.removeUnderscores( + index, + this.readInputFrom(index, isNumberChar)[0], + ); + const num = Number(value); + return this.finishValueToken('Number', num, ob1Add(index, value.length)); + } + + // Word - boolean, undefined etc + if (isWordStartChar(char)) { + const [value] = this.readInputFrom(index, isWordChar); + return this.finishValueToken('Word', value, ob1Add(index, value.length)); + } + + // Unknown character + return undefined; + } + + parseObject(firstKeyStart?: Position, firstKey?: string): JSONObject { + const obj: JSONObject = {}; + + let innerComments: Comments = []; + let isFirstProp = true; + + // These are comments that the next property should take in case the previous accidently took them + let nextLeadingComments; + + do { + if (this.matchToken('BraceClose')) { + break; + } + + // Eat all the comments that appeared before this property, it's the most common and natural place to put them, + + // and is where we'll print all comments for a property. + let leadingComments = this.eatComments(); + + // Take any leading comments that were left by the previous property + if (nextLeadingComments !== undefined) { + leadingComments = [...nextLeadingComments, ...leadingComments]; + nextLeadingComments = undefined; + } + + // Throw a meainingful error for redundant commas + if (this.matchToken('Comma')) { + throw this.unexpected({ + description: descriptions.JSON.REDUNDANT_COMMA, + }); + } + + // If there's no property key indicator then delegate any comments we have to object + const hasKey = isFirstProp && firstKey !== undefined; + if (!hasKey && !this.matchToken('String') && !this.matchToken('Word')) { + innerComments = [...innerComments, ...leadingComments]; + break; + } + + const keyStart = + isFirstProp && firstKeyStart !== undefined + ? firstKeyStart + : this.getPosition(); + + // Parse the property key + let key; + if (isFirstProp && firstKey !== undefined) { + // If this is the first property and we've been given a property key then use it instead + key = firstKey; + } else { + key = this.parsePropertyKey(); + } + isFirstProp = false; + + const keyEnd = this.getPosition(); + this.expectToken('Colon'); + + // Having comments before the value is a really weird place to put them, but we'll handle it + + // anyway to avoid throwing a parser error. When stringified, the comments will all be before + + // the property. + const leadingValueComments = this.eatComments(); + + this.pathKeys.push(key); + + // Parse the value. + const valueStart = this.getPosition(); + const value = this.parseExpression(); + const valueEnd = this.getLastEndPosition(); + + // Eat the comments after the expression and associate the comments with them + let trailingValueComments = this.eatComments(); + + // If the next token isn't a comma or closing brace then we've just stolen + + // the leading comments of the next property + if (!this.matchToken('Comma') && !this.matchToken('BraceClose')) { + nextLeadingComments = trailingValueComments; + trailingValueComments = []; + } + + this.setComments({ + inner: [], + outer: [ + ...leadingComments, + ...leadingValueComments, + ...trailingValueComments, + ], + }); + + this.setPath({ + keyStart, + keyEnd, + valueStart, + valueEnd, + originalValue: value, + }); + + // Set the object correctly, accounting for JS weirdness + if (key === '__proto__') { + // Need to use defineProperty to avoid triggering the Object.prototype.__proto__ setter + Object.defineProperty( + obj, + '__proto__', + { + value, + configurable: true, + writable: true, + enumerable: true, + }, + ); + } else { + obj[key] = value; + } + } while (this.eatPropertySeparator()); + + // Take any loose leading comments + if (nextLeadingComments !== undefined) { + innerComments = [...innerComments, ...nextLeadingComments]; + } + + // If we were passed a first key then this was an implicit object so there's no end token + if (firstKey === undefined) { + this.expectToken('BraceClose'); + } + + this.setComments({ + inner: innerComments, + outer: [], + }); + + return obj; + } + + // Remove underscores from 'a string, this is used for numeric separators eg. 100_000 + removeUnderscores(index: Number0, raw: string): string { + let str = ''; + + for (let i = 0; i < raw.length; i++) { + const char = raw[i]; + + if (char === '_') { + // Don't allow separators in JSON + if (!this.hasExtensions) { + throw this.unexpected({ + description: descriptions.JSON.NUMERIC_SEPARATORS_IN_JSON, + start: this.getPositionFromIndex(ob1Inc(index)), + }); + } + } else { + str += char; + } + } + + return str; + } + + eatComments(): Comments { + const comments: Comments = []; + + while (true) { + const token = this.getToken(); + + if (token.type === 'LineComment') { + comments.push({ + type: 'LineComment', + value: token.value, + }); + } else if (token.type === 'BlockComment') { + comments.push({ + type: 'BlockComment', + value: token.value, + }); + } else { + break; + } + + // Comments aren't allowed in regular JSON + if (!this.hasExtensions) { + throw this.unexpected({ + description: descriptions.JSON.COMMENTS_IN_JSON, + }); + } + + this.nextToken(); + } + + return comments; + } + + parseArray(): Array { + this.expectToken('BracketOpen'); + + const arr = []; + let innerComments: Comments = []; + let i = 0; + + do { + if (this.matchToken('BracketClose')) { + break; + } + + // Eat all the comments before an element + const leadingComments = this.eatComments(); + + if (this.matchToken('Comma')) { + throw this.unexpected({ + description: descriptions.JSON.REDUNDANT_COMMA, + }); + } + + // If we're at the end of the array then associate these comments with the array + if (this.matchToken('BracketClose')) { + innerComments = [...innerComments, ...leadingComments]; + break; + } + + const start = this.getPosition(); + this.pathKeys.push(i); + i++; + + // Parse the value + const item = this.parseExpression(); + arr.push(item); + const end = this.getLastEndPosition(); + + // Trailing comments are really weird, but let's handle them just like object properties + const trailingComments = this.eatComments(); + + this.setComments({ + outer: [...leadingComments, ...trailingComments], + inner: [], + }); + + this.setPath({ + originalValue: item, + keyStart: start, + keyEnd: end, + valueStart: start, + valueEnd: end, + }); + + // Have a meaningful error message when an object is incorrectly using brackets: ["foo": "bar"] + if (this.matchToken('Colon')) { + throw this.unexpected({ + description: descriptions.JSON.MISTAKEN_ARRAY_IDENTITY, + }); + } + } while (this.eatPropertySeparator()); + + this.expectToken('BracketClose'); + + this.setComments({ + inner: innerComments, + outer: [], + }); + + return arr; + } + + // Check if the current token is a property separator and eat it if necessary + eatPropertySeparator(): boolean { + const token = this.getToken(); + + // Implicit commas are only allowed in rjson + if (this.hasExtensions) { + // Eat the token, don't care if we're in RJSON + if (token.type === 'Comma') { + this.nextToken(); + } + + // An object or array close is an instant failure + + // Doesn't matter what we're parsing since the subsequent tokens will be validated + if (token.type === 'BraceClose' || token.type === 'BracketClose') { + return false; + } + + return true; + } else { + if (token.type !== 'Comma') { + return false; + } + + // Make sure this isn't a trailing comma + const lookahead = this.lookaheadToken(); + if (lookahead.type === 'BraceClose' || lookahead.type === 'BracketClose') { + throw this.unexpected({ + description: descriptions.JSON.TRAILING_COMMA_IN_JSON, + }); + } + + this.nextToken(); + return true; + } + } + + parseWord(isStart: boolean): JSONValue { + const start = this.getPosition(); + const token = this.expectToken('Word'); + + switch (token.value) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + case 'undefined': + throw this.unexpected({ + description: descriptions.JSON.UNDEFINED_IN_JSON, + }); + } + + if (isStart && this.matchToken('Colon')) { + if (this.hasExtensions) { + return this.parseObject(start, token.value); + } else { + throw this.unexpected({ + description: descriptions.JSON.IMPLICIT_OBJECT_IN_JSON, + }); + } + } + + throw this.unexpected({ + description: descriptions.JSON.UNKNOWN_WORD_IN_JSON(token.value), + }); + } + + parseNumber(): number { + const isNegative = this.eatToken('Minus') !== undefined; + + // Get a string of the current number that we'll parse later + const token = this.expectToken('Number'); + let value: string = String(token.value); + + // Decimals + if (this.eatToken('Dot')) { + value += '.'; + + const decimal = this.expectToken('Number'); + value += String(decimal.value); + } + + // Scientific notation + const nextToken = this.getToken(); + if ( + nextToken.type === 'Word' && + (nextToken.value === 'e' || nextToken.value === 'E') + ) { + value += 'e'; + + // Operator + const operator = this.nextToken(); + if (operator.type === 'Minus') { + value += '-'; + } else if (operator.type === 'Plus') { + value += '+'; + } else { + throw this.unexpected(); + } + + // Factor + this.nextToken(); + const factor = this.expectToken('Number'); + value += String(factor.value); + } + + // BigInt + const nextToken2 = this.getToken(); + if (nextToken2.type === 'Word' && nextToken2.value === 'n') { + throw this.unexpected({ + description: descriptions.JSON.BIGINT_IN_JSON, + }); + } + + // Turn the string into an actual number + let num = Number(value); + if (isNegative) { + num = -num; + } + return num; + } + + parsePropertyKey() { + const token = this.getToken(); + + switch (token.type) { + case 'String': { + this.nextToken(); + return token.value; + } + + case 'Word': + if (this.hasExtensions) { + this.nextToken(); + return token.value; + } else { + throw this.unexpected({ + description: descriptions.JSON.PROPERTY_KEY_UNQUOTED_IN_JSON, + }); + } + + default: + throw this.unexpected(); + } + } + + parseString(isStart: boolean): string | JSONObject { + const start = this.getPosition(); + const token = this.expectToken('String'); + + if (isStart && this.nextToken().type === 'Colon') { + if (this.hasExtensions) { + return this.parseObject(start, token.value); + } else { + throw this.unexpected({ + description: descriptions.JSON.IMPLICIT_OBJECT_IN_JSON, + }); + } + } else { + return token.value; + } + } + + parseExpression(isStart: boolean = false): JSONValue { + const token = this.getToken(); + + switch (token.type) { + case 'String': + return this.parseString(isStart); + + case 'Minus': + case 'Number': + return this.parseNumber(); + + case 'Word': + return this.parseWord(isStart); + + case 'BracketOpen': + return this.parseArray(); + + case 'BraceOpen': { + this.nextToken(); + return this.parseObject(); + } + + default: + throw this.unexpected(); + } + } + + parseEntry(): JSONValue { + if (this.matchToken('EOF')) { + if (this.hasExtensions) { + // If we're in RJSON mode then an empty input is an implicit object + return {}; + } else { + throw this.unexpected({ + description: descriptions.JSON.EMPTY_INPUT_IN_JSON, + }); + } + } else { + return this.parseExpression(true); + } + } + + parse(): JSONParserResult { + let expectSyntaxError = false; + + if (!this.hasExtensions) { + // If we're in regular JSON, try the native JSON.parse + try { + const value = JSON.parse(this.input); + + // Lazy parse when we need location information + let context: undefined | Required; + const getContext = (): Required => { + if (context === undefined) { + const res = this._parse(); + context = res.context; + return res.context; + } else { + return context; + } + }; + + return { + context: { + category: this.consumeDiagnosticCategory, + normalizeKey(path) { + return getContext().normalizeKey(path); + }, + getOriginalValue(path) { + return getContext().getOriginalValue(path); + }, + getDiagnosticPointer(keys, target) { + return getContext().getDiagnosticPointer(keys, target); + }, + }, + value, + }; + } catch (err) { + // On syntax errors we'll fall back to our parser which is slower, but produces more meaningful errors + if (err instanceof SyntaxError) { + expectSyntaxError = true; + } else { + throw err; + } + } + } + + const res: JSONParserResult = this._parse(); + + if (expectSyntaxError) { + throw new Error( + "JSON.parse failed but our custom JSON parser was successful... That doesn't smell right", + ); + } + + return res; + } + + _parse(): JSONParserResult { + const leadingComments = this.eatComments(); + + const expr = this.parseEntry(); + + const trailingComments = this.eatComments(); + this.setComments({ + inner: [], + outer: [...leadingComments, ...trailingComments], + }); + + this.finalize(); + + const context: Required = { + category: this.consumeDiagnosticCategory, + normalizeKey: (key) => key, + getDiagnosticPointer: ( + keys: ConsumePath, + target: ConsumeSourceLocationRequestTarget, + ): DiagnosticLocation => { + const info = this.getPathInfo(keys); + if (info === undefined) { + return { + filename: this.filename, + }; + } + + let start = info.keyStart; + let end = info.valueEnd; + + if (target === 'key') { + end = info.keyEnd; + } + + if (target === 'value' || target === 'inner-value') { + start = info.valueStart; + } + + let loc: SourceLocation = { + filename: this.filename, + start, + end, + }; + + if (target === 'inner-value') { + const originalValue = context.getOriginalValue(keys); + + // Remove quote marks for strings + if (typeof originalValue === 'string') { + loc = { + ...loc, + start: { + ...loc.start, + column: ob1Add(loc.start.column, 1), + }, + end: { + ...loc.end, + column: ob1Sub(loc.end.column, 1), + }, + }; + } + } + + return { + language: 'json', + ...loc, + mtime: this.mtime, + sourceText: undefined, + }; + }, + getOriginalValue: (keys: ConsumePath) => { + const info = this.getPathInfo(keys); + if (info !== undefined) { + return info.originalValue; + } + }, + }; + + return { + value: expr, + context, + }; + } + } ); diff --git a/packages/@romejs/codec-json/parser-test262.test.ts b/packages/@romejs/codec-json/parser-test262.test.ts index 68216c4edd8..de8debec4e0 100644 --- a/packages/@romejs/codec-json/parser-test262.test.ts +++ b/packages/@romejs/codec-json/parser-test262.test.ts @@ -45,943 +45,943 @@ import {parseJSON} from '@romejs/codec-json'; import {test} from 'rome'; function parse(input: string) { - return parseJSON({input}); + return parseJSON({input}); } // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'The JSON lexical grammar treats whitespace as a token seperator', - (t) => { - t.throws(function() { - parse('12\t\r\n 34'); // should produce a syntax error as whitespace results in two tokens - }); - }, + 'The JSON lexical grammar treats whitespace as a token seperator', + (t) => { + t.throws(function() { + parse('12\t\r\n 34'); // should produce a syntax error as whitespace results in two tokens + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - ' is not valid JSON whitespace as specified by the production JSONWhitespace.', - (t) => { - t.throws(function() { - parse('\x0b1234'); // should produce a syntax error - }); - }, + ' is not valid JSON whitespace as specified by the production JSONWhitespace.', + (t) => { + t.throws(function() { + parse('\x0b1234'); // should produce a syntax error + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - ' is not valid JSON whitespace as specified by the production JSONWhitespace.', - (t) => { - t.throws(function() { - parse('\f1234'); // should produce a syntax error - }); - }, + ' is not valid JSON whitespace as specified by the production JSONWhitespace.', + (t) => { + t.throws(function() { + parse('\f1234'); // should produce a syntax error + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - ' is not valid JSON whitespace as specified by the production JSONWhitespace.', - (t) => { - t.throws(function() { - parse('\xa01234'); // should produce a syntax error - }); - }, + ' is not valid JSON whitespace as specified by the production JSONWhitespace.', + (t) => { + t.throws(function() { + parse('\xa01234'); // should produce a syntax error + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - ' is not valid JSON whitespace as specified by the production JSONWhitespace.', - (t) => { - t.throws(function() { - parse('\u200b1234'); // should produce a syntax error - }); - }, + ' is not valid JSON whitespace as specified by the production JSONWhitespace.', + (t) => { + t.throws(function() { + parse('\u200b1234'); // should produce a syntax error + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - ' is not valid JSON whitespace as specified by the production JSONWhitespace.', - (t) => { - t.throws(function() { - parse('\ufeff1234'); // should produce a syntax error a - }); - }, + ' is not valid JSON whitespace as specified by the production JSONWhitespace.', + (t) => { + t.throws(function() { + parse('\ufeff1234'); // should produce a syntax error a + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'U+2028 and U+2029 are not valid JSON whitespace as specified by the production JSONWhitespace.', - (t) => { - t.throws(function() { - parse('\u2028\u20291234'); // should produce a syntax error - }); - }, + 'U+2028 and U+2029 are not valid JSON whitespace as specified by the production JSONWhitespace.', + (t) => { + t.throws(function() { + parse('\u2028\u20291234'); // should produce a syntax error + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'Whitespace characters can appear before/after any JSONtoken', - () => { - parse( - `\t\r \n{\t\r \n"property"\t\r \n:\t\r \n{\t\r \n}\t\r \n,\t\r \n"prop2"\t\r \n:\t\r \n` + - `[\t\r \ntrue\t\r \n,\t\r \nnull\t\r \n,123.456\t\r \n]\t\r \n}\t\r \n`, - ); // should JOSN parse without error - }, + 'Whitespace characters can appear before/after any JSONtoken', + () => { + parse( + `\t\r \n{\t\r \n"property"\t\r \n:\t\r \n{\t\r \n}\t\r \n,\t\r \n"prop2"\t\r \n:\t\r \n` + + `[\t\r \ntrue\t\r \n,\t\r \nnull\t\r \n,123.456\t\r \n]\t\r \n}\t\r \n`, + ); // should JOSN parse without error + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'The JSON lexical grammar treats as a whitespace character', - (t) => { - t.is(parse('\t1234'), 1_234, ' should be ignored'); + 'The JSON lexical grammar treats as a whitespace character', + (t) => { + t.is(parse('\t1234'), 1_234, ' should be ignored'); - t.throws( - function() { - parse('12\t34'); - }, - DiagnosticsError, - ' should produce a syntax error as whitespace results in two tokens', - ); - }, + t.throws( + function() { + parse('12\t34'); + }, + DiagnosticsError, + ' should produce a syntax error as whitespace results in two tokens', + ); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'The JSON lexical grammar treats as a whitespace character', - (t) => { - t.is(parse('\r1234'), 1_234, ' should be ignored'); + 'The JSON lexical grammar treats as a whitespace character', + (t) => { + t.is(parse('\r1234'), 1_234, ' should be ignored'); - t.throws( - function() { - parse('12\r34'); - }, - DiagnosticsError, - ' should produce a syntax error as whitespace results in two tokens', - ); - }, + t.throws( + function() { + parse('12\r34'); + }, + DiagnosticsError, + ' should produce a syntax error as whitespace results in two tokens', + ); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'The JSON lexical grammar treats as a whitespace character', - (t) => { - t.is(parse('\n1234'), 1_234, ' should be ignored'); + 'The JSON lexical grammar treats as a whitespace character', + (t) => { + t.is(parse('\n1234'), 1_234, ' should be ignored'); - t.throws( - function() { - parse('12\n34'); - }, - DiagnosticsError, - ' should produce a syntax error as whitespace results in two tokens', - ); - }, + t.throws( + function() { + parse('12\n34'); + }, + DiagnosticsError, + ' should produce a syntax error as whitespace results in two tokens', + ); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'The JSON lexical grammar treats as a whitespace character', - (t) => { - t.is(parse(' 1234'), 1_234, ' should be ignored'); - t.throws( - function() { - parse('12 34'); - }, - DiagnosticsError, - ' should produce a syntax error as whitespace results in two tokens', - ); - }, + 'The JSON lexical grammar treats as a whitespace character', + (t) => { + t.is(parse(' 1234'), 1_234, ' should be ignored'); + t.throws( + function() { + parse('12 34'); + }, + DiagnosticsError, + ' should produce a syntax error as whitespace results in two tokens', + ); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'JSONStrings can be written using double quotes', - (t) => { - t.is(parse('"abc"'), 'abc', "parse('\"abc\"'})"); - }, + 'JSONStrings can be written using double quotes', + (t) => { + t.is(parse('"abc"'), 'abc', "parse('\"abc\"'})"); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'A JSONString may not be delimited by single quotes', - (t) => { - t.throws(function() { - parse("'abc'"); - }); - }, + 'A JSONString may not be delimited by single quotes', + (t) => { + t.throws(function() { + parse("'abc'"); + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'A JSONString may not be delimited by Uncode escaped quotes', - (t) => { - t.throws(function() { - parse('\\u0022abc\\u0022'); - }); - }, + 'A JSONString may not be delimited by Uncode escaped quotes', + (t) => { + t.throws(function() { + parse('\\u0022abc\\u0022'); + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'A JSONStrings can contain no JSONStringCharacters (Empty JSONStrings)', - (t) => { - t.is(parse('""'), '', "parse('\"\"'})"); - }, + 'A JSONStrings can contain no JSONStringCharacters (Empty JSONStrings)', + (t) => { + t.is(parse('""'), '', "parse('\"\"'})"); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'The JSON lexical grammar does not allow a JSONStringCharacter to be any of the Unicode characters U+0000 thru U+0007', - (t) => { - t.throws(function() { - parse('"\0\x01\x02\x03\x04\x05\x06\x07"'); // invalid string characters should produce a syntax error - }); - }, + 'The JSON lexical grammar does not allow a JSONStringCharacter to be any of the Unicode characters U+0000 thru U+0007', + (t) => { + t.throws(function() { + parse('"\0\x01\x02\x03\x04\x05\x06\x07"'); // invalid string characters should produce a syntax error + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'The JSON lexical grammar does not allow a JSONStringCharacter to be any of the Unicode characters U+0008 thru U+000F', - (t) => { - t.throws(function() { - parse('"\b\t\n\x0b\f\r\x0e\x0f"'); // invalid string characters should produce a syntax error - }); - }, + 'The JSON lexical grammar does not allow a JSONStringCharacter to be any of the Unicode characters U+0008 thru U+000F', + (t) => { + t.throws(function() { + parse('"\b\t\n\x0b\f\r\x0e\x0f"'); // invalid string characters should produce a syntax error + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'The JSON lexical grammar does not allow a JSONStringCharacter to be any of the Unicode characters U+0010 thru U+0017', - (t) => { - t.throws(function() { - parse('"\x10\x11\x12\x13\x14\x15\x16\x17"'); // invalid string characters should produce a syntax error - }); - }, + 'The JSON lexical grammar does not allow a JSONStringCharacter to be any of the Unicode characters U+0010 thru U+0017', + (t) => { + t.throws(function() { + parse('"\x10\x11\x12\x13\x14\x15\x16\x17"'); // invalid string characters should produce a syntax error + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'The JSON lexical grammar does not allow a JSONStringCharacter to be any of the Unicode characters U+0018 thru U+001F', - (t) => { - t.throws(function() { - parse('"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"'); // invalid string characters should produce a syntax error - }); - }, + 'The JSON lexical grammar does not allow a JSONStringCharacter to be any of the Unicode characters U+0018 thru U+001F', + (t) => { + t.throws(function() { + parse('"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"'); // invalid string characters should produce a syntax error + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'The JSON lexical grammar allows Unicode escape sequences in a JSONString', - (t) => { - t.is(parse('"\\u0058"'), 'X', "parse('\"\\u0058\"'})"); - }, + 'The JSON lexical grammar allows Unicode escape sequences in a JSONString', + (t) => { + t.is(parse('"\\u0058"'), 'X', "parse('\"\\u0058\"'})"); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'A JSONStringCharacter UnicodeEscape may not have fewer than 4 hex characters', - (t) => { - t.throws(function() { - parse('"\\u005"'); - }); - }, + 'A JSONStringCharacter UnicodeEscape may not have fewer than 4 hex characters', + (t) => { + t.throws(function() { + parse('"\\u005"'); + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'A JSONStringCharacter UnicodeEscape may not include any non=hex characters', - (t) => { - t.throws(function() { - parse('"\\u0X50"'); - }); - }, + 'A JSONStringCharacter UnicodeEscape may not include any non=hex characters', + (t) => { + t.throws(function() { + parse('"\\u0X50"'); + }); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - "The JSON lexical grammer allows '/' as a JSONEscapeCharacter after '' in a JSONString", - (t) => { - t.is(parse('"\\/"'), '/', "parse('\"\\/\"'})"); - }, + "The JSON lexical grammer allows '/' as a JSONEscapeCharacter after '' in a JSONString", + (t) => { + t.is(parse('"\\/"'), '/', "parse('\"\\/\"'})"); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - "The JSON lexical grammer allows '' as a JSONEscapeCharacter after '' in a JSONString", - (t) => { - t.is(parse('"\\\\"'), '\\', "parse('\"\\\\\"'})"); - }, + "The JSON lexical grammer allows '' as a JSONEscapeCharacter after '' in a JSONString", + (t) => { + t.is(parse('"\\\\"'), '\\', "parse('\"\\\\\"'})"); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - "The JSON lexical grammer allows 'b' as a JSONEscapeCharacter after '' in a JSONString", - (t) => { - t.is(parse('"\\b"'), '\b', "parse('\"\\b\"'})"); - }, + "The JSON lexical grammer allows 'b' as a JSONEscapeCharacter after '' in a JSONString", + (t) => { + t.is(parse('"\\b"'), '\b', "parse('\"\\b\"'})"); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - "The JSON lexical grammer allows 'f' as a JSONEscapeCharacter after '' in a JSONString", - (t) => { - t.is(parse('"\\f"'), '\f', "parse('\"\\f\"'})"); - }, + "The JSON lexical grammer allows 'f' as a JSONEscapeCharacter after '' in a JSONString", + (t) => { + t.is(parse('"\\f"'), '\f', "parse('\"\\f\"'})"); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - "The JSON lexical grammer allows 'n' as a JSONEscapeCharacter after '' in a JSONString", - (t) => { - t.is(parse('"\\n"'), '\n', "parse('\"\\n\"'})"); - }, + "The JSON lexical grammer allows 'n' as a JSONEscapeCharacter after '' in a JSONString", + (t) => { + t.is(parse('"\\n"'), '\n', "parse('\"\\n\"'})"); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - "The JSON lexical grammer allows 'r' as a JSONEscapeCharacter after '' in a JSONString", - (t) => { - t.is(parse('"\\r"'), '\r', "parse('\"\\r\"'})"); - }, + "The JSON lexical grammer allows 'r' as a JSONEscapeCharacter after '' in a JSONString", + (t) => { + t.is(parse('"\\r"'), '\r', "parse('\"\\r\"'})"); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - "The JSON lexical grammer allows 't' as a JSONEscapeCharacter after '' in a JSONString", - (t) => { - t.is(parse('"\\t"'), '\t', "parse('\"\\t\"'})"); - }, + "The JSON lexical grammer allows 't' as a JSONEscapeCharacter after '' in a JSONString", + (t) => { + t.is(parse('"\\t"'), '\t', "parse('\"\\t\"'})"); + }, ); // Copyright (c) 2012 Ecma International. All rights reserved. test( - 'parse - parsing an object where property value middles with a null character', - (t) => { - let nullChars: Array = []; - nullChars[0] = '"\0"'; - nullChars[1] = '"\x01"'; - nullChars[2] = '"\x02"'; - nullChars[3] = '"\x03"'; - nullChars[4] = '"\x04"'; - nullChars[5] = '"\x05"'; - nullChars[6] = '"\x06"'; - nullChars[7] = '"\x07"'; - nullChars[8] = '"\b"'; - nullChars[9] = '"\t"'; - nullChars[10] = '"\n"'; - nullChars[11] = '"\x0b"'; - nullChars[12] = '"\f"'; - nullChars[13] = '"\r"'; - nullChars[14] = '"\x0e"'; - nullChars[15] = '"\x0f"'; - nullChars[16] = '"\x10"'; - nullChars[17] = '"\x11"'; - nullChars[18] = '"\x12"'; - nullChars[19] = '"\x13"'; - nullChars[20] = '"\x14"'; - nullChars[21] = '"\x15"'; - nullChars[22] = '"\x16"'; - nullChars[23] = '"\x17"'; - nullChars[24] = '"\x18"'; - nullChars[25] = '"\x19"'; - nullChars[26] = '"\x1a"'; - nullChars[27] = '"\x1b"'; - nullChars[28] = '"\x1c"'; - nullChars[29] = '"\x1d"'; - nullChars[30] = '"\x1e"'; - nullChars[31] = '"\x1f"'; - - for (const char of nullChars) { - t.throws( - function() { - parse(`{ "name" : Jo'${char}hn } `); - }, - DiagnosticsError, - ); - } - }, -); - -// Copyright (c) 2012 Ecma International. All rights reserved. -test( - 'parse - parsing an object where property name is a null character', - (t) => { - let nullChars: Array = []; - nullChars[0] = '"\0"'; - nullChars[1] = '"\x01"'; - nullChars[2] = '"\x02"'; - nullChars[3] = '"\x03"'; - nullChars[4] = '"\x04"'; - nullChars[5] = '"\x05"'; - nullChars[6] = '"\x06"'; - nullChars[7] = '"\x07"'; - nullChars[8] = '"\b"'; - nullChars[9] = '"\t"'; - nullChars[10] = '"\n"'; - nullChars[11] = '"\x0b"'; - nullChars[12] = '"\f"'; - nullChars[13] = '"\r"'; - nullChars[14] = '"\x0e"'; - nullChars[15] = '"\x0f"'; - nullChars[16] = '"\x10"'; - nullChars[17] = '"\x11"'; - nullChars[18] = '"\x12"'; - nullChars[19] = '"\x13"'; - nullChars[20] = '"\x14"'; - nullChars[21] = '"\x15"'; - nullChars[22] = '"\x16"'; - nullChars[23] = '"\x17"'; - nullChars[24] = '"\x18"'; - nullChars[25] = '"\x19"'; - nullChars[26] = '"\x1a"'; - nullChars[27] = '"\x1b"'; - nullChars[28] = '"\x1c"'; - nullChars[29] = '"\x1d"'; - nullChars[30] = '"\x1e"'; - nullChars[31] = '"\x1f"'; - - for (let char of nullChars) { - t.throws( - function() { - parse(`{ ${char} : "John" }`); - }, - DiagnosticsError, - ); - } - }, -); - -// Copyright (c) 2012 Ecma International. All rights reserved. -test( - 'parse - parsing an object where property name starts with a null character', - (t) => { - let nullChars: Array = []; - nullChars[0] = '"\0"'; - nullChars[1] = '"\x01"'; - nullChars[2] = '"\x02"'; - nullChars[3] = '"\x03"'; - nullChars[4] = '"\x04"'; - nullChars[5] = '"\x05"'; - nullChars[6] = '"\x06"'; - nullChars[7] = '"\x07"'; - nullChars[8] = '"\b"'; - nullChars[9] = '"\t"'; - nullChars[10] = '"\n"'; - nullChars[11] = '"\x0b"'; - nullChars[12] = '"\f"'; - nullChars[13] = '"\r"'; - nullChars[14] = '"\x0e"'; - nullChars[15] = '"\x0f"'; - nullChars[16] = '"\x10"'; - nullChars[17] = '"\x11"'; - nullChars[18] = '"\x12"'; - nullChars[19] = '"\x13"'; - nullChars[20] = '"\x14"'; - nullChars[21] = '"\x15"'; - nullChars[22] = '"\x16"'; - nullChars[23] = '"\x17"'; - nullChars[24] = '"\x18"'; - nullChars[25] = '"\x19"'; - nullChars[26] = '"\x1a"'; - nullChars[27] = '"\x1b"'; - nullChars[28] = '"\x1c"'; - nullChars[29] = '"\x1d"'; - nullChars[30] = '"\x1e"'; - nullChars[31] = '"\x1f"'; - for (let char of nullChars) { - t.throws( - function() { - parse(`{ ${char}name : "John" }`); - }, - DiagnosticsError, - ); - } - }, -); - -// Copyright (c) 2012 Ecma International. All rights reserved. -test( - 'parse - parsing an object where property name ends with a null character', - (t) => { - let nullChars: Array = []; - nullChars[0] = '"\0"'; - nullChars[1] = '"\x01"'; - nullChars[2] = '"\x02"'; - nullChars[3] = '"\x03"'; - nullChars[4] = '"\x04"'; - nullChars[5] = '"\x05"'; - nullChars[6] = '"\x06"'; - nullChars[7] = '"\x07"'; - nullChars[8] = '"\b"'; - nullChars[9] = '"\t"'; - nullChars[10] = '"\n"'; - nullChars[11] = '"\x0b"'; - nullChars[12] = '"\f"'; - nullChars[13] = '"\r"'; - nullChars[14] = '"\x0e"'; - nullChars[15] = '"\x0f"'; - nullChars[16] = '"\x10"'; - nullChars[17] = '"\x11"'; - nullChars[18] = '"\x12"'; - nullChars[19] = '"\x13"'; - nullChars[20] = '"\x14"'; - nullChars[21] = '"\x15"'; - nullChars[22] = '"\x16"'; - nullChars[23] = '"\x17"'; - nullChars[24] = '"\x18"'; - nullChars[25] = '"\x19"'; - nullChars[26] = '"\x1a"'; - nullChars[27] = '"\x1b"'; - nullChars[28] = '"\x1c"'; - nullChars[29] = '"\x1d"'; - nullChars[30] = '"\x1e"'; - nullChars[31] = '"\x1f"'; - - for (let char of nullChars) { - t.throws( - function() { - parse(`{name${char} : "John" }`); - }, - DiagnosticsError, - ); - } - }, -); - -// Copyright (c) 2012 Ecma International. All rights reserved. -test( - 'parse - parsing an object where property name starts and ends with a null character', - (t) => { - let nullChars: Array = []; - nullChars[0] = '"\0"'; - nullChars[1] = '"\x01"'; - nullChars[2] = '"\x02"'; - nullChars[3] = '"\x03"'; - nullChars[4] = '"\x04"'; - nullChars[5] = '"\x05"'; - nullChars[6] = '"\x06"'; - nullChars[7] = '"\x07"'; - nullChars[8] = '"\b"'; - nullChars[9] = '"\t"'; - nullChars[10] = '"\n"'; - nullChars[11] = '"\x0b"'; - nullChars[12] = '"\f"'; - nullChars[13] = '"\r"'; - nullChars[14] = '"\x0e"'; - nullChars[15] = '"\x0f"'; - nullChars[16] = '"\x10"'; - nullChars[17] = '"\x11"'; - nullChars[18] = '"\x12"'; - nullChars[19] = '"\x13"'; - nullChars[20] = '"\x14"'; - nullChars[21] = '"\x15"'; - nullChars[22] = '"\x16"'; - nullChars[23] = '"\x17"'; - nullChars[24] = '"\x18"'; - nullChars[25] = '"\x19"'; - nullChars[26] = '"\x1a"'; - nullChars[27] = '"\x1b"'; - nullChars[28] = '"\x1c"'; - nullChars[29] = '"\x1d"'; - nullChars[30] = '"\x1e"'; - nullChars[31] = '"\x1f"'; - for (let char of nullChars) { - t.throws( - function() { - parse(`{${char}name${char} : "John" }`); - }, - DiagnosticsError, - ); - } - }, -); - -// Copyright (c) 2012 Ecma International. All rights reserved. -test( - 'parse - parsing an object where property name middles with a null character', - (t) => { - let nullChars: Array = []; - nullChars[0] = '"\0"'; - nullChars[1] = '"\x01"'; - nullChars[2] = '"\x02"'; - nullChars[3] = '"\x03"'; - nullChars[4] = '"\x04"'; - nullChars[5] = '"\x05"'; - nullChars[6] = '"\x06"'; - nullChars[7] = '"\x07"'; - nullChars[8] = '"\b"'; - nullChars[9] = '"\t"'; - nullChars[10] = '"\n"'; - nullChars[11] = '"\x0b"'; - nullChars[12] = '"\f"'; - nullChars[13] = '"\r"'; - nullChars[14] = '"\x0e"'; - nullChars[15] = '"\x0f"'; - nullChars[16] = '"\x10"'; - nullChars[17] = '"\x11"'; - nullChars[18] = '"\x12"'; - nullChars[19] = '"\x13"'; - nullChars[20] = '"\x14"'; - nullChars[21] = '"\x15"'; - nullChars[22] = '"\x16"'; - nullChars[23] = '"\x17"'; - nullChars[24] = '"\x18"'; - nullChars[25] = '"\x19"'; - nullChars[26] = '"\x1a"'; - nullChars[27] = '"\x1b"'; - nullChars[28] = '"\x1c"'; - nullChars[29] = '"\x1d"'; - nullChars[30] = '"\x1e"'; - nullChars[31] = '"\x1f"'; - for (let char of nullChars) { - t.throws( - function() { - parse(`{ na${char}me : "John" }`); - }, - DiagnosticsError, - ); - } - }, -); - -// Copyright (c) 2012 Ecma International. All rights reserved. -test( - 'parse - parsing an object where property value is a null character', - (t) => { - let nullChars: Array = []; - nullChars[0] = '"\0"'; - nullChars[1] = '"\x01"'; - nullChars[2] = '"\x02"'; - nullChars[3] = '"\x03"'; - nullChars[4] = '"\x04"'; - nullChars[5] = '"\x05"'; - nullChars[6] = '"\x06"'; - nullChars[7] = '"\x07"'; - nullChars[8] = '"\b"'; - nullChars[9] = '"\t"'; - nullChars[10] = '"\n"'; - nullChars[11] = '"\x0b"'; - nullChars[12] = '"\f"'; - nullChars[13] = '"\r"'; - nullChars[14] = '"\x0e"'; - nullChars[15] = '"\x0f"'; - nullChars[16] = '"\x10"'; - nullChars[17] = '"\x11"'; - nullChars[18] = '"\x12"'; - nullChars[19] = '"\x13"'; - nullChars[20] = '"\x14"'; - nullChars[21] = '"\x15"'; - nullChars[22] = '"\x16"'; - nullChars[23] = '"\x17"'; - nullChars[24] = '"\x18"'; - nullChars[25] = '"\x19"'; - nullChars[26] = '"\x1a"'; - nullChars[27] = '"\x1b"'; - nullChars[28] = '"\x1c"'; - nullChars[29] = '"\x1d"'; - nullChars[30] = '"\x1e"'; - nullChars[31] = '"\x1f"'; - for (let char of nullChars) { - t.throws( - function() { - parse(`{ "name" : ${char} }`); - }, - DiagnosticsError, - ); - } - }, -); - -// Copyright (c) 2012 Ecma International. All rights reserved. -test( - 'parse - parsing an object where property value starts with a null character', - (t) => { - let nullChars: Array = []; - nullChars[0] = '"\0"'; - nullChars[1] = '"\x01"'; - nullChars[2] = '"\x02"'; - nullChars[3] = '"\x03"'; - nullChars[4] = '"\x04"'; - nullChars[5] = '"\x05"'; - nullChars[6] = '"\x06"'; - nullChars[7] = '"\x07"'; - nullChars[8] = '"\b"'; - nullChars[9] = '"\t"'; - nullChars[10] = '"\n"'; - nullChars[11] = '"\x0b"'; - nullChars[12] = '"\f"'; - nullChars[13] = '"\r"'; - nullChars[14] = '"\x0e"'; - nullChars[15] = '"\x0f"'; - nullChars[16] = '"\x10"'; - nullChars[17] = '"\x11"'; - nullChars[18] = '"\x12"'; - nullChars[19] = '"\x13"'; - nullChars[20] = '"\x14"'; - nullChars[21] = '"\x15"'; - nullChars[22] = '"\x16"'; - nullChars[23] = '"\x17"'; - nullChars[24] = '"\x18"'; - nullChars[25] = '"\x19"'; - nullChars[26] = '"\x1a"'; - nullChars[27] = '"\x1b"'; - nullChars[28] = '"\x1c"'; - nullChars[29] = '"\x1d"'; - nullChars[30] = '"\x1e"'; - nullChars[31] = '"\x1f"'; - for (let char of nullChars) { - t.throws( - function() { - parse(`{ "name" : ${char}John }`); - }, - DiagnosticsError, - ); - } - }, -); - -// Copyright (c) 2012 Ecma International. All rights reserved. -test( - 'parse - parsing an object where property value ends with a null character', - (t) => { - let nullChars: Array = []; - nullChars[0] = '"\0"'; - nullChars[1] = '"\x01"'; - nullChars[2] = '"\x02"'; - nullChars[3] = '"\x03"'; - nullChars[4] = '"\x04"'; - nullChars[5] = '"\x05"'; - nullChars[6] = '"\x06"'; - nullChars[7] = '"\x07"'; - nullChars[8] = '"\b"'; - nullChars[9] = '"\t"'; - nullChars[10] = '"\n"'; - nullChars[11] = '"\x0b"'; - nullChars[12] = '"\f"'; - nullChars[13] = '"\r"'; - nullChars[14] = '"\x0e"'; - nullChars[15] = '"\x0f"'; - nullChars[16] = '"\x10"'; - nullChars[17] = '"\x11"'; - nullChars[18] = '"\x12"'; - nullChars[19] = '"\x13"'; - nullChars[20] = '"\x14"'; - nullChars[21] = '"\x15"'; - nullChars[22] = '"\x16"'; - nullChars[23] = '"\x17"'; - nullChars[24] = '"\x18"'; - nullChars[25] = '"\x19"'; - nullChars[26] = '"\x1a"'; - nullChars[27] = '"\x1b"'; - nullChars[28] = '"\x1c"'; - nullChars[29] = '"\x1d"'; - nullChars[30] = '"\x1e"'; - nullChars[31] = '"\x1f"'; - for (let char of nullChars) { - t.throws( - function() { - parse(`{ "name" : John${char} }`); - }, - DiagnosticsError, - ); - } - }, -); - -// Copyright (c) 2012 Ecma International. All rights reserved. -test( - 'parse - parsing an object where property value starts and ends with a null character', - (t) => { - let nullChars: Array = []; - nullChars[0] = '"\0"'; - nullChars[1] = '"\x01"'; - nullChars[2] = '"\x02"'; - nullChars[3] = '"\x03"'; - nullChars[4] = '"\x04"'; - nullChars[5] = '"\x05"'; - nullChars[6] = '"\x06"'; - nullChars[7] = '"\x07"'; - nullChars[8] = '"\b"'; - nullChars[9] = '"\t"'; - nullChars[10] = '"\n"'; - nullChars[11] = '"\x0b"'; - nullChars[12] = '"\f"'; - nullChars[13] = '"\r"'; - nullChars[14] = '"\x0e"'; - nullChars[15] = '"\x0f"'; - nullChars[16] = '"\x10"'; - nullChars[17] = '"\x11"'; - nullChars[18] = '"\x12"'; - nullChars[19] = '"\x13"'; - nullChars[20] = '"\x14"'; - nullChars[21] = '"\x15"'; - nullChars[22] = '"\x16"'; - nullChars[23] = '"\x17"'; - nullChars[24] = '"\x18"'; - nullChars[25] = '"\x19"'; - nullChars[26] = '"\x1a"'; - nullChars[27] = '"\x1b"'; - nullChars[28] = '"\x1c"'; - nullChars[29] = '"\x1d"'; - nullChars[30] = '"\x1e"'; - nullChars[31] = '"\x1f"'; - for (let char of nullChars) { - t.throws( - function() { - parse(`{ "name" : ${char}John${char} }`); - }, - DiagnosticsError, - ); - } - }, -); - -// Copyright (c) 2012 Ecma International. All rights reserved. -test( - 'Other category z spaces are not valid JSON whitespace as specified by the production JSONWhitespace.', - (t) => { - t.throws( - function() { - parse('\u16801'); - }, - DiagnosticsError, - '\\u1680', - ); - - t.throws( - function() { - parse('\u180e1'); - }, - DiagnosticsError, - '\\u180e', - ); - - t.throws( - function() { - parse('\u20001'); - }, - DiagnosticsError, - '\\u2000', - ); - - t.throws( - function() { - parse('\u20011'); - }, - DiagnosticsError, - '\\u2001', - ); - - t.throws( - function() { - parse('\u20021'); - }, - DiagnosticsError, - '\\u2002', - ); - - t.throws( - function() { - parse('\u20031'); - }, - DiagnosticsError, - '\\u2003', - ); - - t.throws( - function() { - parse('\u20041'); - }, - DiagnosticsError, - '\\u2004', - ); - - t.throws( - function() { - parse('\u20051'); - }, - DiagnosticsError, - '\\u2005', - ); - - t.throws( - function() { - parse('\u20061'); - }, - DiagnosticsError, - '\\u2006', - ); - - t.throws( - function() { - parse('\u20071'); - }, - DiagnosticsError, - '\\u2007', - ); - - t.throws( - function() { - parse('\u20081'); - }, - DiagnosticsError, - '\\u2008', - ); - - t.throws( - function() { - parse('\u20091'); - }, - DiagnosticsError, - '\\u2009', - ); - - t.throws( - function() { - parse('\u200a1'); - }, - DiagnosticsError, - '\\u200a', - ); - - t.throws( - function() { - parse('\u202f1'); - }, - DiagnosticsError, - '\\u202f', - ); - - t.throws( - function() { - parse('\u205f1'); - }, - DiagnosticsError, - '\\u205f', - ); - - t.throws( - function() { - parse('\u30001'); - }, - DiagnosticsError, - '\\u3000', - ); - }, + 'parse - parsing an object where property value middles with a null character', + (t) => { + let nullChars: Array = []; + nullChars[0] = '"\0"'; + nullChars[1] = '"\x01"'; + nullChars[2] = '"\x02"'; + nullChars[3] = '"\x03"'; + nullChars[4] = '"\x04"'; + nullChars[5] = '"\x05"'; + nullChars[6] = '"\x06"'; + nullChars[7] = '"\x07"'; + nullChars[8] = '"\b"'; + nullChars[9] = '"\t"'; + nullChars[10] = '"\n"'; + nullChars[11] = '"\x0b"'; + nullChars[12] = '"\f"'; + nullChars[13] = '"\r"'; + nullChars[14] = '"\x0e"'; + nullChars[15] = '"\x0f"'; + nullChars[16] = '"\x10"'; + nullChars[17] = '"\x11"'; + nullChars[18] = '"\x12"'; + nullChars[19] = '"\x13"'; + nullChars[20] = '"\x14"'; + nullChars[21] = '"\x15"'; + nullChars[22] = '"\x16"'; + nullChars[23] = '"\x17"'; + nullChars[24] = '"\x18"'; + nullChars[25] = '"\x19"'; + nullChars[26] = '"\x1a"'; + nullChars[27] = '"\x1b"'; + nullChars[28] = '"\x1c"'; + nullChars[29] = '"\x1d"'; + nullChars[30] = '"\x1e"'; + nullChars[31] = '"\x1f"'; + + for (const char of nullChars) { + t.throws( + function() { + parse(`{ "name" : Jo'${char}hn } `); + }, + DiagnosticsError, + ); + } + }, +); + +// Copyright (c) 2012 Ecma International. All rights reserved. +test( + 'parse - parsing an object where property name is a null character', + (t) => { + let nullChars: Array = []; + nullChars[0] = '"\0"'; + nullChars[1] = '"\x01"'; + nullChars[2] = '"\x02"'; + nullChars[3] = '"\x03"'; + nullChars[4] = '"\x04"'; + nullChars[5] = '"\x05"'; + nullChars[6] = '"\x06"'; + nullChars[7] = '"\x07"'; + nullChars[8] = '"\b"'; + nullChars[9] = '"\t"'; + nullChars[10] = '"\n"'; + nullChars[11] = '"\x0b"'; + nullChars[12] = '"\f"'; + nullChars[13] = '"\r"'; + nullChars[14] = '"\x0e"'; + nullChars[15] = '"\x0f"'; + nullChars[16] = '"\x10"'; + nullChars[17] = '"\x11"'; + nullChars[18] = '"\x12"'; + nullChars[19] = '"\x13"'; + nullChars[20] = '"\x14"'; + nullChars[21] = '"\x15"'; + nullChars[22] = '"\x16"'; + nullChars[23] = '"\x17"'; + nullChars[24] = '"\x18"'; + nullChars[25] = '"\x19"'; + nullChars[26] = '"\x1a"'; + nullChars[27] = '"\x1b"'; + nullChars[28] = '"\x1c"'; + nullChars[29] = '"\x1d"'; + nullChars[30] = '"\x1e"'; + nullChars[31] = '"\x1f"'; + + for (let char of nullChars) { + t.throws( + function() { + parse(`{ ${char} : "John" }`); + }, + DiagnosticsError, + ); + } + }, +); + +// Copyright (c) 2012 Ecma International. All rights reserved. +test( + 'parse - parsing an object where property name starts with a null character', + (t) => { + let nullChars: Array = []; + nullChars[0] = '"\0"'; + nullChars[1] = '"\x01"'; + nullChars[2] = '"\x02"'; + nullChars[3] = '"\x03"'; + nullChars[4] = '"\x04"'; + nullChars[5] = '"\x05"'; + nullChars[6] = '"\x06"'; + nullChars[7] = '"\x07"'; + nullChars[8] = '"\b"'; + nullChars[9] = '"\t"'; + nullChars[10] = '"\n"'; + nullChars[11] = '"\x0b"'; + nullChars[12] = '"\f"'; + nullChars[13] = '"\r"'; + nullChars[14] = '"\x0e"'; + nullChars[15] = '"\x0f"'; + nullChars[16] = '"\x10"'; + nullChars[17] = '"\x11"'; + nullChars[18] = '"\x12"'; + nullChars[19] = '"\x13"'; + nullChars[20] = '"\x14"'; + nullChars[21] = '"\x15"'; + nullChars[22] = '"\x16"'; + nullChars[23] = '"\x17"'; + nullChars[24] = '"\x18"'; + nullChars[25] = '"\x19"'; + nullChars[26] = '"\x1a"'; + nullChars[27] = '"\x1b"'; + nullChars[28] = '"\x1c"'; + nullChars[29] = '"\x1d"'; + nullChars[30] = '"\x1e"'; + nullChars[31] = '"\x1f"'; + for (let char of nullChars) { + t.throws( + function() { + parse(`{ ${char}name : "John" }`); + }, + DiagnosticsError, + ); + } + }, +); + +// Copyright (c) 2012 Ecma International. All rights reserved. +test( + 'parse - parsing an object where property name ends with a null character', + (t) => { + let nullChars: Array = []; + nullChars[0] = '"\0"'; + nullChars[1] = '"\x01"'; + nullChars[2] = '"\x02"'; + nullChars[3] = '"\x03"'; + nullChars[4] = '"\x04"'; + nullChars[5] = '"\x05"'; + nullChars[6] = '"\x06"'; + nullChars[7] = '"\x07"'; + nullChars[8] = '"\b"'; + nullChars[9] = '"\t"'; + nullChars[10] = '"\n"'; + nullChars[11] = '"\x0b"'; + nullChars[12] = '"\f"'; + nullChars[13] = '"\r"'; + nullChars[14] = '"\x0e"'; + nullChars[15] = '"\x0f"'; + nullChars[16] = '"\x10"'; + nullChars[17] = '"\x11"'; + nullChars[18] = '"\x12"'; + nullChars[19] = '"\x13"'; + nullChars[20] = '"\x14"'; + nullChars[21] = '"\x15"'; + nullChars[22] = '"\x16"'; + nullChars[23] = '"\x17"'; + nullChars[24] = '"\x18"'; + nullChars[25] = '"\x19"'; + nullChars[26] = '"\x1a"'; + nullChars[27] = '"\x1b"'; + nullChars[28] = '"\x1c"'; + nullChars[29] = '"\x1d"'; + nullChars[30] = '"\x1e"'; + nullChars[31] = '"\x1f"'; + + for (let char of nullChars) { + t.throws( + function() { + parse(`{name${char} : "John" }`); + }, + DiagnosticsError, + ); + } + }, +); + +// Copyright (c) 2012 Ecma International. All rights reserved. +test( + 'parse - parsing an object where property name starts and ends with a null character', + (t) => { + let nullChars: Array = []; + nullChars[0] = '"\0"'; + nullChars[1] = '"\x01"'; + nullChars[2] = '"\x02"'; + nullChars[3] = '"\x03"'; + nullChars[4] = '"\x04"'; + nullChars[5] = '"\x05"'; + nullChars[6] = '"\x06"'; + nullChars[7] = '"\x07"'; + nullChars[8] = '"\b"'; + nullChars[9] = '"\t"'; + nullChars[10] = '"\n"'; + nullChars[11] = '"\x0b"'; + nullChars[12] = '"\f"'; + nullChars[13] = '"\r"'; + nullChars[14] = '"\x0e"'; + nullChars[15] = '"\x0f"'; + nullChars[16] = '"\x10"'; + nullChars[17] = '"\x11"'; + nullChars[18] = '"\x12"'; + nullChars[19] = '"\x13"'; + nullChars[20] = '"\x14"'; + nullChars[21] = '"\x15"'; + nullChars[22] = '"\x16"'; + nullChars[23] = '"\x17"'; + nullChars[24] = '"\x18"'; + nullChars[25] = '"\x19"'; + nullChars[26] = '"\x1a"'; + nullChars[27] = '"\x1b"'; + nullChars[28] = '"\x1c"'; + nullChars[29] = '"\x1d"'; + nullChars[30] = '"\x1e"'; + nullChars[31] = '"\x1f"'; + for (let char of nullChars) { + t.throws( + function() { + parse(`{${char}name${char} : "John" }`); + }, + DiagnosticsError, + ); + } + }, +); + +// Copyright (c) 2012 Ecma International. All rights reserved. +test( + 'parse - parsing an object where property name middles with a null character', + (t) => { + let nullChars: Array = []; + nullChars[0] = '"\0"'; + nullChars[1] = '"\x01"'; + nullChars[2] = '"\x02"'; + nullChars[3] = '"\x03"'; + nullChars[4] = '"\x04"'; + nullChars[5] = '"\x05"'; + nullChars[6] = '"\x06"'; + nullChars[7] = '"\x07"'; + nullChars[8] = '"\b"'; + nullChars[9] = '"\t"'; + nullChars[10] = '"\n"'; + nullChars[11] = '"\x0b"'; + nullChars[12] = '"\f"'; + nullChars[13] = '"\r"'; + nullChars[14] = '"\x0e"'; + nullChars[15] = '"\x0f"'; + nullChars[16] = '"\x10"'; + nullChars[17] = '"\x11"'; + nullChars[18] = '"\x12"'; + nullChars[19] = '"\x13"'; + nullChars[20] = '"\x14"'; + nullChars[21] = '"\x15"'; + nullChars[22] = '"\x16"'; + nullChars[23] = '"\x17"'; + nullChars[24] = '"\x18"'; + nullChars[25] = '"\x19"'; + nullChars[26] = '"\x1a"'; + nullChars[27] = '"\x1b"'; + nullChars[28] = '"\x1c"'; + nullChars[29] = '"\x1d"'; + nullChars[30] = '"\x1e"'; + nullChars[31] = '"\x1f"'; + for (let char of nullChars) { + t.throws( + function() { + parse(`{ na${char}me : "John" }`); + }, + DiagnosticsError, + ); + } + }, +); + +// Copyright (c) 2012 Ecma International. All rights reserved. +test( + 'parse - parsing an object where property value is a null character', + (t) => { + let nullChars: Array = []; + nullChars[0] = '"\0"'; + nullChars[1] = '"\x01"'; + nullChars[2] = '"\x02"'; + nullChars[3] = '"\x03"'; + nullChars[4] = '"\x04"'; + nullChars[5] = '"\x05"'; + nullChars[6] = '"\x06"'; + nullChars[7] = '"\x07"'; + nullChars[8] = '"\b"'; + nullChars[9] = '"\t"'; + nullChars[10] = '"\n"'; + nullChars[11] = '"\x0b"'; + nullChars[12] = '"\f"'; + nullChars[13] = '"\r"'; + nullChars[14] = '"\x0e"'; + nullChars[15] = '"\x0f"'; + nullChars[16] = '"\x10"'; + nullChars[17] = '"\x11"'; + nullChars[18] = '"\x12"'; + nullChars[19] = '"\x13"'; + nullChars[20] = '"\x14"'; + nullChars[21] = '"\x15"'; + nullChars[22] = '"\x16"'; + nullChars[23] = '"\x17"'; + nullChars[24] = '"\x18"'; + nullChars[25] = '"\x19"'; + nullChars[26] = '"\x1a"'; + nullChars[27] = '"\x1b"'; + nullChars[28] = '"\x1c"'; + nullChars[29] = '"\x1d"'; + nullChars[30] = '"\x1e"'; + nullChars[31] = '"\x1f"'; + for (let char of nullChars) { + t.throws( + function() { + parse(`{ "name" : ${char} }`); + }, + DiagnosticsError, + ); + } + }, +); + +// Copyright (c) 2012 Ecma International. All rights reserved. +test( + 'parse - parsing an object where property value starts with a null character', + (t) => { + let nullChars: Array = []; + nullChars[0] = '"\0"'; + nullChars[1] = '"\x01"'; + nullChars[2] = '"\x02"'; + nullChars[3] = '"\x03"'; + nullChars[4] = '"\x04"'; + nullChars[5] = '"\x05"'; + nullChars[6] = '"\x06"'; + nullChars[7] = '"\x07"'; + nullChars[8] = '"\b"'; + nullChars[9] = '"\t"'; + nullChars[10] = '"\n"'; + nullChars[11] = '"\x0b"'; + nullChars[12] = '"\f"'; + nullChars[13] = '"\r"'; + nullChars[14] = '"\x0e"'; + nullChars[15] = '"\x0f"'; + nullChars[16] = '"\x10"'; + nullChars[17] = '"\x11"'; + nullChars[18] = '"\x12"'; + nullChars[19] = '"\x13"'; + nullChars[20] = '"\x14"'; + nullChars[21] = '"\x15"'; + nullChars[22] = '"\x16"'; + nullChars[23] = '"\x17"'; + nullChars[24] = '"\x18"'; + nullChars[25] = '"\x19"'; + nullChars[26] = '"\x1a"'; + nullChars[27] = '"\x1b"'; + nullChars[28] = '"\x1c"'; + nullChars[29] = '"\x1d"'; + nullChars[30] = '"\x1e"'; + nullChars[31] = '"\x1f"'; + for (let char of nullChars) { + t.throws( + function() { + parse(`{ "name" : ${char}John }`); + }, + DiagnosticsError, + ); + } + }, +); + +// Copyright (c) 2012 Ecma International. All rights reserved. +test( + 'parse - parsing an object where property value ends with a null character', + (t) => { + let nullChars: Array = []; + nullChars[0] = '"\0"'; + nullChars[1] = '"\x01"'; + nullChars[2] = '"\x02"'; + nullChars[3] = '"\x03"'; + nullChars[4] = '"\x04"'; + nullChars[5] = '"\x05"'; + nullChars[6] = '"\x06"'; + nullChars[7] = '"\x07"'; + nullChars[8] = '"\b"'; + nullChars[9] = '"\t"'; + nullChars[10] = '"\n"'; + nullChars[11] = '"\x0b"'; + nullChars[12] = '"\f"'; + nullChars[13] = '"\r"'; + nullChars[14] = '"\x0e"'; + nullChars[15] = '"\x0f"'; + nullChars[16] = '"\x10"'; + nullChars[17] = '"\x11"'; + nullChars[18] = '"\x12"'; + nullChars[19] = '"\x13"'; + nullChars[20] = '"\x14"'; + nullChars[21] = '"\x15"'; + nullChars[22] = '"\x16"'; + nullChars[23] = '"\x17"'; + nullChars[24] = '"\x18"'; + nullChars[25] = '"\x19"'; + nullChars[26] = '"\x1a"'; + nullChars[27] = '"\x1b"'; + nullChars[28] = '"\x1c"'; + nullChars[29] = '"\x1d"'; + nullChars[30] = '"\x1e"'; + nullChars[31] = '"\x1f"'; + for (let char of nullChars) { + t.throws( + function() { + parse(`{ "name" : John${char} }`); + }, + DiagnosticsError, + ); + } + }, +); + +// Copyright (c) 2012 Ecma International. All rights reserved. +test( + 'parse - parsing an object where property value starts and ends with a null character', + (t) => { + let nullChars: Array = []; + nullChars[0] = '"\0"'; + nullChars[1] = '"\x01"'; + nullChars[2] = '"\x02"'; + nullChars[3] = '"\x03"'; + nullChars[4] = '"\x04"'; + nullChars[5] = '"\x05"'; + nullChars[6] = '"\x06"'; + nullChars[7] = '"\x07"'; + nullChars[8] = '"\b"'; + nullChars[9] = '"\t"'; + nullChars[10] = '"\n"'; + nullChars[11] = '"\x0b"'; + nullChars[12] = '"\f"'; + nullChars[13] = '"\r"'; + nullChars[14] = '"\x0e"'; + nullChars[15] = '"\x0f"'; + nullChars[16] = '"\x10"'; + nullChars[17] = '"\x11"'; + nullChars[18] = '"\x12"'; + nullChars[19] = '"\x13"'; + nullChars[20] = '"\x14"'; + nullChars[21] = '"\x15"'; + nullChars[22] = '"\x16"'; + nullChars[23] = '"\x17"'; + nullChars[24] = '"\x18"'; + nullChars[25] = '"\x19"'; + nullChars[26] = '"\x1a"'; + nullChars[27] = '"\x1b"'; + nullChars[28] = '"\x1c"'; + nullChars[29] = '"\x1d"'; + nullChars[30] = '"\x1e"'; + nullChars[31] = '"\x1f"'; + for (let char of nullChars) { + t.throws( + function() { + parse(`{ "name" : ${char}John${char} }`); + }, + DiagnosticsError, + ); + } + }, +); + +// Copyright (c) 2012 Ecma International. All rights reserved. +test( + 'Other category z spaces are not valid JSON whitespace as specified by the production JSONWhitespace.', + (t) => { + t.throws( + function() { + parse('\u16801'); + }, + DiagnosticsError, + '\\u1680', + ); + + t.throws( + function() { + parse('\u180e1'); + }, + DiagnosticsError, + '\\u180e', + ); + + t.throws( + function() { + parse('\u20001'); + }, + DiagnosticsError, + '\\u2000', + ); + + t.throws( + function() { + parse('\u20011'); + }, + DiagnosticsError, + '\\u2001', + ); + + t.throws( + function() { + parse('\u20021'); + }, + DiagnosticsError, + '\\u2002', + ); + + t.throws( + function() { + parse('\u20031'); + }, + DiagnosticsError, + '\\u2003', + ); + + t.throws( + function() { + parse('\u20041'); + }, + DiagnosticsError, + '\\u2004', + ); + + t.throws( + function() { + parse('\u20051'); + }, + DiagnosticsError, + '\\u2005', + ); + + t.throws( + function() { + parse('\u20061'); + }, + DiagnosticsError, + '\\u2006', + ); + + t.throws( + function() { + parse('\u20071'); + }, + DiagnosticsError, + '\\u2007', + ); + + t.throws( + function() { + parse('\u20081'); + }, + DiagnosticsError, + '\\u2008', + ); + + t.throws( + function() { + parse('\u20091'); + }, + DiagnosticsError, + '\\u2009', + ); + + t.throws( + function() { + parse('\u200a1'); + }, + DiagnosticsError, + '\\u200a', + ); + + t.throws( + function() { + parse('\u202f1'); + }, + DiagnosticsError, + '\\u202f', + ); + + t.throws( + function() { + parse('\u205f1'); + }, + DiagnosticsError, + '\\u205f', + ); + + t.throws( + function() { + parse('\u30001'); + }, + DiagnosticsError, + '\\u3000', + ); + }, ); // Copyright 2011 the Sputnik authors. All rights reserved. test( - 'Tests that parse treats "__proto__" as a regular property name', - () => { - let x = parse('{"__proto__":[]}'); + 'Tests that parse treats "__proto__" as a regular property name', + () => { + let x = parse('{"__proto__":[]}'); - if (Object.getPrototypeOf(x) !== Object.prototype) { - throw new Error('#1: parse confused by "__proto__"'); - } + if (Object.getPrototypeOf(x) !== Object.prototype) { + throw new Error('#1: parse confused by "__proto__"'); + } - // @ts-ignore - if (!Array.isArray(x.__proto__)) { - throw new Error('#2: parse did not set "__proto__" as a regular property'); - } - }, + // @ts-ignore + if (!Array.isArray(x.__proto__)) { + throw new Error('#2: parse did not set "__proto__" as a regular property'); + } + }, ); diff --git a/packages/@romejs/codec-json/parser.test.ts b/packages/@romejs/codec-json/parser.test.ts index f38bad16096..35814485bde 100644 --- a/packages/@romejs/codec-json/parser.test.ts +++ b/packages/@romejs/codec-json/parser.test.ts @@ -14,317 +14,317 @@ import {createUnknownFilePath} from '@romejs/path'; // These are just some very basic tests, most of it is already covered by test262-parse so most are redundant function parseExtJSON(opts: ParserOptions) { - return parseJSON({...opts, path: createUnknownFilePath('input.rjson')}); + return parseJSON({...opts, path: createUnknownFilePath('input.rjson')}); } test( - 'comments', - (t) => { - // comment at beginning - t.true(parseExtJSON({input: '// comment\ntrue'})); - t.true(parseExtJSON({input: '/* comment */\ntrue'})); - t.true(parseExtJSON({input: '/* comment */ true'})); - - // comment at end - t.true(parseExtJSON({input: 'true\n// comment'})); - t.true(parseExtJSON({input: 'true\n/* comment */'})); - t.true(parseExtJSON({input: 'true/* comment */'})); - - // comment before object property - t.looksLike( - parseExtJSON({input: '{/* comment */ "foo": "bar"}'}), - { - foo: 'bar', - }, - ); - t.looksLike( - parseExtJSON({input: '{// comment\n"foo": "bar"}'}), - { - foo: 'bar', - }, - ); - - // comment before object property value - t.looksLike( - parseExtJSON({input: '{"foo": /* comment */ "bar"}'}), - { - foo: 'bar', - }, - ); - t.looksLike( - parseExtJSON({input: '{"foo": // comment\n"bar"}'}), - { - foo: 'bar', - }, - ); - - // comment after object property value - t.looksLike( - parseExtJSON({input: '{"foo": "bar" /* comment */,}'}), - { - foo: 'bar', - }, - ); - t.looksLike( - parseExtJSON({input: '{"foo": "bar" // comment\n,}'}), - { - foo: 'bar', - }, - ); - - // comment after object property - t.looksLike( - parseExtJSON({input: '{"foo": "bar", /* comment */}'}), - { - foo: 'bar', - }, - ); - t.looksLike( - parseExtJSON({input: '{"foo": "bar", // comment\n}'}), - { - foo: 'bar', - }, - ); - - // comment before array element - t.looksLike(parseExtJSON({input: '[/* comment */ "foo"]'}), ['foo']); - t.looksLike(parseExtJSON({input: '[//comment\n"foo"]'}), ['foo']); - - // comment after array element - t.looksLike(parseExtJSON({input: '["foo" /* comment */]'}), ['foo']); - t.looksLike(parseExtJSON({input: '["foo" //comment\n]'}), ['foo']); - - // comment after array element value - t.looksLike( - parseExtJSON({input: '["foo" /* comment */, "bar"]'}), - ['foo', 'bar'], - ); - t.looksLike( - parseExtJSON({input: '["foo" //comment\n, "bar"]'}), - ['foo', 'bar'], - ); - - // comment only in array - t.looksLike(parseExtJSON({input: '[/* comment */]'}), []); - t.looksLike(parseExtJSON({input: '[// comment\n]'}), []); - - // comment only in object - t.looksLike(parseExtJSON({input: '{/* comment */}'}), {}); - t.looksLike(parseExtJSON({input: '{// comment\n}'}), {}); - - // ensure closed block comment - t.throws( - () => { - parseExtJSON({input: 'true /* unclosed comment'}); - }, - descriptions.JSON.UNCLOSED_BLOCK_COMMENT.message.value, - ); - }, + 'comments', + (t) => { + // comment at beginning + t.true(parseExtJSON({input: '// comment\ntrue'})); + t.true(parseExtJSON({input: '/* comment */\ntrue'})); + t.true(parseExtJSON({input: '/* comment */ true'})); + + // comment at end + t.true(parseExtJSON({input: 'true\n// comment'})); + t.true(parseExtJSON({input: 'true\n/* comment */'})); + t.true(parseExtJSON({input: 'true/* comment */'})); + + // comment before object property + t.looksLike( + parseExtJSON({input: '{/* comment */ "foo": "bar"}'}), + { + foo: 'bar', + }, + ); + t.looksLike( + parseExtJSON({input: '{// comment\n"foo": "bar"}'}), + { + foo: 'bar', + }, + ); + + // comment before object property value + t.looksLike( + parseExtJSON({input: '{"foo": /* comment */ "bar"}'}), + { + foo: 'bar', + }, + ); + t.looksLike( + parseExtJSON({input: '{"foo": // comment\n"bar"}'}), + { + foo: 'bar', + }, + ); + + // comment after object property value + t.looksLike( + parseExtJSON({input: '{"foo": "bar" /* comment */,}'}), + { + foo: 'bar', + }, + ); + t.looksLike( + parseExtJSON({input: '{"foo": "bar" // comment\n,}'}), + { + foo: 'bar', + }, + ); + + // comment after object property + t.looksLike( + parseExtJSON({input: '{"foo": "bar", /* comment */}'}), + { + foo: 'bar', + }, + ); + t.looksLike( + parseExtJSON({input: '{"foo": "bar", // comment\n}'}), + { + foo: 'bar', + }, + ); + + // comment before array element + t.looksLike(parseExtJSON({input: '[/* comment */ "foo"]'}), ['foo']); + t.looksLike(parseExtJSON({input: '[//comment\n"foo"]'}), ['foo']); + + // comment after array element + t.looksLike(parseExtJSON({input: '["foo" /* comment */]'}), ['foo']); + t.looksLike(parseExtJSON({input: '["foo" //comment\n]'}), ['foo']); + + // comment after array element value + t.looksLike( + parseExtJSON({input: '["foo" /* comment */, "bar"]'}), + ['foo', 'bar'], + ); + t.looksLike( + parseExtJSON({input: '["foo" //comment\n, "bar"]'}), + ['foo', 'bar'], + ); + + // comment only in array + t.looksLike(parseExtJSON({input: '[/* comment */]'}), []); + t.looksLike(parseExtJSON({input: '[// comment\n]'}), []); + + // comment only in object + t.looksLike(parseExtJSON({input: '{/* comment */}'}), {}); + t.looksLike(parseExtJSON({input: '{// comment\n}'}), {}); + + // ensure closed block comment + t.throws( + () => { + parseExtJSON({input: 'true /* unclosed comment'}); + }, + descriptions.JSON.UNCLOSED_BLOCK_COMMENT.message.value, + ); + }, ); test( - 'numbers', - (t) => { - t.is(parseExtJSON({input: '1'}), 1); - t.is(parseExtJSON({input: '12'}), 12); - t.is(parseExtJSON({input: '123'}), 123); - t.is(parseExtJSON({input: '1.2'}), 1.2); - t.is(parseExtJSON({input: '1234.21234'}), 1_234.21234); - t.is(parseExtJSON({input: '0.5e+5'}), 50_000); - t.is(parseExtJSON({input: '0.5e-5'}), 0.000005); - t.is(parseExtJSON({input: '0.5E+5'}), 50_000); - t.is(parseExtJSON({input: '0.5E-5'}), 0.000005); - }, + 'numbers', + (t) => { + t.is(parseExtJSON({input: '1'}), 1); + t.is(parseExtJSON({input: '12'}), 12); + t.is(parseExtJSON({input: '123'}), 123); + t.is(parseExtJSON({input: '1.2'}), 1.2); + t.is(parseExtJSON({input: '1234.21234'}), 1_234.21234); + t.is(parseExtJSON({input: '0.5e+5'}), 50_000); + t.is(parseExtJSON({input: '0.5e-5'}), 0.000005); + t.is(parseExtJSON({input: '0.5E+5'}), 50_000); + t.is(parseExtJSON({input: '0.5E-5'}), 0.000005); + }, ); test( - 'strings', - (t) => { - t.is(parseExtJSON({input: '"foo"'}), 'foo'); - t.is(parseExtJSON({input: '"foo\u1234"'}), 'foo\u1234'); - t.is(parseExtJSON({input: '"foo\\n"'}), 'foo\n'); - t.is(parseExtJSON({input: '"foo\\t"'}), 'foo\t'); - - t.throws( - () => { - parseExtJSON({input: '"foo'}); - }, - descriptions.JSON.UNCLOSED_STRING.message.value, - ); - - t.throws( - () => { - parseExtJSON({input: '"foo\n"'}); - }, - descriptions.JSON.UNCLOSED_STRING.message.value, - ); - - t.throws( - () => { - parseExtJSON({input: "'foo'"}); - }, - descriptions.JSON.SINGLE_QUOTE_USAGE.message.value, - ); - - t.throws( - () => { - parseExtJSON({input: '"\\u000Z"'}); - }, - descriptions.STRING_ESCAPE.INVALID_HEX_DIGIT_FOR_ESCAPE.message.value, - ); - - t.throws( - () => { - parseExtJSON({input: '"\t"'}); - }, - descriptions.STRING_ESCAPE.INVALID_STRING_CHARACTER.message.value, - ); - - t.throws( - () => { - parseExtJSON({input: '"\\u123"'}); - }, - descriptions.STRING_ESCAPE.NOT_ENOUGH_CODE_POINTS.message.value, - ); - }, + 'strings', + (t) => { + t.is(parseExtJSON({input: '"foo"'}), 'foo'); + t.is(parseExtJSON({input: '"foo\u1234"'}), 'foo\u1234'); + t.is(parseExtJSON({input: '"foo\\n"'}), 'foo\n'); + t.is(parseExtJSON({input: '"foo\\t"'}), 'foo\t'); + + t.throws( + () => { + parseExtJSON({input: '"foo'}); + }, + descriptions.JSON.UNCLOSED_STRING.message.value, + ); + + t.throws( + () => { + parseExtJSON({input: '"foo\n"'}); + }, + descriptions.JSON.UNCLOSED_STRING.message.value, + ); + + t.throws( + () => { + parseExtJSON({input: "'foo'"}); + }, + descriptions.JSON.SINGLE_QUOTE_USAGE.message.value, + ); + + t.throws( + () => { + parseExtJSON({input: '"\\u000Z"'}); + }, + descriptions.STRING_ESCAPE.INVALID_HEX_DIGIT_FOR_ESCAPE.message.value, + ); + + t.throws( + () => { + parseExtJSON({input: '"\t"'}); + }, + descriptions.STRING_ESCAPE.INVALID_STRING_CHARACTER.message.value, + ); + + t.throws( + () => { + parseExtJSON({input: '"\\u123"'}); + }, + descriptions.STRING_ESCAPE.NOT_ENOUGH_CODE_POINTS.message.value, + ); + }, ); test( - 'booleans', - (t) => { - t.is(parseExtJSON({input: 'true'}), true); - t.is(parseExtJSON({input: 'false'}), false); - }, + 'booleans', + (t) => { + t.is(parseExtJSON({input: 'true'}), true); + t.is(parseExtJSON({input: 'false'}), false); + }, ); test( - 'null', - (t) => { - t.is(parseExtJSON({input: 'null'}), null); - }, + 'null', + (t) => { + t.is(parseExtJSON({input: 'null'}), null); + }, ); test( - 'undefined', - (t) => { - t.throws( - () => { - t.is(parseExtJSON({input: 'undefined'}), undefined); - }, - descriptions.JSON.UNDEFINED_IN_JSON.message.value, - ); - }, + 'undefined', + (t) => { + t.throws( + () => { + t.is(parseExtJSON({input: 'undefined'}), undefined); + }, + descriptions.JSON.UNDEFINED_IN_JSON.message.value, + ); + }, ); test( - 'arrays', - (t) => { - t.looksLike(parseExtJSON({input: '[]'}), []); - t.looksLike(parseExtJSON({input: '[1, 2, 3]'}), [1, 2, 3]); - t.looksLike(parseExtJSON({input: '[[1, 2, 3]]'}), [[1, 2, 3]]); - - t.throws( - () => { - parseExtJSON({input: '[,]'}); - }, - descriptions.JSON.REDUNDANT_COMMA.message.value, - ); - - t.throws( - () => { - parseExtJSON({input: '[1,,]'}); - }, - descriptions.JSON.REDUNDANT_COMMA.message.value, - ); - - t.throws( - () => { - parseExtJSON({input: '[1, /*comment*/,]'}); - }, - descriptions.JSON.REDUNDANT_COMMA.message.value, - ); - - t.throws( - () => { - parseExtJSON({input: '["foo": "bar"]'}); - }, - descriptions.JSON.MISTAKEN_ARRAY_IDENTITY.message.value, - ); - }, + 'arrays', + (t) => { + t.looksLike(parseExtJSON({input: '[]'}), []); + t.looksLike(parseExtJSON({input: '[1, 2, 3]'}), [1, 2, 3]); + t.looksLike(parseExtJSON({input: '[[1, 2, 3]]'}), [[1, 2, 3]]); + + t.throws( + () => { + parseExtJSON({input: '[,]'}); + }, + descriptions.JSON.REDUNDANT_COMMA.message.value, + ); + + t.throws( + () => { + parseExtJSON({input: '[1,,]'}); + }, + descriptions.JSON.REDUNDANT_COMMA.message.value, + ); + + t.throws( + () => { + parseExtJSON({input: '[1, /*comment*/,]'}); + }, + descriptions.JSON.REDUNDANT_COMMA.message.value, + ); + + t.throws( + () => { + parseExtJSON({input: '["foo": "bar"]'}); + }, + descriptions.JSON.MISTAKEN_ARRAY_IDENTITY.message.value, + ); + }, ); test( - 'objects', - (t) => { - t.looksLike(parseExtJSON({input: '{}'}), {}); - t.looksLike(parseExtJSON({input: '{"foo": "bar"}'}), {foo: 'bar'}); - t.looksLike( - parseExtJSON({input: '{"foo": "bar", "bar": "foo"}'}), - { - foo: 'bar', - bar: 'foo', - }, - ); - - t.throws( - () => { - parseExtJSON({input: '{,}'}); - }, - descriptions.JSON.REDUNDANT_COMMA.message.value, - ); - - t.throws( - () => { - parseExtJSON({input: '{"foo": "bar",,}'}); - }, - descriptions.JSON.REDUNDANT_COMMA.message.value, - ); - - t.throws( - () => { - parseExtJSON({input: '{"foo": "bar", /*comment*/,}'}); - }, - descriptions.JSON.REDUNDANT_COMMA.message.value, - ); - }, + 'objects', + (t) => { + t.looksLike(parseExtJSON({input: '{}'}), {}); + t.looksLike(parseExtJSON({input: '{"foo": "bar"}'}), {foo: 'bar'}); + t.looksLike( + parseExtJSON({input: '{"foo": "bar", "bar": "foo"}'}), + { + foo: 'bar', + bar: 'foo', + }, + ); + + t.throws( + () => { + parseExtJSON({input: '{,}'}); + }, + descriptions.JSON.REDUNDANT_COMMA.message.value, + ); + + t.throws( + () => { + parseExtJSON({input: '{"foo": "bar",,}'}); + }, + descriptions.JSON.REDUNDANT_COMMA.message.value, + ); + + t.throws( + () => { + parseExtJSON({input: '{"foo": "bar", /*comment*/,}'}); + }, + descriptions.JSON.REDUNDANT_COMMA.message.value, + ); + }, ); test( - 'regular JSON', - (t) => { - t.throws( - () => { - parseJSON({input: '{foo: "bar"}'}); - }, - descriptions.JSON.PROPERTY_KEY_UNQUOTED_IN_JSON.message.value, - ); - - t.throws( - () => { - parseJSON({input: '// foobar\ntrue'}); - }, - descriptions.JSON.COMMENTS_IN_JSON.message.value, - ); - - t.throws( - () => { - parseJSON({input: '/* foobar */\ntrue'}); - }, - descriptions.JSON.COMMENTS_IN_JSON.message.value, - ); - - t.throws( - () => { - parseJSON({input: '{"foo": "bar",}'}); - }, - descriptions.JSON.TRAILING_COMMA_IN_JSON.message.value, - ); - - t.throws( - () => { - parseJSON({input: '["foo",]'}); - }, - descriptions.JSON.TRAILING_COMMA_IN_JSON.message.value, - ); - }, + 'regular JSON', + (t) => { + t.throws( + () => { + parseJSON({input: '{foo: "bar"}'}); + }, + descriptions.JSON.PROPERTY_KEY_UNQUOTED_IN_JSON.message.value, + ); + + t.throws( + () => { + parseJSON({input: '// foobar\ntrue'}); + }, + descriptions.JSON.COMMENTS_IN_JSON.message.value, + ); + + t.throws( + () => { + parseJSON({input: '/* foobar */\ntrue'}); + }, + descriptions.JSON.COMMENTS_IN_JSON.message.value, + ); + + t.throws( + () => { + parseJSON({input: '{"foo": "bar",}'}); + }, + descriptions.JSON.TRAILING_COMMA_IN_JSON.message.value, + ); + + t.throws( + () => { + parseJSON({input: '["foo",]'}); + }, + descriptions.JSON.TRAILING_COMMA_IN_JSON.message.value, + ); + }, ); diff --git a/packages/@romejs/codec-json/stringify.test.ts b/packages/@romejs/codec-json/stringify.test.ts index b5d506be7ef..9d3a8ed7967 100644 --- a/packages/@romejs/codec-json/stringify.test.ts +++ b/packages/@romejs/codec-json/stringify.test.ts @@ -13,103 +13,129 @@ import {createUnknownFilePath} from '@romejs/path'; import {Dict} from '@romejs/typescript-helpers'; function consumeExtJSON(opts: ParserOptions) { - return consumeJSONExtra({ - ...opts, - path: createUnknownFilePath('input.rjson'), - }); + return consumeJSONExtra({ + ...opts, + path: createUnknownFilePath('input.rjson'), + }); } test( - 'arrays', - (t) => { - t.is(stringifyRJSONFromConsumer(consumeExtJSON({input: '[]'})), '[]'); - t.is(stringifyRJSONFromConsumer(consumeExtJSON({input: '[1]'})), '[1]'); - t.is(stringifyRJSONFromConsumer(consumeExtJSON({input: '[1,]'})), '[1]'); - t.is( - stringifyRJSONFromConsumer(consumeExtJSON({input: '[1, 2, 3]'})), - '[\n 1\n 2\n 3\n]', - ); - }, + 'arrays', + (t) => { + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '[]'})), + '[]', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '[1]'})), + '[1]', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '[1,]'})), + '[1]', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '[1, 2, 3]'})), + '[\n\t1\n\t2\n\t3\n]', + ); + }, ); test( - 'booleans', - (t) => { - t.is(stringifyRJSONFromConsumer(consumeExtJSON({input: 'true'})), 'true'); - t.is(stringifyRJSONFromConsumer(consumeExtJSON({input: 'false'})), 'false'); - }, + 'booleans', + (t) => { + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: 'true'})), + 'true', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: 'false'})), + 'false', + ); + }, ); test( - 'numbers', - (t) => { - t.is(stringifyRJSONFromConsumer(consumeExtJSON({input: '1'})), '1'); - t.is(stringifyRJSONFromConsumer(consumeExtJSON({input: '12'})), '12'); - t.is(stringifyRJSONFromConsumer(consumeExtJSON({input: '123'})), '123'); - t.is( - stringifyRJSONFromConsumer(consumeExtJSON({input: '123.45'})), - '123.45', - ); - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({input: '1.2341234123412341e+27'}), - ), - '1.2341234123412341e+27', - ); - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({input: '1.2341234123412341E+27'}), - ), - '1.2341234123412341e+27', - ); - }, + 'numbers', + (t) => { + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '1'})), + '1', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '12'})), + '12', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '123'})), + '123', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '123.45'})), + '123.45', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '1.2341234123412341e+27'})), + '1.2341234123412341e+27', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '1.2341234123412341E+27'})), + '1.2341234123412341e+27', + ); + }, ); test( - 'null', - (t) => { - t.is(stringifyRJSONFromConsumer(consumeExtJSON({input: 'null'})), 'null'); + 'null', + (t) => { + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: 'null'})), + 'null', + ); - const funcToNull = consumeExtJSON({input: '1'}); - funcToNull.consumer.setValue(() => {}); - t.is(stringifyRJSONFromConsumer(funcToNull), 'null'); + const funcToNull = consumeExtJSON({input: '1'}); + funcToNull.consumer.setValue(() => {}); + t.inlineSnapshot(stringifyRJSONFromConsumer(funcToNull), 'null'); - const undefinedToNull = consumeExtJSON({input: '1'}); - undefinedToNull.consumer.setValue(undefined); - t.is(stringifyRJSONFromConsumer(undefinedToNull), 'null'); + const undefinedToNull = consumeExtJSON({input: '1'}); + undefinedToNull.consumer.setValue(undefined); + t.inlineSnapshot(stringifyRJSONFromConsumer(undefinedToNull), 'null'); - const NaNToNull = consumeExtJSON({input: '1'}); - NaNToNull.consumer.setValue(NaN); - t.is(stringifyRJSONFromConsumer(NaNToNull), 'NaN'); - }, + const NaNToNull = consumeExtJSON({input: '1'}); + NaNToNull.consumer.setValue(NaN); + t.inlineSnapshot(stringifyRJSONFromConsumer(NaNToNull), 'NaN'); + }, ); test( - 'objects', - (t) => { - t.is(stringifyRJSONFromConsumer(consumeExtJSON({input: '{}'})), '{}'); - t.is( - stringifyRJSONFromConsumer(consumeExtJSON({input: '{"foo":"bar"}'})), - 'foo: "bar"', - ); - t.is( - stringifyRJSONFromConsumer(consumeExtJSON({input: '{"foo":"bar",}'})), - 'foo: "bar"', - ); - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({input: '{"foo":"bar", "bar": "foo"}'}), - ), - 'bar: "foo"\nfoo: "bar"', - ); + 'objects', + (t) => { + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '{}'})), + '{}', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '{"foo":"bar"}'})), + 'foo: "bar"', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '{"foo":"bar",}'})), + 'foo: "bar"', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer( + consumeExtJSON({input: '{"foo":"bar", "bar": "foo"}'}), + ), + 'bar: "foo"\nfoo: "bar"', + ); - // ignore functions and undefined - const ret = consumeExtJSON({input: '{}'}); - ret.consumer.get('foo').setValue('bar'); - ret.consumer.get('func').setValue(function() {}); - ret.consumer.get('undef').setValue(undefined); - t.is(stringifyRJSONFromConsumer(ret), 'foo: "bar"'); - }, + // ignore functions and undefined + const ret = consumeExtJSON({input: '{}'}); + ret.consumer.get('foo').setValue('bar'); + ret.consumer.get('func').setValue(function() {}); + ret.consumer.get('undef').setValue(undefined); + t.inlineSnapshot(stringifyRJSONFromConsumer(ret), 'foo: "bar"'); + }, ); const complexTest = `// root comment @@ -128,148 +154,151 @@ hello: [ 3.53 ]`; test( - 'complex', - (t) => { - const consumer = consumeExtJSON({input: complexTest}); - t.is(stringifyRJSONFromConsumer(consumer), complexTest); - }, + 'complex', + (t) => { + const consumer = consumeExtJSON({input: complexTest}); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumer), + '// root comment\n/* and another!*/\nfoo: {\n\t// comment before property\n\tbar: {nested: true}\n\tgreat: 1.233e+58\n\tyes: null\n}\n// hello!\nhello: [\n\t// comment before element\n\t"world"\n\t2\n\t3.53\n]', + ); + }, ); test( - 'comments', - (t) => { - t.is( - stringifyRJSONFromConsumer(consumeExtJSON({input: '// foo\ntrue'})), - '// foo\ntrue', - ); - t.is( - stringifyRJSONFromConsumer(consumeExtJSON({input: 'true\n// foo'})), - '// foo\ntrue', - ); + 'comments', + (t) => { + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: '// foo\ntrue'})), + '// foo\ntrue', + ); + t.inlineSnapshot( + stringifyRJSONFromConsumer(consumeExtJSON({input: 'true\n// foo'})), + '// foo\ntrue', + ); - //# Comments - loose + //# Comments - loose - // comments at end of object - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({ - input: `{ + // comments at end of object + t.inlineSnapshot( + stringifyRJSONFromConsumer( + consumeExtJSON({ + input: `{ "foo": "bar", // end comment }`, - }), - ), - 'foo: "bar"\n// end comment', - ); - // comments at end of array - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({ - input: `[ + }), + ), + 'foo: "bar"\n// end comment', + ); + // comments at end of array + t.inlineSnapshot( + stringifyRJSONFromConsumer( + consumeExtJSON({ + input: `[ "foobar", // end comment ]`, - }), - ), - '[\n "foobar"\n // end comment\n]', - ); - // comments in empty array - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({ - input: `[ + }), + ), + '[\n\t"foobar"\n\t// end comment\n]', + ); + // comments in empty array + t.inlineSnapshot( + stringifyRJSONFromConsumer( + consumeExtJSON({ + input: `[ // inner comment ]`, - }), - ), - '[\n // inner comment\n]', - ); - // comments in empty object - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({ - input: `{ + }), + ), + '[\n\t// inner comment\n]', + ); + // comments in empty object + t.inlineSnapshot( + stringifyRJSONFromConsumer( + consumeExtJSON({ + input: `{ // inner comment }`, - }), - ), - '{\n // inner comment\n}', - ); + }), + ), + '{\n // inner comment\n}', + ); - //# Comments - object property + //# Comments - object property - // before property - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({ - input: `{ + // before property + t.inlineSnapshot( + stringifyRJSONFromConsumer( + consumeExtJSON({ + input: `{ /* bar */ "foo": "bar", }`, - }), - ), - '/* bar */\nfoo: "bar"', - ); - // before value - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({ - input: `{ + }), + ), + '/* bar */\nfoo: "bar"', + ); + // before value + t.inlineSnapshot( + stringifyRJSONFromConsumer( + consumeExtJSON({ + input: `{ "foo": /* bar */ "bar", }`, - }), - ), - '/* bar */\nfoo: "bar"', - ); - // after value - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({ - input: `{ + }), + ), + '/* bar */\nfoo: "bar"', + ); + // after value + t.inlineSnapshot( + stringifyRJSONFromConsumer( + consumeExtJSON({ + input: `{ "foo": "bar" /* bar */, }`, - }), - ), - '/* bar */\nfoo: "bar"', - ); + }), + ), + '/* bar */\nfoo: "bar"', + ); - //# Comments - array element + //# Comments - array element - // before element - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({ - input: `[ + // before element + t.inlineSnapshot( + stringifyRJSONFromConsumer( + consumeExtJSON({ + input: `[ /* bar */ "foo", ]`, - }), - ), - '[\n /* bar */\n "foo"\n]', - ); - // after value - t.is( - stringifyRJSONFromConsumer( - consumeExtJSON({ - input: `[ + }), + ), + '[\n\t/* bar */\n\t"foo"\n]', + ); + // after value + t.inlineSnapshot( + stringifyRJSONFromConsumer( + consumeExtJSON({ + input: `[ "foo" /* bar */, ]`, - }), - ), - '[\n /* bar */\n "foo"\n]', - ); - }, + }), + ), + '[\n\t/* bar */\n\t"foo"\n]', + ); + }, ); test( - 'recursion', - (t) => { - t.throws(() => { - const ret = consumeExtJSON({input: '{}'}); - const foo: Dict = {}; - foo.bar = foo; - ret.consumer.get('foo').setValue(foo); - stringifyRJSONFromConsumer(ret); - }); - }, + 'recursion', + (t) => { + t.throws(() => { + const ret = consumeExtJSON({input: '{}'}); + const foo: Dict = {}; + foo.bar = foo; + ret.consumer.get('foo').setValue(foo); + stringifyRJSONFromConsumer(ret); + }); + }, ); diff --git a/packages/@romejs/codec-json/stringify.ts b/packages/@romejs/codec-json/stringify.ts index 90dbe38df81..58c3bf293db 100644 --- a/packages/@romejs/codec-json/stringify.ts +++ b/packages/@romejs/codec-json/stringify.ts @@ -13,332 +13,332 @@ import {PRIORITIZE_KEYS, formatNumber} from '@romejs/pretty-format'; import {escapeString} from '@romejs/string-escape'; function joinList( - open: string, - close: string, - indent: string, - items: Array, + open: string, + close: string, + indent: string, + items: Array, ) { - if (items.length === 0) { - return open + close; - } + if (items.length === 0) { + return open + close; + } - if (items.length === 1) { - // Trim to remove indentation - const first = items[0].trim(); + if (items.length === 1) { + // Trim to remove indentation + const first = items[0].trim(); - // We never want to place a comment in between braces because it will break for line comments + // We never want to place a comment in between braces because it will break for line comments - // and look weird for blocks - if (first[0] !== '/') { - return open + first + close; - } - } + // and look weird for blocks + if (first[0] !== '/') { + return open + first + close; + } + } - return [open, ...items, indent + close].join('\n'); + return [open, ...items, indent + close].join('\n'); } function stringifyKey(key: string): string { - if (isValidWord(key)) { - // A property key doesn't need quotes if it's a valid word - return key; - } else { - return escapeString( - key, - { - quote: '"', - ignoreWhitespaceEscapes: true, - json: true, - }, - ); - } + if (isValidWord(key)) { + // A property key doesn't need quotes if it's a valid word + return key; + } else { + return escapeString( + key, + { + quote: '"', + ignoreWhitespaceEscapes: true, + json: true, + }, + ); + } } export function stringifyComments( - indent: string, - comments: Comments, + indent: string, + comments: Comments, ): Array { - return comments.map((node) => { - if (node.type === 'BlockComment') { - return `${indent}/*${node.value}*/`; - } else { - // node.type === 'LineComment' - return `${indent}//${node.value}`; - } - }); + return comments.map((node) => { + if (node.type === 'BlockComment') { + return `${indent}/*${node.value}*/`; + } else { + // node.type === 'LineComment' + return `${indent}//${node.value}`; + } + }); } function stringifyPrimitives(value: unknown): undefined | string { - if (value === null) { - return 'null'; - } - - // Coerce primitive objects to their primitive form, as specified in ECMA262 24.5.2.1 - if ( - value instanceof Number || - value instanceof String || - value instanceof Boolean - ) { - value = value.valueOf(); - } - - // Basic primitive types - switch (typeof value) { - case 'symbol': - case 'function': - case 'undefined': - return 'null'; - - case 'boolean': - return value ? 'true' : 'false'; - - case 'string': - return escapeString( - value, - { - quote: '"', - json: true, - ignoreWhitespaceEscapes: true, - }, - ); - - case 'bigint': - // This is the actual V8 message lol - throw new Error('Do not know how to serialize a BigInt'); - - case 'number': - return formatNumber(value); - } - - return undefined; + if (value === null) { + return 'null'; + } + + // Coerce primitive objects to their primitive form, as specified in ECMA262 24.5.2.1 + if ( + value instanceof Number || + value instanceof String || + value instanceof Boolean + ) { + value = value.valueOf(); + } + + // Basic primitive types + switch (typeof value) { + case 'symbol': + case 'function': + case 'undefined': + return 'null'; + + case 'boolean': + return value ? 'true' : 'false'; + + case 'string': + return escapeString( + value, + { + quote: '"', + json: true, + ignoreWhitespaceEscapes: true, + }, + ); + + case 'bigint': + // This is the actual V8 message lol + throw new Error('Do not know how to serialize a BigInt'); + + case 'number': + return formatNumber(value); + } + + return undefined; } function sortMapKeys(map: Map): Set { - return new Set(Array.from(map.keys()).sort(naturalCompare)); + return new Set(Array.from(map.keys()).sort(naturalCompare)); } function sortMap(map: Map): Map { - const sortedMap: Map = new Map(); - const sortedKeys: Set = sortMapKeys(map); - - // Add any prioritized keys so they're before anything alphabetized - for (const key of PRIORITIZE_KEYS) { - if (sortedKeys.has(key)) { - sortedKeys.delete(key); - - const val = map.get(key); - if (val === undefined) { - throw new Error('Expected value'); - } - - sortedMap.set(key, val); - } - } - - // Now add the rest - for (const key of sortedKeys) { - const val = map.get(key); - if (val === undefined) { - throw new Error('Expected value'); - } - - sortedMap.set(key, val); - } - - return sortedMap; + const sortedMap: Map = new Map(); + const sortedKeys: Set = sortMapKeys(map); + + // Add any prioritized keys so they're before anything alphabetized + for (const key of PRIORITIZE_KEYS) { + if (sortedKeys.has(key)) { + sortedKeys.delete(key); + + const val = map.get(key); + if (val === undefined) { + throw new Error('Expected value'); + } + + sortedMap.set(key, val); + } + } + + // Now add the rest + for (const key of sortedKeys) { + const val = map.get(key); + if (val === undefined) { + throw new Error('Expected value'); + } + + sortedMap.set(key, val); + } + + return sortedMap; } type StringifyOptions = { - comments: PathToComments; - isTopLevel: boolean; - level: number; - stack: Set; + comments: PathToComments; + isTopLevel: boolean; + level: number; + stack: Set; }; type StringifyObjectOptions = StringifyOptions & { - prevIndent: string; - nextIndent: string; + prevIndent: string; + nextIndent: string; }; function getComments(consumer: Consumer, opts: StringifyOptions): PathComments { - const comments = opts.comments.get(consumer.keyPath.join('.')); - if (comments === undefined) { - return { - inner: [], - outer: [], - }; - } else { - return comments; - } + const comments = opts.comments.get(consumer.keyPath.join('.')); + if (comments === undefined) { + return { + inner: [], + outer: [], + }; + } else { + return comments; + } } function stringifyArray(consumer: Consumer, info: StringifyObjectOptions) { - const {level, prevIndent, nextIndent, stack} = info; - - let buff: Array = []; - - const arr = consumer.asArray(); - for (const consumer of arr) { - // Add element comments - const comments = getComments(consumer, info).outer; - buff = buff.concat(stringifyComments(nextIndent, comments)); - - // Add the actual element line - const element = stringifyConsumer( - consumer, - { - comments: info.comments, - isTopLevel: false, - level: level + 1, - stack, - }, - ); - buff.push(`${nextIndent}${element}`); - } - - // Add inner comments - const innerComments = getComments(consumer, info).inner; - buff = buff.concat(stringifyComments(nextIndent, innerComments)); - - return joinList('[', ']', prevIndent, buff); + const {level, prevIndent, nextIndent, stack} = info; + + let buff: Array = []; + + const arr = consumer.asArray(); + for (const consumer of arr) { + // Add element comments + const comments = getComments(consumer, info).outer; + buff = buff.concat(stringifyComments(nextIndent, comments)); + + // Add the actual element line + const element = stringifyConsumer( + consumer, + { + comments: info.comments, + isTopLevel: false, + level: level + 1, + stack, + }, + ); + buff.push(`${nextIndent}${element}`); + } + + // Add inner comments + const innerComments = getComments(consumer, info).inner; + buff = buff.concat(stringifyComments(nextIndent, innerComments)); + + return joinList('[', ']', prevIndent, buff); } function stringifyPlainObject( - consumer: Consumer, - info: StringifyObjectOptions, + consumer: Consumer, + info: StringifyObjectOptions, ): string { - const {level, prevIndent, stack, isTopLevel} = info; - let {nextIndent} = info; - - // Must be an object if we failed all the other conditions - let buff: Array = []; - const map = consumer.asMap(); - - // Remove function, symbol, and undefined properties - for (const [key, consumer] of map) { - const value = consumer.asUnknown(); - - if ( - typeof value === 'function' || - typeof value === 'undefined' || - typeof value === 'symbol' - ) { - map.delete(key); - } - } - - let propLevel = level + 1; - - // We only want to increase the level for properties when we aren't at the top - if (isTopLevel && level === 0) { - propLevel = 0; - nextIndent = ''; - } - - // Build properties - for (const [key, consumer] of sortMap(map)) { - // Add property comments - const comments = getComments(consumer, info).outer; - buff = buff.concat(stringifyComments(nextIndent, comments)); - - // Add the actual property line - const propKey = stringifyKey(key); - const propValue = stringifyConsumer( - consumer, - { - comments: info.comments, - isTopLevel: false, - level: propLevel, - stack, - }, - ); - buff.push(`${nextIndent}${propKey}: ${propValue}`); - } - - // We track this so we know whether we can safely put everything at the top level - - // If we only have comments then there's no way the parser could infer it was originally an object - const hasProps = buff.length > 0; - - // Add inner comments - const innerComments = getComments(consumer, info).inner; - buff = buff.concat(stringifyComments(nextIndent, innerComments)); - - if (level === 0 && isTopLevel) { - if (hasProps) { - return buff.join('\n'); - } else if (buff.length > 0) { - // Otherwise we just have a bunch of comments - // Indent them correctly and just output it as a normal object - buff = buff.map((str) => { - return ` ${str}`; - }); - } - } - - return joinList('{', '}', prevIndent, buff); + const {level, prevIndent, stack, isTopLevel} = info; + let {nextIndent} = info; + + // Must be an object if we failed all the other conditions + let buff: Array = []; + const map = consumer.asMap(); + + // Remove function, symbol, and undefined properties + for (const [key, consumer] of map) { + const value = consumer.asUnknown(); + + if ( + typeof value === 'function' || + typeof value === 'undefined' || + typeof value === 'symbol' + ) { + map.delete(key); + } + } + + let propLevel = level + 1; + + // We only want to increase the level for properties when we aren't at the top + if (isTopLevel && level === 0) { + propLevel = 0; + nextIndent = ''; + } + + // Build properties + for (const [key, consumer] of sortMap(map)) { + // Add property comments + const comments = getComments(consumer, info).outer; + buff = buff.concat(stringifyComments(nextIndent, comments)); + + // Add the actual property line + const propKey = stringifyKey(key); + const propValue = stringifyConsumer( + consumer, + { + comments: info.comments, + isTopLevel: false, + level: propLevel, + stack, + }, + ); + buff.push(`${nextIndent}${propKey}: ${propValue}`); + } + + // We track this so we know whether we can safely put everything at the top level + + // If we only have comments then there's no way the parser could infer it was originally an object + const hasProps = buff.length > 0; + + // Add inner comments + const innerComments = getComments(consumer, info).inner; + buff = buff.concat(stringifyComments(nextIndent, innerComments)); + + if (level === 0 && isTopLevel) { + if (hasProps) { + return buff.join('\n'); + } else if (buff.length > 0) { + // Otherwise we just have a bunch of comments + // Indent them correctly and just output it as a normal object + buff = buff.map((str) => { + return ` ${str}`; + }); + } + } + + return joinList('{', '}', prevIndent, buff); } function stringifyObject( - consumer: Consumer, - value: unknown, - opts: StringifyOptions, + consumer: Consumer, + value: unknown, + opts: StringifyOptions, ) { - const {isTopLevel, level, stack} = opts; - - const info: StringifyObjectOptions = { - comments: opts.comments, - isTopLevel, - nextIndent: ' '.repeat(level + 1), - prevIndent: level === 0 ? '' : ' '.repeat(level - 1), - level, - stack, - }; - - try { - stack.add(value); - - if (Array.isArray(value) || value instanceof Set) { - return stringifyArray(consumer, info); - } - - return stringifyPlainObject(consumer, info); - } finally { - stack.delete(value); - } + const {isTopLevel, level, stack} = opts; + + const info: StringifyObjectOptions = { + comments: opts.comments, + isTopLevel, + nextIndent: '\t'.repeat(level + 1), + prevIndent: level === 0 ? '' : '\t'.repeat(level - 1), + level, + stack, + }; + + try { + stack.add(value); + + if (Array.isArray(value) || value instanceof Set) { + return stringifyArray(consumer, info); + } + + return stringifyPlainObject(consumer, info); + } finally { + stack.delete(value); + } } export function stringifyRootConsumer( - consumer: Consumer, - pathToComments: PathToComments, + consumer: Consumer, + pathToComments: PathToComments, ): string { - const opts: StringifyOptions = { - comments: pathToComments, - isTopLevel: true, - level: 0, - stack: new Set(), - }; - - // Nothing else handles comments at the top level - const inner = stringifyConsumer(consumer, opts); - const comments = getComments(consumer, opts); - const outer = stringifyComments('', comments.outer); - - return [...outer, inner].join('\n'); + const opts: StringifyOptions = { + comments: pathToComments, + isTopLevel: true, + level: 0, + stack: new Set(), + }; + + // Nothing else handles comments at the top level + const inner = stringifyConsumer(consumer, opts); + const comments = getComments(consumer, opts); + const outer = stringifyComments('', comments.outer); + + return [...outer, inner].join('\n'); } function stringifyConsumer(consumer: Consumer, opts: StringifyOptions): string { - const value = consumer.asUnknown(); + const value = consumer.asUnknown(); - // Stringify primitives - const asPrim = stringifyPrimitives(value); - if (asPrim !== undefined) { - return asPrim; - } + // Stringify primitives + const asPrim = stringifyPrimitives(value); + if (asPrim !== undefined) { + return asPrim; + } - // Check if we're already stringfying this value to prevent recursion - if (opts.stack.has(value)) { - throw new TypeError('Recursive'); - } + // Check if we're already stringfying this value to prevent recursion + if (opts.stack.has(value)) { + throw new TypeError('Recursive'); + } - return stringifyObject(consumer, value, opts); + return stringifyObject(consumer, value, opts); } diff --git a/packages/@romejs/codec-json/types.ts b/packages/@romejs/codec-json/types.ts index 0f9c587d1f4..4f1274e4768 100644 --- a/packages/@romejs/codec-json/types.ts +++ b/packages/@romejs/codec-json/types.ts @@ -6,72 +6,72 @@ */ import { - BaseTokens, - ParserOptions, - SimpleToken, - ValueToken, + BaseTokens, + ParserOptions, + SimpleToken, + ValueToken, } from '@romejs/parser-core'; import {ConsumeContext} from '@romejs/consume'; import {DiagnosticCategory} from '@romejs/diagnostics'; export type JSONParserOptions = Omit & { - consumeDiagnosticCategory?: DiagnosticCategory; + consumeDiagnosticCategory?: DiagnosticCategory; }; export type PathComments = { - inner: Comments; - outer: Comments; + inner: Comments; + outer: Comments; }; export type PathToComments = Map; export type LineComment = { - type: 'LineComment'; - value: string; + type: 'LineComment'; + value: string; }; export type BlockComment = { - type: 'BlockComment'; - value: string; + type: 'BlockComment'; + value: string; }; export type Comments = Array; export type JSONParserResult = { - value: JSONValue; - context: Required; + value: JSONValue; + context: Required; }; export type Tokens = BaseTokens & { - BlockComment: ValueToken<'BlockComment', string>; - LineComment: ValueToken<'LineComment', string>; - String: ValueToken<'String', string>; - Number: ValueToken<'Number', number>; - Word: ValueToken<'Word', string>; - BracketOpen: SimpleToken<'BracketOpen'>; - BracketClose: SimpleToken<'BracketClose'>; - BraceOpen: SimpleToken<'BraceOpen'>; - BraceClose: SimpleToken<'BraceClose'>; - Comma: SimpleToken<'Comma'>; - Colon: SimpleToken<'Colon'>; - Dot: SimpleToken<'Dot'>; - Minus: SimpleToken<'Minus'>; - Plus: SimpleToken<'Plus'>; + BlockComment: ValueToken<'BlockComment', string>; + LineComment: ValueToken<'LineComment', string>; + String: ValueToken<'String', string>; + Number: ValueToken<'Number', number>; + Word: ValueToken<'Word', string>; + BracketOpen: SimpleToken<'BracketOpen'>; + BracketClose: SimpleToken<'BracketClose'>; + BraceOpen: SimpleToken<'BraceOpen'>; + BraceClose: SimpleToken<'BraceClose'>; + Comma: SimpleToken<'Comma'>; + Colon: SimpleToken<'Colon'>; + Dot: SimpleToken<'Dot'>; + Minus: SimpleToken<'Minus'>; + Plus: SimpleToken<'Plus'>; }; // export type JSONValue = - | null - | string - | number - | boolean - | JSONObject - | JSONArray; + | null + | string + | number + | boolean + | JSONObject + | JSONArray; export type JSONPropertyValue = undefined | void | JSONValue; export type JSONObject = { - [x: string]: JSONPropertyValue; + [x: string]: JSONPropertyValue; }; export type JSONArray = Array; diff --git a/packages/@romejs/codec-semver/compare.ts b/packages/@romejs/codec-semver/compare.ts index 0de25d163ea..9d3f07ba3fe 100644 --- a/packages/@romejs/codec-semver/compare.ts +++ b/packages/@romejs/codec-semver/compare.ts @@ -13,81 +13,81 @@ import {AbsoluteVersionNode, VersionNode} from './types'; type CompareRet = -1 | 0 | 1; function compareIdentifiers( - a: number | string, - b: undefined | string | number, + a: number | string, + b: undefined | string | number, ): CompareRet { - // Equal - if (b === undefined) { - return 0; - } + // Equal + if (b === undefined) { + return 0; + } - if (typeof a === 'string' || typeof b === 'string') { - // @ts-ignore: built-in def is not restrictive enough - return String(a).localeCompare(String(b)); - } + if (typeof a === 'string' || typeof b === 'string') { + // @ts-ignore: built-in def is not restrictive enough + return String(a).localeCompare(String(b)); + } - // Less than - if (a < b) { - return -1; - } + // Less than + if (a < b) { + return -1; + } - // Greater than - if (a > b) { - return 1; - } + // Greater than + if (a > b) { + return 1; + } - // Equal - return 0; + // Equal + return 0; } function compareMain( - version: AbsoluteVersionNode, - range: VersionNode, + version: AbsoluteVersionNode, + range: VersionNode, ): CompareRet { - return ( - compareIdentifiers(version.major, range.major) || - compareIdentifiers(version.minor, range.minor) || - compareIdentifiers(version.patch, range.patch) - ); + return ( + compareIdentifiers(version.major, range.major) || + compareIdentifiers(version.minor, range.minor) || + compareIdentifiers(version.patch, range.patch) + ); } function comparePre( - version: AbsoluteVersionNode, - range: VersionNode, + version: AbsoluteVersionNode, + range: VersionNode, ): CompareRet { - // NOT having a prerelease is > having one - if (version.prerelease.length > 0 && range.prerelease.length === 0) { - return -1; - } else if (version.prerelease.length === 0 && range.prerelease.length > 0) { - return 1; - } else if (version.prerelease.length === 0 && range.prerelease.length === 0) { - return 0; - } + // NOT having a prerelease is > having one + if (version.prerelease.length > 0 && range.prerelease.length === 0) { + return -1; + } else if (version.prerelease.length === 0 && range.prerelease.length > 0) { + return 1; + } else if (version.prerelease.length === 0 && range.prerelease.length === 0) { + return 0; + } - let i = 0; - do { - const a = version.prerelease[i]; - const b = range.prerelease[i]; + let i = 0; + do { + const a = version.prerelease[i]; + const b = range.prerelease[i]; - if (a === undefined && b === undefined) { - return 0; - } else if (b === undefined) { - return 1; - } else if (a === undefined) { - return -1; - } else if (a === b) { - continue; - } else { - return compareIdentifiers(a, b); - } - } while (++i); + if (a === undefined && b === undefined) { + return 0; + } else if (b === undefined) { + return 1; + } else if (a === undefined) { + return -1; + } else if (a === b) { + continue; + } else { + return compareIdentifiers(a, b); + } + } while (++i); - throw new Error('Unreachable'); + throw new Error('Unreachable'); } export function compareFromAst( - version: AbsoluteVersionNode, - range: VersionNode, + version: AbsoluteVersionNode, + range: VersionNode, ): CompareRet { - return compareMain(version, range) || comparePre(version, range); + return compareMain(version, range) || comparePre(version, range); } diff --git a/packages/@romejs/codec-semver/index.ts b/packages/@romejs/codec-semver/index.ts index ef12c702296..843c3e51fff 100644 --- a/packages/@romejs/codec-semver/index.ts +++ b/packages/@romejs/codec-semver/index.ts @@ -6,18 +6,18 @@ */ import { - AbsoluteVersionNode, - RangeNode, - UserRange, - UserVersion, - UserVersions, + AbsoluteVersionNode, + RangeNode, + UserRange, + UserVersion, + UserVersions, } from './types'; import {satisfiesFromAst} from './satisfies'; import {compareFromAst} from './compare'; import { - SemverParserOptions, - parseSemverRange, - parseSemverVersion, + SemverParserOptions, + parseSemverRange, + parseSemverVersion, } from './parse'; import {normalizeUserRange, normalizeUserVersion} from './utils'; import {DiagnosticsError} from '@romejs/diagnostics'; @@ -32,61 +32,61 @@ export {parseSemverRange, parseSemverVersion}; export {default as stringifySemver} from './stringify'; export function sortSemverVersions( - rawVersions: UserVersions, - opts?: SemverParserOptions, + rawVersions: UserVersions, + opts?: SemverParserOptions, ): Array { - const versions = rawVersions.map((ver) => normalizeUserVersion(ver, opts)); - return versions.sort((a, b) => compareFromAst(a, b)); + const versions = rawVersions.map((ver) => normalizeUserVersion(ver, opts)); + return versions.sort((a, b) => compareFromAst(a, b)); } export function maxSatisfyingSemver( - rawVersions: UserVersions, - rawRange: UserRange, - opts: SemverParserOptions, + rawVersions: UserVersions, + rawRange: UserRange, + opts: SemverParserOptions, ): undefined | AbsoluteVersionNode { - const versions = sortSemverVersions(rawVersions, opts).reverse(); - const range = normalizeUserRange(rawRange, opts); + const versions = sortSemverVersions(rawVersions, opts).reverse(); + const range = normalizeUserRange(rawRange, opts); - for (const version of versions) { - if (satisfiesFromAst(version, range)) { - return version; - } - } + for (const version of versions) { + if (satisfiesFromAst(version, range)) { + return version; + } + } - return undefined; + return undefined; } export function minSatisfyingSemver( - rawVersions: UserVersions, - rawRange: UserRange, - opts?: SemverParserOptions, + rawVersions: UserVersions, + rawRange: UserRange, + opts?: SemverParserOptions, ): undefined | AbsoluteVersionNode { - const versions = sortSemverVersions(rawVersions, opts); - const range = normalizeUserRange(rawRange, opts); + const versions = sortSemverVersions(rawVersions, opts); + const range = normalizeUserRange(rawRange, opts); - for (const version of versions) { - if (satisfiesFromAst(version, range)) { - return version; - } - } + for (const version of versions) { + if (satisfiesFromAst(version, range)) { + return version; + } + } - return undefined; + return undefined; } export function satisfiesSemver( - rawVersion: UserVersion, - rawRange: UserRange, - opts?: SemverParserOptions, + rawVersion: UserVersion, + rawRange: UserRange, + opts?: SemverParserOptions, ) { - try { - const version = normalizeUserVersion(rawVersion, opts); - const range = normalizeUserRange(rawRange, opts); - return satisfiesFromAst(version, range); - } catch (err) { - if (err instanceof DiagnosticsError) { - return false; - } else { - throw err; - } - } + try { + const version = normalizeUserVersion(rawVersion, opts); + const range = normalizeUserRange(rawRange, opts); + return satisfiesFromAst(version, range); + } catch (err) { + if (err instanceof DiagnosticsError) { + return false; + } else { + throw err; + } + } } diff --git a/packages/@romejs/codec-semver/parse.test.md b/packages/@romejs/codec-semver/parse.test.md index 76a5afe7ffa..15173a00feb 100644 --- a/packages/@romejs/codec-semver/parse.test.md +++ b/packages/@romejs/codec-semver/parse.test.md @@ -8,24 +8,24 @@ ```javascript AbsoluteVersion { - build: Array [] - major: 1 - minor: 2 - patch: 3 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: 2 + patch: 3 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -33,24 +33,24 @@ AbsoluteVersion { ```javascript AbsoluteVersion { - build: Array [] - major: 1 - minor: 2 - patch: 3 - prerelease: Array ['prerelease'] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: 2 + patch: 3 + prerelease: Array ['prerelease'] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -58,54 +58,54 @@ AbsoluteVersion { ```javascript VersionRange { - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Wildcard { - loc: Object { - filename: undefined - end: Object { - column: 1 - index: 1 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - } - right: AbsoluteVersion { - build: Array [] - major: 1 - minor: 2 - patch: 3 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 4 - index: 4 - line: 1 - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Wildcard { + loc: Object { + filename: undefined + end: Object { + column: 1 + index: 1 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + } + right: AbsoluteVersion { + build: Array [] + major: 1 + minor: 2 + patch: 3 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 4 + index: 4 + line: 1 + } + } + } } ``` @@ -113,24 +113,24 @@ VersionRange { ```javascript AbsoluteVersion { - build: Array [] - major: 1 - minor: 2 - patch: 3 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: 2 + patch: 3 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -138,19 +138,19 @@ AbsoluteVersion { ```javascript Wildcard { - loc: Object { - filename: undefined - end: Object { - column: 0 - index: 0 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + loc: Object { + filename: undefined + end: Object { + column: 0 + index: 0 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -158,19 +158,19 @@ Wildcard { ```javascript Wildcard { - loc: Object { - filename: undefined - end: Object { - column: 0 - index: 0 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + loc: Object { + filename: undefined + end: Object { + column: 0 + index: 0 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -178,24 +178,24 @@ Wildcard { ```javascript AbsoluteVersion { - build: Array [] - major: 1 - minor: 2 - patch: 3 - prerelease: Array ['prerelease'] - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: 2 + patch: 3 + prerelease: Array ['prerelease'] + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -203,24 +203,24 @@ AbsoluteVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 0 - index: 0 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 0 + index: 0 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -228,24 +228,24 @@ WildcardVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: 2 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: 2 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -253,24 +253,24 @@ WildcardVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -278,24 +278,24 @@ WildcardVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: 3 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: undefined + patch: 3 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -303,24 +303,24 @@ WildcardVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: 2 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: 2 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -328,27 +328,27 @@ WildcardVersion { ```javascript AbsoluteVersion { - build: Array [] - major: 1 - minor: 2 - patch: 3 - prerelease: Array [ - 'pre' - 2 - ] - loc: Object { - filename: undefined - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: 2 + patch: 3 + prerelease: Array [ + 'pre' + 2 + ] + loc: Object { + filename: undefined + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -356,24 +356,24 @@ AbsoluteVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -381,24 +381,24 @@ WildcardVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: 3 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: undefined + patch: 3 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -406,24 +406,24 @@ WildcardVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: 2 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: 2 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -431,24 +431,24 @@ WildcardVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -456,24 +456,24 @@ WildcardVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: 3 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: undefined + patch: 3 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -481,24 +481,24 @@ WildcardVersion { ```javascript WildcardVersion { - build: Array [] - major: 1 - minor: 2 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: 2 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -506,59 +506,59 @@ WildcardVersion { ```javascript VersionRange { - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: AbsoluteVersion { - build: Array [] - major: 1 - minor: 2 - patch: 3 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - } - right: AbsoluteVersion { - build: Array [] - major: 1 - minor: 2 - patch: 4 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: AbsoluteVersion { + build: Array [] + major: 1 + minor: 2 + patch: 3 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + } + right: AbsoluteVersion { + build: Array [] + major: 1 + minor: 2 + patch: 4 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } } ``` @@ -566,59 +566,59 @@ VersionRange { ```javascript LogicalOr { - loc: Object { - filename: undefined - end: Object { - column: 7 - index: 7 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: WildcardVersion { - build: Array [] - major: 1 - minor: 2 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - } - right: WildcardVersion { - build: Array [] - major: 3 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 7 - index: 7 - line: 1 - } - start: Object { - column: 7 - index: 7 - line: 1 - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 7 + index: 7 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: WildcardVersion { + build: Array [] + major: 1 + minor: 2 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + } + right: WildcardVersion { + build: Array [] + major: 3 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 7 + index: 7 + line: 1 + } + start: Object { + column: 7 + index: 7 + line: 1 + } + } + } } ``` @@ -626,94 +626,94 @@ LogicalOr { ```javascript LogicalOr { - loc: Object { - filename: undefined - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 1 - index: 1 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - } - right: LogicalOr { - loc: Object { - filename: undefined - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 5 - index: 5 - line: 1 - } - } - left: WildcardVersion { - build: Array [] - major: 2 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 5 - index: 5 - line: 1 - } - } - } - right: WildcardVersion { - build: Array [] - major: 3 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 10 - index: 10 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 1 + index: 1 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + } + right: LogicalOr { + loc: Object { + filename: undefined + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 5 + index: 5 + line: 1 + } + } + left: WildcardVersion { + build: Array [] + major: 2 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 5 + index: 5 + line: 1 + } + } + } + right: WildcardVersion { + build: Array [] + major: 3 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 10 + index: 10 + line: 1 + } + } + } + } } ``` @@ -721,40 +721,40 @@ LogicalOr { ```javascript Comparator { - operator: '>=' - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } + operator: '>=' + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } } ``` @@ -762,31 +762,31 @@ Comparator { ```javascript AbsoluteVersion { - build: Array [] - major: 1 - minor: 2 - patch: 3 - prerelease: Array [ - 'pre' - 2 - 3 - 4 - 5 - 'foo' - ] - loc: Object { - filename: undefined - end: Object { - column: 18 - index: 18 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + build: Array [] + major: 1 + minor: 2 + patch: 3 + prerelease: Array [ + 'pre' + 2 + 3 + 4 + 5 + 'foo' + ] + loc: Object { + filename: undefined + end: Object { + column: 18 + index: 18 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -794,91 +794,91 @@ AbsoluteVersion { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 14 - index: 14 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '>=' - loc: Object { - filename: undefined - end: Object { - column: 7 - index: 7 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 7 - index: 7 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } - } - right: Comparator { - operator: '>=' - loc: Object { - filename: undefined - end: Object { - column: 14 - index: 14 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 14 - index: 14 - line: 1 - } - start: Object { - column: 10 - index: 10 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 14 + index: 14 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '>=' + loc: Object { + filename: undefined + end: Object { + column: 7 + index: 7 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 7 + index: 7 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } + } + right: Comparator { + operator: '>=' + loc: Object { + filename: undefined + end: Object { + column: 14 + index: 14 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 14 + index: 14 + line: 1 + } + start: Object { + column: 10 + index: 10 + line: 1 + } + } + } + } } ``` @@ -886,40 +886,40 @@ LogicalAnd { ```javascript Comparator { - operator: '<=' - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } + operator: '<=' + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } } ``` @@ -927,91 +927,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 14 - index: 14 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '<=' - loc: Object { - filename: undefined - end: Object { - column: 7 - index: 7 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 7 - index: 7 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } - } - right: Comparator { - operator: '<=' - loc: Object { - filename: undefined - end: Object { - column: 14 - index: 14 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 14 - index: 14 - line: 1 - } - start: Object { - column: 10 - index: 10 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 14 + index: 14 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '<=' + loc: Object { + filename: undefined + end: Object { + column: 7 + index: 7 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 7 + index: 7 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } + } + right: Comparator { + operator: '<=' + loc: Object { + filename: undefined + end: Object { + column: 14 + index: 14 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 14 + index: 14 + line: 1 + } + start: Object { + column: 10 + index: 10 + line: 1 + } + } + } + } } ``` @@ -1019,40 +1019,40 @@ LogicalAnd { ```javascript Comparator { - operator: '>' - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '>' + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -1060,91 +1060,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '>' - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '>' - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 7 - index: 7 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '>' + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '>' + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 7 + index: 7 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + } } ``` @@ -1152,40 +1152,40 @@ LogicalAnd { ```javascript Comparator { - operator: '<' - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '<' + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -1193,91 +1193,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '<' - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '<' - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 7 - index: 7 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '<' + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '<' + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 7 + index: 7 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + } } ``` @@ -1285,40 +1285,40 @@ LogicalAnd { ```javascript Comparator { - operator: '^' - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '^' + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -1326,91 +1326,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '^' - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '^' - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 7 - index: 7 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '^' + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '^' + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 7 + index: 7 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + } } ``` @@ -1418,40 +1418,40 @@ LogicalAnd { ```javascript Comparator { - operator: '~' - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '~' + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -1459,24 +1459,24 @@ Comparator { ```javascript AbsoluteVersion { - major: 1 - minor: 2 - patch: 3 - prerelease: Array [] - build: Array ['build'] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + major: 1 + minor: 2 + patch: 3 + prerelease: Array [] + build: Array ['build'] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -1484,91 +1484,91 @@ AbsoluteVersion { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '~' - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '~' - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 7 - index: 7 - line: 1 - } - } - version: AbsoluteVersion { - build: Array [] - major: 1 - minor: 4 - patch: 5 - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '~' + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '~' + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 7 + index: 7 + line: 1 + } + } + version: AbsoluteVersion { + build: Array [] + major: 1 + minor: 4 + patch: 5 + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + } } ``` @@ -1576,40 +1576,40 @@ LogicalAnd { ```javascript Comparator { - operator: '>=' - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } + operator: '>=' + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } } ``` @@ -1617,91 +1617,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '>=' - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } - } - right: Comparator { - operator: '>=' - loc: Object { - filename: undefined - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 6 - index: 6 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '>=' + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } + } + right: Comparator { + operator: '>=' + loc: Object { + filename: undefined + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 6 + index: 6 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + } } ``` @@ -1709,40 +1709,40 @@ LogicalAnd { ```javascript Comparator { - operator: '<=' - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } + operator: '<=' + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } } ``` @@ -1750,91 +1750,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '<=' - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 5 - index: 5 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } - } - right: Comparator { - operator: '<=' - loc: Object { - filename: undefined - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 6 - index: 6 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '<=' + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 5 + index: 5 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } + } + right: Comparator { + operator: '<=' + loc: Object { + filename: undefined + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 6 + index: 6 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + } } ``` @@ -1842,40 +1842,40 @@ LogicalAnd { ```javascript Comparator { - operator: '>' - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '>' + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -1883,91 +1883,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '>' - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '>' - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 5 - index: 5 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 6 - index: 6 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '>' + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '>' + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 5 + index: 5 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 6 + index: 6 + line: 1 + } + } + } + } } ``` @@ -1975,40 +1975,40 @@ LogicalAnd { ```javascript Comparator { - operator: '<' - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '<' + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -2016,91 +2016,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '<' - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '<' - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 5 - index: 5 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 6 - index: 6 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '<' + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '<' + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 5 + index: 5 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 6 + index: 6 + line: 1 + } + } + } + } } ``` @@ -2108,40 +2108,40 @@ LogicalAnd { ```javascript Comparator { - operator: '^' - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '^' + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -2149,24 +2149,24 @@ Comparator { ```javascript AbsoluteVersion { - major: 1 - minor: 2 - patch: 3 - build: Array ['build'] - prerelease: Array ['prerelease'] - loc: Object { - filename: undefined - end: Object { - column: 17 - index: 17 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + major: 1 + minor: 2 + patch: 3 + build: Array ['build'] + prerelease: Array ['prerelease'] + loc: Object { + filename: undefined + end: Object { + column: 17 + index: 17 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -2174,91 +2174,91 @@ AbsoluteVersion { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '^' - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '^' - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 5 - index: 5 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 6 - index: 6 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '^' + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '^' + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 5 + index: 5 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 6 + index: 6 + line: 1 + } + } + } + } } ``` @@ -2266,40 +2266,40 @@ LogicalAnd { ```javascript Comparator { - operator: '~' - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '~' + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -2307,91 +2307,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '~' - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '~' - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 5 - index: 5 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: 4 - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 8 - index: 8 - line: 1 - } - start: Object { - column: 6 - index: 6 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '~' + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '~' + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 5 + index: 5 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: 4 + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 8 + index: 8 + line: 1 + } + start: Object { + column: 6 + index: 6 + line: 1 + } + } + } + } } ``` @@ -2399,40 +2399,40 @@ LogicalAnd { ```javascript Comparator { - operator: '>=' - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } + operator: '>=' + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } } ``` @@ -2440,91 +2440,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '>=' - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } - } - right: Comparator { - operator: '>=' - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 4 - index: 4 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 6 - index: 6 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '>=' + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } + } + right: Comparator { + operator: '>=' + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 4 + index: 4 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 6 + index: 6 + line: 1 + } + } + } + } } ``` @@ -2532,40 +2532,40 @@ LogicalAnd { ```javascript Comparator { - operator: '<=' - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } + operator: '<=' + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } } ``` @@ -2573,91 +2573,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '<=' - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 3 - index: 3 - line: 1 - } - start: Object { - column: 2 - index: 2 - line: 1 - } - } - } - } - right: Comparator { - operator: '<=' - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 4 - index: 4 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 6 - index: 6 - line: 1 - } - start: Object { - column: 6 - index: 6 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '<=' + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 3 + index: 3 + line: 1 + } + start: Object { + column: 2 + index: 2 + line: 1 + } + } + } + } + right: Comparator { + operator: '<=' + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 4 + index: 4 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 6 + index: 6 + line: 1 + } + start: Object { + column: 6 + index: 6 + line: 1 + } + } + } + } } ``` @@ -2665,40 +2665,40 @@ LogicalAnd { ```javascript Comparator { - operator: '>' - loc: Object { - filename: undefined - end: Object { - column: 1 - index: 1 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 1 - index: 1 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '>' + loc: Object { + filename: undefined + end: Object { + column: 1 + index: 1 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 1 + index: 1 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -2706,91 +2706,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '>' - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '>' - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 3 - index: 3 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 4 - index: 4 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '>' + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '>' + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 3 + index: 3 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 4 + index: 4 + line: 1 + } + } + } + } } ``` @@ -2798,40 +2798,40 @@ LogicalAnd { ```javascript Comparator { - operator: '<' - loc: Object { - filename: undefined - end: Object { - column: 1 - index: 1 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 1 - index: 1 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '<' + loc: Object { + filename: undefined + end: Object { + column: 1 + index: 1 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 1 + index: 1 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -2839,27 +2839,27 @@ Comparator { ```javascript AbsoluteVersion { - major: 1 - minor: 2 - patch: 3 - build: Array ['build'] - prerelease: Array [ - 'pre' - 2 - ] - loc: Object { - filename: undefined - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + major: 1 + minor: 2 + patch: 3 + build: Array ['build'] + prerelease: Array [ + 'pre' + 2 + ] + loc: Object { + filename: undefined + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -2867,91 +2867,91 @@ AbsoluteVersion { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '<' - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '<' - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 3 - index: 3 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 4 - index: 4 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '<' + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '<' + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 3 + index: 3 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 4 + index: 4 + line: 1 + } + } + } + } } ``` @@ -2959,40 +2959,40 @@ LogicalAnd { ```javascript Comparator { - operator: '^' - loc: Object { - filename: undefined - end: Object { - column: 1 - index: 1 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 1 - index: 1 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '^' + loc: Object { + filename: undefined + end: Object { + column: 1 + index: 1 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 1 + index: 1 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -3000,91 +3000,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '^' - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '^' - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 3 - index: 3 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 4 - index: 4 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '^' + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '^' + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 3 + index: 3 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 4 + index: 4 + line: 1 + } + } + } + } } ``` @@ -3092,40 +3092,40 @@ LogicalAnd { ```javascript Comparator { - operator: '~' - loc: Object { - filename: undefined - end: Object { - column: 1 - index: 1 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 1 - index: 1 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } + operator: '~' + loc: Object { + filename: undefined + end: Object { + column: 1 + index: 1 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 1 + index: 1 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } } ``` @@ -3133,91 +3133,91 @@ Comparator { ```javascript LogicalAnd { - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - left: Comparator { - operator: '~' - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 2 - index: 2 - line: 1 - } - start: Object { - column: 1 - index: 1 - line: 1 - } - } - } - } - right: Comparator { - operator: '~' - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 3 - index: 3 - line: 1 - } - } - version: WildcardVersion { - build: Array [] - major: 1 - minor: undefined - patch: undefined - prerelease: Array [] - loc: Object { - filename: undefined - end: Object { - column: 4 - index: 4 - line: 1 - } - start: Object { - column: 4 - index: 4 - line: 1 - } - } - } - } + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + left: Comparator { + operator: '~' + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 2 + index: 2 + line: 1 + } + start: Object { + column: 1 + index: 1 + line: 1 + } + } + } + } + right: Comparator { + operator: '~' + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 3 + index: 3 + line: 1 + } + } + version: WildcardVersion { + build: Array [] + major: 1 + minor: undefined + patch: undefined + prerelease: Array [] + loc: Object { + filename: undefined + end: Object { + column: 4 + index: 4 + line: 1 + } + start: Object { + column: 4 + index: 4 + line: 1 + } + } + } + } } ``` @@ -3225,31 +3225,31 @@ LogicalAnd { ```javascript AbsoluteVersion { - major: 1 - minor: 2 - patch: 3 - build: Array ['build'] - prerelease: Array [ - 'pre' - 2 - 3 - 4 - 5 - 'foo' - ] - loc: Object { - filename: undefined - end: Object { - column: 22 - index: 22 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + major: 1 + minor: 2 + patch: 3 + build: Array ['build'] + prerelease: Array [ + 'pre' + 2 + 3 + 4 + 5 + 'foo' + ] + loc: Object { + filename: undefined + end: Object { + column: 22 + index: 22 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -3257,37 +3257,37 @@ AbsoluteVersion { ```javascript AbsoluteVersion { - major: 1 - minor: 2 - patch: 3 - build: Array [ - 'build' - 2 - 3 - 4 - 'foo' - ] - prerelease: Array [ - 'pre' - 2 - 3 - 4 - 5 - 'foo' - ] - loc: Object { - filename: undefined - end: Object { - column: 34 - index: 34 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + major: 1 + minor: 2 + patch: 3 + build: Array [ + 'build' + 2 + 3 + 4 + 'foo' + ] + prerelease: Array [ + 'pre' + 2 + 3 + 4 + 5 + 'foo' + ] + loc: Object { + filename: undefined + end: Object { + column: 34 + index: 34 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -3295,26 +3295,26 @@ AbsoluteVersion { ```javascript AbsoluteVersion { - major: 1 - minor: 2 - patch: 3 - build: Array ['45build'] - prerelease: Array [ - '45pre' - '42yes' - ] - loc: Object { - filename: undefined - end: Object { - column: 20 - index: 20 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + major: 1 + minor: 2 + patch: 3 + build: Array ['45build'] + prerelease: Array [ + '45pre' + '42yes' + ] + loc: Object { + filename: undefined + end: Object { + column: 20 + index: 20 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` diff --git a/packages/@romejs/codec-semver/parse.test.ts b/packages/@romejs/codec-semver/parse.test.ts index 0f832ab7a9b..a0f1d919f5a 100644 --- a/packages/@romejs/codec-semver/parse.test.ts +++ b/packages/@romejs/codec-semver/parse.test.ts @@ -10,93 +10,93 @@ import {parseSemverRange, parseSemverVersion} from '@romejs/codec-semver'; import {test} from 'rome'; test( - 'parse', - async (t) => { - // versions in version mode - const versionTests = [ - '1.2.3', - '1.2.3-prerelease', - '1.2.3-pre.2', - '1.2.3-pre.2.3.4.5.foo', - '1.2.3+build', - '1.2.3-prerelease+build', - '1.2.3-pre.2+build', - '1.2.3-pre.2.3.4.5.foo+build', - '1.2.3-pre.2.3.4.5.foo+build.2.3.4.foo', - '1.2.3-45pre.42yes+45build', - ]; - for (const str of versionTests) { - t.snapshot(parseSemverVersion({input: str})); - } + 'parse', + async (t) => { + // versions in version mode + const versionTests = [ + '1.2.3', + '1.2.3-prerelease', + '1.2.3-pre.2', + '1.2.3-pre.2.3.4.5.foo', + '1.2.3+build', + '1.2.3-prerelease+build', + '1.2.3-pre.2+build', + '1.2.3-pre.2.3.4.5.foo+build', + '1.2.3-pre.2.3.4.5.foo+build.2.3.4.foo', + '1.2.3-45pre.42yes+45build', + ]; + for (const str of versionTests) { + t.snapshot(parseSemverVersion({input: str})); + } - // loose mode - const looseRangeTests = ['* - 1.2.3', 'v1.2.3', '||', '', '1.2.3prerelease']; - for (const str of looseRangeTests) { - t.snapshot(parseSemverRange({input: str, loose: true})); + // loose mode + const looseRangeTests = ['* - 1.2.3', 'v1.2.3', '||', '', '1.2.3prerelease']; + for (const str of looseRangeTests) { + t.snapshot(parseSemverRange({input: str, loose: true})); - t.throws(() => { - parseSemverRange({input: str, loose: false}); - }); - } + t.throws(() => { + parseSemverRange({input: str, loose: false}); + }); + } - // ranges in range mode - const rangeTests = [ - // partial versions - '1', - '1.2', - // wildcards - '1.*', - '1.*.3', - '1.2.*', - '1.x', - '1.x.3', - '1.2.x', - '1.X', - '1.X.3', - '1.2.X', - // ranges - '1.2.3 - 1.2.4', - // or - '1.2 || 3', - '1 || 2 || 3', - ]; + // ranges in range mode + const rangeTests = [ + // partial versions + '1', + '1.2', + // wildcards + '1.*', + '1.*.3', + '1.2.*', + '1.x', + '1.x.3', + '1.2.x', + '1.X', + '1.X.3', + '1.2.X', + // ranges + '1.2.3 - 1.2.4', + // or + '1.2 || 3', + '1 || 2 || 3', + ]; - // operators in range mode - const operatorTests = [ - '>=1.4.5', - '<=1.4.5', - '>1.4.5', - '<1.4.5', - '^1.4.5', - '~1.4.5', - '>=1.4', - '<=1.4', - '>1.4', - '<1.4', - '^1.4', - '~1.4', - '>=1', - '<=1', - '>1', - '<1', - '^1', - '~1', - ]; - for (const op of operatorTests) { - rangeTests.push(op); - rangeTests.push(`${op} ${op}`); - } + // operators in range mode + const operatorTests = [ + '>=1.4.5', + '<=1.4.5', + '>1.4.5', + '<1.4.5', + '^1.4.5', + '~1.4.5', + '>=1.4', + '<=1.4', + '>1.4', + '<1.4', + '^1.4', + '~1.4', + '>=1', + '<=1', + '>1', + '<1', + '^1', + '~1', + ]; + for (const op of operatorTests) { + rangeTests.push(op); + rangeTests.push(`${op} ${op}`); + } - // run range tests - for (const str of rangeTests) { - t.snapshot(parseSemverRange({input: str})); - } + // run range tests + for (const str of rangeTests) { + t.snapshot(parseSemverRange({input: str})); + } - // ensure ranges throw in version mode - for (const str of rangeTests) { - t.throws(() => { - parseSemverVersion({input: str}); - }); - } - }, + // ensure ranges throw in version mode + for (const str of rangeTests) { + t.throws(() => { + parseSemverVersion({input: str}); + }); + } + }, ); diff --git a/packages/@romejs/codec-semver/parse.ts b/packages/@romejs/codec-semver/parse.ts index 7461e9f9d59..65a8eb8822c 100644 --- a/packages/@romejs/codec-semver/parse.ts +++ b/packages/@romejs/codec-semver/parse.ts @@ -6,24 +6,24 @@ */ import { - AbsoluteVersionNode, - ComparatorNode, - ComparatorOperator, - LogicalAndNode, - LogicalOrNode, - RangeNode, - Tokens, - VersionNode, - VersionPrereleaseParts, - VersionRangeNode, - WildcardNode, + AbsoluteVersionNode, + ComparatorNode, + ComparatorOperator, + LogicalAndNode, + LogicalOrNode, + RangeNode, + Tokens, + VersionNode, + VersionPrereleaseParts, + VersionRangeNode, + WildcardNode, } from './types'; import { - ParserOptions, - TokenValues, - createParser, - isAlpha, - isDigit, + ParserOptions, + TokenValues, + createParser, + isAlpha, + isDigit, } from '@romejs/parser-core'; import {Number0, ob1Add, ob1Get0} from '@romejs/ob1'; @@ -32,460 +32,460 @@ import {descriptions} from '@romejs/diagnostics'; type ParseMode = 'version' | 'range'; export type SemverParserOptions = ParserOptions & { - loose?: boolean; + loose?: boolean; }; const createSemverParser = createParser((ParserCore) => - class SemverParser extends ParserCore { - constructor({loose, ...opts}: SemverParserOptions, mode: ParseMode) { - super(opts, 'parse/semver'); - this.input = this.input.trimRight(); - this.mode = mode; - this.loose = loose === undefined ? false : loose; - } - - loose: boolean; - mode: ParseMode; - - // For some reason Flow will throw an error without the type casts... - tokenize(index: Number0, input: string): undefined | TokenValues { - const char = input[ob1Get0(index)]; - const nextChar = input[ob1Get0(index) + 1]; - - if ( - (char === '<' && nextChar === '=') || - (char === '>' && nextChar === '=') || - (char === '~' && nextChar === '>') - ) { - // @ts-ignore: TS doesn't infer the possible combinations - const value: ComparatorOperator = char + nextChar; - return this.finishValueToken('Operator', value, ob1Add(index, 2)); - } - - if ( - char === '^' || - char === '<' || - char === '>' || - char === '~' || - char === '=' - ) { - const op: ComparatorOperator = char; - return this.finishValueToken('Operator', op); - } - - if (char === '|' && nextChar === '|') { - return this.finishToken('Pipe', ob1Add(index, 2)); - } - - if (char === '*') { - return this.finishToken('Star'); - } - - if (input[ob1Get0(index) - 1] === ' ' && char === '-' && nextChar === ' ') { - return this.finishToken('RangeDash'); - } - - if (char === '-') { - return this.finishToken('Dash'); - } - - if (char === '+') { - return this.finishToken('Plus'); - } - - if (char === '.') { - return this.finishToken('Dot'); - } - - if (isDigit(char)) { - const [value] = this.readInputFrom(index, isDigit); - return this.finishValueToken( - 'Number', - Number(value), - ob1Add(index, value.length), - ); - } - - if (isAlpha(char)) { - const [value] = this.readInputFrom(index, isAlpha); - return this.finishValueToken('Word', value, ob1Add(index, value.length)); - } - - if (char === ' ' || char === '\t') { - return this.finishToken('Space'); - } - - // Unknown character - return undefined; - } - - // Remove all subsequent space tokens - eatSpaceToken() { - while (this.eatToken('Space') !== undefined) { - // empty - } - } - - parseVersionOrWildcard(): WildcardNode | VersionNode { - const startPos = this.getPosition(); - const startToken = this.getToken(); - const version = this.parseVersion(); - - // We should return a bare wildcard when parsed in a version position if there was nothing else attached - if ( - this.isWildcardToken(startToken) && - version.minor === undefined && - version.patch === undefined && - version.prerelease.length === 0 && - version.build.length === 0 - ) { - return { - type: 'Wildcard', - loc: this.finishLoc(startPos), - }; - } - - return version; - } - - parseVersion(): VersionNode { - const startPos = this.getPosition(); - const startToken = this.getToken(); - - if (this.isVersionCharacter(startToken)) { - this.nextToken(); - } - - const major = this.parseVersionNumber(); - let minor = undefined; - let patch = undefined; - - if (this.eatToken('Dot')) { - minor = this.parseVersionNumber(); - } else if (this.mode === 'version') { - throw this.unexpected({ - description: descriptions.SEMVER.MISSING_MINOR_VERSION, - }); - } - - if (this.eatToken('Dot')) { - patch = this.parseVersionNumber(); - } else if (this.mode === 'version') { - throw this.unexpected({ - description: descriptions.SEMVER.MISSING_PATCH_VERSION, - }); - } - - if (this.matchToken('Dot')) { - throw this.unexpected({ - description: descriptions.SEMVER.EXCESSIVE_VERSION_PARTS, - }); - } - - // The dash is optional in loose mode. eg. 1.2.3pre - let prerelease: VersionPrereleaseParts = []; - if (this.eatToken('Dash') || (this.loose && this.matchToken('Word'))) { - prerelease = this.parseVersionQualifierParts(); - } - - let build: VersionPrereleaseParts = []; - if (this.eatToken('Plus')) { - build = this.parseVersionQualifierParts(); - } - - if (major !== undefined && minor !== undefined && patch !== undefined) { - return { - type: 'AbsoluteVersion', - loc: this.finishLoc(startPos), - major, - minor, - patch, - prerelease, - build, - }; - } else { - return { - type: 'WildcardVersion', - loc: this.finishLoc(startPos), - major, - minor, - patch, - prerelease, - build, - }; - } - } - - parseVersionQualifierParts(): VersionPrereleaseParts { - const parts: VersionPrereleaseParts = []; - do { - parts.push(this.parseVersionQualifierPart()); - } while (this.eatToken('Dot') !== undefined); - return parts; - } - - parseVersionQualifierPart(): string | number { - const parts: Array = []; - - do { - const token = this.getToken(); - - if (token.type === 'Number' || token.type === 'Word') { - this.nextToken(); - parts.push(token.value); - } else if (token.type === 'Dash') { - this.nextToken(); - parts.push('-'); - } else { - throw this.unexpected({ - description: descriptions.SEMVER.INVALID_QUANTIFIER_PART, - }); - } - } while ( - this.matchToken('Number') || - this.matchToken('Word') || - this.matchToken('Dash') - ); - - if (parts.length === 1 && typeof parts[0] === 'number') { - return parts[0]; - } else { - return parts.join(''); - } - } - - isWildcardToken(token: TokenValues): boolean { - if (token.type === 'Star') { - return true; - } - - if (token.type === 'Word') { - return token.value === 'x' || token.value === 'X'; - } - - return false; - } - - parseVersionNumber(): undefined | number { - const token = this.getToken(); - - if (token.type === 'Number') { - this.nextToken(); - return token.value; - } - - if (this.isWildcardToken(token)) { - if (this.mode === 'version') { - throw this.unexpected({ - description: descriptions.SEMVER.WILDCARD_IN_VERSION, - }); - } - - this.nextToken(); - } else { - throw this.unexpected({ - description: descriptions.SEMVER.INVALID_VERSION_NUMBER, - }); - } - - return undefined; - } - - parseLogicalOr(left: RangeNode): LogicalOrNode { - this.nextToken(); - this.eatSpaceToken(); - - const right = this.parseExpression(); - return { - loc: this.finishLoc(this.getLoc(left).start), - type: 'LogicalOr', - left, - right, - }; - } - - validateRangeSide(node: RangeNode): VersionNode | WildcardNode { - // In loose mode, we allow ranges to be a bare wildcard instead of a version - // eg. * - 1.2.3 - if (node.type === 'WildcardVersion' || node.type === 'AbsoluteVersion') { - return node; - } - - if (node.type === 'Wildcard' && this.loose) { - return node; - } - - throw this.unexpected({ - ...descriptions.SEMVER.INVALID_RANGE, - start: this.getLoc(node).start, - }); - } - - parseVersionRange(left: RangeNode): VersionRangeNode { - this.nextToken(); - this.eatSpaceToken(); - - const right = this.parseVersionOrWildcard(); - - return { - type: 'VersionRange', - loc: this.finishLoc(this.getLoc(left).start), - left: this.validateRangeSide(left), - right: this.validateRangeSide(right), - }; - } - - parseWildcard(): WildcardNode { - const startPos = this.getPosition(); - this.nextToken(); - return {type: 'Wildcard', loc: this.finishLoc(startPos)}; - } - - parseAtomOperator(token: Tokens['Operator']): ComparatorNode { - const startPos = this.getPosition(); - this.nextToken(); - this.eatSpaceToken(); - - const version = this.parseVersionOrWildcard(); - - return { - type: 'Comparator', - loc: this.finishLoc(startPos), - operator: token.value, - version, - }; - } - - isVersionCharacter(token: TokenValues): boolean { - if (this.loose && token.type === 'Word') { - return token.value === 'v'; - } - - return false; - } - - parseAtomStartPipe() { - if (this.loose) { - // A bare pipe in an atom start position is treated the same as a wildcard... - // Why...? Because node-semver allows it lol - // > satisfies('1.2.3', '||') === true - return this.parseWildcard(); - } else { - throw this.unexpected({ - description: descriptions.SEMVER.BARE_PIPE_WITHOUT_LOOSE, - }); - } - } - - parseAtomStartWord(token: Tokens['Word']) { - if (this.isWildcardToken(token)) { - return this.parseWildcard(); - } else if (this.isVersionCharacter(token)) { - return this.parseVersion(); - } else { - throw this.unexpected({ - description: descriptions.SEMVER.UNEXPECTED_WORD(token.value), - }); - } - } - - parseAtom() { - const token = this.getToken(); - - switch (token.type) { - case 'Number': - return this.parseVersion(); - - case 'Operator': - return this.parseAtomOperator(token); - - case 'Star': - return this.parseWildcard(); - - case 'Pipe': - return this.parseAtomStartPipe(); - - case 'Word': - return this.parseAtomStartWord(token); - - default: - throw this.unexpected({ - description: descriptions.SEMVER.UNKNOWN_START, - }); - } - } - - parseLogicalAnd(left: RangeNode): LogicalAndNode { - const right = this.parseExpression(); - - return { - type: 'LogicalAnd', - left, - right, - loc: { - filename: this.filename, - start: this.getLoc(left).start, - end: this.getLoc(right).end, - }, - }; - } - - parseExpression(): RangeNode { - const left = this.parseAtom(); - this.eatSpaceToken(); - - if (this.matchToken('RangeDash')) { - return this.parseVersionRange(left); - } - - if (this.matchToken('Pipe')) { - return this.parseLogicalOr(left); - } - - if (!this.matchToken('EOF')) { - return this.parseLogicalAnd(left); - } - - return left; - } - - parseInitialRange(): RangeNode { - // Allow spaces at the beginning, spaces at the end have been removed by the trimRight in the constructor - this.eatSpaceToken(); - - // Empty string is an implicit wildcard in loose mode - if (this.matchToken('EOF') && this.loose) { - return this.parseWildcard(); - } - - const expr = this.parseExpression(); - this.finalize(); - - return expr; - } - - parseInitialVersion(): AbsoluteVersionNode { - const node = this.parseInitialRange(); - - // Verify the return value in version mode - if (node.type !== 'AbsoluteVersion') { - throw this.unexpected({ - ...descriptions.SEMVER.EXPECTED_VERSION, - start: this.getLoc(node).start, - }); - } - - return node; - } - } + class SemverParser extends ParserCore { + constructor({loose, ...opts}: SemverParserOptions, mode: ParseMode) { + super(opts, 'parse/semver'); + this.input = this.input.trimRight(); + this.mode = mode; + this.loose = loose === undefined ? false : loose; + } + + loose: boolean; + mode: ParseMode; + + // For some reason Flow will throw an error without the type casts... + tokenize(index: Number0, input: string): undefined | TokenValues { + const char = input[ob1Get0(index)]; + const nextChar = input[ob1Get0(index) + 1]; + + if ( + (char === '<' && nextChar === '=') || + (char === '>' && nextChar === '=') || + (char === '~' && nextChar === '>') + ) { + // @ts-ignore: TS doesn't infer the possible combinations + const value: ComparatorOperator = char + nextChar; + return this.finishValueToken('Operator', value, ob1Add(index, 2)); + } + + if ( + char === '^' || + char === '<' || + char === '>' || + char === '~' || + char === '=' + ) { + const op: ComparatorOperator = char; + return this.finishValueToken('Operator', op); + } + + if (char === '|' && nextChar === '|') { + return this.finishToken('Pipe', ob1Add(index, 2)); + } + + if (char === '*') { + return this.finishToken('Star'); + } + + if (input[ob1Get0(index) - 1] === ' ' && char === '-' && nextChar === ' ') { + return this.finishToken('RangeDash'); + } + + if (char === '-') { + return this.finishToken('Dash'); + } + + if (char === '+') { + return this.finishToken('Plus'); + } + + if (char === '.') { + return this.finishToken('Dot'); + } + + if (isDigit(char)) { + const [value] = this.readInputFrom(index, isDigit); + return this.finishValueToken( + 'Number', + Number(value), + ob1Add(index, value.length), + ); + } + + if (isAlpha(char)) { + const [value] = this.readInputFrom(index, isAlpha); + return this.finishValueToken('Word', value, ob1Add(index, value.length)); + } + + if (char === ' ' || char === '\t') { + return this.finishToken('Space'); + } + + // Unknown character + return undefined; + } + + // Remove all subsequent space tokens + eatSpaceToken() { + while (this.eatToken('Space') !== undefined) { + // empty + } + } + + parseVersionOrWildcard(): WildcardNode | VersionNode { + const startPos = this.getPosition(); + const startToken = this.getToken(); + const version = this.parseVersion(); + + // We should return a bare wildcard when parsed in a version position if there was nothing else attached + if ( + this.isWildcardToken(startToken) && + version.minor === undefined && + version.patch === undefined && + version.prerelease.length === 0 && + version.build.length === 0 + ) { + return { + type: 'Wildcard', + loc: this.finishLoc(startPos), + }; + } + + return version; + } + + parseVersion(): VersionNode { + const startPos = this.getPosition(); + const startToken = this.getToken(); + + if (this.isVersionCharacter(startToken)) { + this.nextToken(); + } + + const major = this.parseVersionNumber(); + let minor = undefined; + let patch = undefined; + + if (this.eatToken('Dot')) { + minor = this.parseVersionNumber(); + } else if (this.mode === 'version') { + throw this.unexpected({ + description: descriptions.SEMVER.MISSING_MINOR_VERSION, + }); + } + + if (this.eatToken('Dot')) { + patch = this.parseVersionNumber(); + } else if (this.mode === 'version') { + throw this.unexpected({ + description: descriptions.SEMVER.MISSING_PATCH_VERSION, + }); + } + + if (this.matchToken('Dot')) { + throw this.unexpected({ + description: descriptions.SEMVER.EXCESSIVE_VERSION_PARTS, + }); + } + + // The dash is optional in loose mode. eg. 1.2.3pre + let prerelease: VersionPrereleaseParts = []; + if (this.eatToken('Dash') || (this.loose && this.matchToken('Word'))) { + prerelease = this.parseVersionQualifierParts(); + } + + let build: VersionPrereleaseParts = []; + if (this.eatToken('Plus')) { + build = this.parseVersionQualifierParts(); + } + + if (major !== undefined && minor !== undefined && patch !== undefined) { + return { + type: 'AbsoluteVersion', + loc: this.finishLoc(startPos), + major, + minor, + patch, + prerelease, + build, + }; + } else { + return { + type: 'WildcardVersion', + loc: this.finishLoc(startPos), + major, + minor, + patch, + prerelease, + build, + }; + } + } + + parseVersionQualifierParts(): VersionPrereleaseParts { + const parts: VersionPrereleaseParts = []; + do { + parts.push(this.parseVersionQualifierPart()); + } while (this.eatToken('Dot') !== undefined); + return parts; + } + + parseVersionQualifierPart(): string | number { + const parts: Array = []; + + do { + const token = this.getToken(); + + if (token.type === 'Number' || token.type === 'Word') { + this.nextToken(); + parts.push(token.value); + } else if (token.type === 'Dash') { + this.nextToken(); + parts.push('-'); + } else { + throw this.unexpected({ + description: descriptions.SEMVER.INVALID_QUANTIFIER_PART, + }); + } + } while ( + this.matchToken('Number') || + this.matchToken('Word') || + this.matchToken('Dash') + ); + + if (parts.length === 1 && typeof parts[0] === 'number') { + return parts[0]; + } else { + return parts.join(''); + } + } + + isWildcardToken(token: TokenValues): boolean { + if (token.type === 'Star') { + return true; + } + + if (token.type === 'Word') { + return token.value === 'x' || token.value === 'X'; + } + + return false; + } + + parseVersionNumber(): undefined | number { + const token = this.getToken(); + + if (token.type === 'Number') { + this.nextToken(); + return token.value; + } + + if (this.isWildcardToken(token)) { + if (this.mode === 'version') { + throw this.unexpected({ + description: descriptions.SEMVER.WILDCARD_IN_VERSION, + }); + } + + this.nextToken(); + } else { + throw this.unexpected({ + description: descriptions.SEMVER.INVALID_VERSION_NUMBER, + }); + } + + return undefined; + } + + parseLogicalOr(left: RangeNode): LogicalOrNode { + this.nextToken(); + this.eatSpaceToken(); + + const right = this.parseExpression(); + return { + loc: this.finishLoc(this.getLoc(left).start), + type: 'LogicalOr', + left, + right, + }; + } + + validateRangeSide(node: RangeNode): VersionNode | WildcardNode { + // In loose mode, we allow ranges to be a bare wildcard instead of a version + // eg. * - 1.2.3 + if (node.type === 'WildcardVersion' || node.type === 'AbsoluteVersion') { + return node; + } + + if (node.type === 'Wildcard' && this.loose) { + return node; + } + + throw this.unexpected({ + ...descriptions.SEMVER.INVALID_RANGE, + start: this.getLoc(node).start, + }); + } + + parseVersionRange(left: RangeNode): VersionRangeNode { + this.nextToken(); + this.eatSpaceToken(); + + const right = this.parseVersionOrWildcard(); + + return { + type: 'VersionRange', + loc: this.finishLoc(this.getLoc(left).start), + left: this.validateRangeSide(left), + right: this.validateRangeSide(right), + }; + } + + parseWildcard(): WildcardNode { + const startPos = this.getPosition(); + this.nextToken(); + return {type: 'Wildcard', loc: this.finishLoc(startPos)}; + } + + parseAtomOperator(token: Tokens['Operator']): ComparatorNode { + const startPos = this.getPosition(); + this.nextToken(); + this.eatSpaceToken(); + + const version = this.parseVersionOrWildcard(); + + return { + type: 'Comparator', + loc: this.finishLoc(startPos), + operator: token.value, + version, + }; + } + + isVersionCharacter(token: TokenValues): boolean { + if (this.loose && token.type === 'Word') { + return token.value === 'v'; + } + + return false; + } + + parseAtomStartPipe() { + if (this.loose) { + // A bare pipe in an atom start position is treated the same as a wildcard... + // Why...? Because node-semver allows it lol + // > satisfies('1.2.3', '||') === true + return this.parseWildcard(); + } else { + throw this.unexpected({ + description: descriptions.SEMVER.BARE_PIPE_WITHOUT_LOOSE, + }); + } + } + + parseAtomStartWord(token: Tokens['Word']) { + if (this.isWildcardToken(token)) { + return this.parseWildcard(); + } else if (this.isVersionCharacter(token)) { + return this.parseVersion(); + } else { + throw this.unexpected({ + description: descriptions.SEMVER.UNEXPECTED_WORD(token.value), + }); + } + } + + parseAtom() { + const token = this.getToken(); + + switch (token.type) { + case 'Number': + return this.parseVersion(); + + case 'Operator': + return this.parseAtomOperator(token); + + case 'Star': + return this.parseWildcard(); + + case 'Pipe': + return this.parseAtomStartPipe(); + + case 'Word': + return this.parseAtomStartWord(token); + + default: + throw this.unexpected({ + description: descriptions.SEMVER.UNKNOWN_START, + }); + } + } + + parseLogicalAnd(left: RangeNode): LogicalAndNode { + const right = this.parseExpression(); + + return { + type: 'LogicalAnd', + left, + right, + loc: { + filename: this.filename, + start: this.getLoc(left).start, + end: this.getLoc(right).end, + }, + }; + } + + parseExpression(): RangeNode { + const left = this.parseAtom(); + this.eatSpaceToken(); + + if (this.matchToken('RangeDash')) { + return this.parseVersionRange(left); + } + + if (this.matchToken('Pipe')) { + return this.parseLogicalOr(left); + } + + if (!this.matchToken('EOF')) { + return this.parseLogicalAnd(left); + } + + return left; + } + + parseInitialRange(): RangeNode { + // Allow spaces at the beginning, spaces at the end have been removed by the trimRight in the constructor + this.eatSpaceToken(); + + // Empty string is an implicit wildcard in loose mode + if (this.matchToken('EOF') && this.loose) { + return this.parseWildcard(); + } + + const expr = this.parseExpression(); + this.finalize(); + + return expr; + } + + parseInitialVersion(): AbsoluteVersionNode { + const node = this.parseInitialRange(); + + // Verify the return value in version mode + if (node.type !== 'AbsoluteVersion') { + throw this.unexpected({ + ...descriptions.SEMVER.EXPECTED_VERSION, + start: this.getLoc(node).start, + }); + } + + return node; + } + } ); export function parseSemverRange(opts: SemverParserOptions): RangeNode { - return createSemverParser(opts, 'range').parseInitialRange(); + return createSemverParser(opts, 'range').parseInitialRange(); } export function parseSemverVersion( - opts: SemverParserOptions, + opts: SemverParserOptions, ): AbsoluteVersionNode { - return createSemverParser(opts, 'version').parseInitialVersion(); + return createSemverParser(opts, 'version').parseInitialVersion(); } diff --git a/packages/@romejs/codec-semver/satisfies.test.ts b/packages/@romejs/codec-semver/satisfies.test.ts index 154865a457f..9dcc43b00a8 100644 --- a/packages/@romejs/codec-semver/satisfies.test.ts +++ b/packages/@romejs/codec-semver/satisfies.test.ts @@ -10,220 +10,220 @@ import {satisfiesSemver} from '@romejs/codec-semver'; import {test} from 'rome'; const looseOnly: Array<[string, string]> = [ - ['||', '1.3.4'], - ['', '1.0.0'], - ['1.2.3pre+asdf - 2.4.3-pre+asdf', '1.2.3'], - ['1.2.3-pre+asdf - 2.4.3pre+asdf', '1.2.3'], - ['1.2.3pre+asdf - 2.4.3pre+asdf', '1.2.3'], - ['x - 1.0.0', '0.9.7'], - ['x - 1.x', '0.9.7'], - ['1.0.0 - x', '1.9.7'], - ['1.x - x', '1.9.7'], - ['>=0.1.97', 'v0.1.97'], - ['~v0.5.4-pre', '0.5.5'], - ['~v0.5.4-pre', '0.5.4'], + ['||', '1.3.4'], + ['', '1.0.0'], + ['1.2.3pre+asdf - 2.4.3-pre+asdf', '1.2.3'], + ['1.2.3-pre+asdf - 2.4.3pre+asdf', '1.2.3'], + ['1.2.3pre+asdf - 2.4.3pre+asdf', '1.2.3'], + ['x - 1.0.0', '0.9.7'], + ['x - 1.x', '0.9.7'], + ['1.0.0 - x', '1.9.7'], + ['1.x - x', '1.9.7'], + ['>=0.1.97', 'v0.1.97'], + ['~v0.5.4-pre', '0.5.5'], + ['~v0.5.4-pre', '0.5.4'], ]; type TestDataItems = Array<[string, string, boolean] | [string, string]>; const testData: { - pass: TestDataItems; - fail: TestDataItems; + pass: TestDataItems; + fail: TestDataItems; } = { - pass: [ - ...(looseOnly.map(([range, version]) => [range, version, true]) as TestDataItems), - ['1.0.0 - 2.0.0', '1.2.3'], - ['^1.2.3+build', '1.2.3'], - ['^1.2.3+build', '1.3.0'], - ['1.2.3-pre+asdf - 2.4.3-pre+asdf', '1.2.3'], - ['1.2.3-pre+asdf - 2.4.3-pre+asdf', '1.2.3-pre.2'], - ['1.2.3-pre+asdf - 2.4.3-pre+asdf', '2.4.3-alpha'], - ['1.2.3+asdf - 2.4.3+asdf', '1.2.3'], - ['1.0.0', '1.0.0'], - ['>=*', '0.2.4'], - ['>1.0.0', '1.1.0'], - ['<=2.0.0', '2.0.0'], - ['<=2.0.0', '1.9999.9999'], - ['<=2.0.0', '0.2.9'], - ['<2.0.0', '1.9999.9999'], - ['<2.0.0', '0.2.9'], - ['>= 1.0.0', '1.0.0'], - ['>= 1.0.0', '1.0.1'], - ['>= 1.0.0', '1.1.0'], - ['> 1.0.0', '1.0.1'], - ['> 1.0.0', '1.1.0'], - ['<= 2.0.0', '2.0.0'], - ['<= 2.0.0', '1.9999.9999'], - ['<= 2.0.0', '0.2.9'], - ['< 2.0.0', '1.9999.9999'], - ['<\t2.0.0', '0.2.9'], - ['>=0.1.97', '0.1.97'], - ['0.1.20 || 1.2.4', '1.2.4'], - ['>=0.2.3 || <0.0.1', '0.0.0'], - ['>=0.2.3 || <0.0.1', '0.2.3'], - ['>=0.2.3 || <0.0.1', '0.2.4'], - ['2.x.x', '2.1.3'], - ['1.2.x', '1.2.3'], - ['1.2.x || 2.x', '2.1.3'], - ['1.2.x || 2.x', '1.2.3'], - ['x', '1.2.3'], - ['2.*.*', '2.1.3'], - ['1.2.*', '1.2.3'], - ['1.2.* || 2.*', '2.1.3'], - ['1.2.* || 2.*', '1.2.3'], - ['*', '1.2.3'], - ['2', '2.1.2'], - ['2.3', '2.3.1'], - ['~x', '0.0.9'], - // >=2.4.0 <2.5.0 - ['~2', '2.0.9'], - // >=2.0.0 <2.1.0 - ['~2.4', '2.4.0'], - // >=2.4.0 <2.5.0 - ['~2.4', '2.4.5'], - ['~>3.2.1', '3.2.2'], - // >=3.2.1 <3.3.0, - ['~1', '1.2.3'], - // >=1.0.0 <2.0.0 - ['~>1', '1.2.3'], - ['~> 1', '1.2.3'], - ['~1.0', '1.0.2'], - // >=1.0.0 <1.1.0, - ['~ 1.0', '1.0.2'], - ['~ 1.0.3', '1.0.12'], - ['~0.2', '0.2.3'], - ['~0', '0.2.3'], - ['~*.*.*', '0.2.3'], - ['>=1', '1.0.0'], - ['>= 1', '1.0.0'], - ['<1.2', '1.1.1'], - ['< 1.2', '1.1.1'], - ['=0.7.x', '0.7.2'], - ['<=0.7.x', '0.7.2'], - ['>=0.7.x', '0.7.2'], - ['<=0.7.x', '0.6.2'], - ['~1.2.1 >=1.2.3', '1.2.3'], - ['~1.2.1 =1.2.3', '1.2.3'], - ['~1.2.1 1.2.3', '1.2.3'], - ['~1.2.1 >=1.2.3 1.2.3', '1.2.3'], - ['~1.2.1 1.2.3 >=1.2.3', '1.2.3'], - ['~1.2.1 1.2.3', '1.2.3'], - ['>=1.2.1 1.2.3', '1.2.3'], - ['1.2.3 >=1.2.1', '1.2.3'], - ['>=1.2.3 >=1.2.1', '1.2.3'], - ['>=1.2.1 >=1.2.3', '1.2.3'], - ['>=1.2', '1.2.8'], - ['^1.2.3', '1.8.1'], - ['^0.1.2', '0.1.2'], - ['^0.1', '0.1.2'], - ['^0.0.1', '0.0.1'], - ['^1.2', '1.4.2'], - ['^1.2 ^1', '1.4.2'], - ['^1.2.3-alpha', '1.2.3-pre'], - ['^1.2.0-alpha', '1.2.0-pre'], - ['^0.0.1-alpha', '0.0.1-beta'], - ['^0.1.1-alpha', '0.1.1-beta'], - ['^x', '1.2.3'], - ['<=7.x', '7.9.9'], - ], - fail: [ - ...looseOnly, - ['1.0.0 - 2.0.0', '2.2.3'], - ['^1.2.3+build', '2.0.0'], - ['^1.2.3+build', '1.2.0'], - ['^1.2.3', '1.2.3-pre'], - ['1.2.3+asdf - 2.4.3+asdf', '1.2.3-pre.2'], - ['1.2.3+asdf - 2.4.3+asdf', '2.4.3-alpha'], - ['^1.2', '1.2.0-pre'], - ['>1.2', '1.3.0-beta'], - ['<=1.2.3', '1.2.3-beta'], - ['^1.2.3', '1.2.3-beta'], - ['=0.7.x', '0.7.0-asdf'], - ['>=0.7.x', '0.7.0-asdf'], - ['<1.2.3', '1.2.3-beta'], - ['^1.2.3', '2.0.0-alpha'], - ['^1.2.3', '2.0.0-pre'], - ['1', '1.0.0beta', true], - ['<1', '1.0.0beta', true], - ['< 1', '1.0.0beta', true], - ['>=0.1.97', 'v0.1.93', true], - ['1', '2.0.0beta', true], - ['~v0.5.4-beta', '0.5.4-alpha', true], - ['*', 'v1.2.3-foo', true], - ['<1', '1.0.0'], - ['<0.7.x', '0.7.2'], - ['1.0.0', '1.0.1'], - ['>=1.0.0', '0.0.0'], - ['>=1.0.0', '0.0.1'], - ['>=1.0.0', '0.1.0'], - ['>1.0.0', '0.0.1'], - ['>1.0.0', '0.1.0'], - ['<=2.0.0', '3.0.0'], - ['<=2.0.0', '2.9999.9999'], - ['<=2.0.0', '2.2.9'], - ['<2.0.0', '2.9999.9999'], - ['<2.0.0', '2.2.9'], - ['>=0.1.97', '0.1.93'], - ['0.1.20 || 1.2.4', '1.2.3'], - ['>=0.2.3 || <0.0.1', '0.0.3'], - ['>=0.2.3 || <0.0.1', '0.2.2'], - ['2.x.x', '3.1.3'], - ['1.2.x', '1.3.3'], - ['1.2.x || 2.x', '3.1.3'], - ['1.2.x || 2.x', '1.1.3'], - ['2.*.*', '1.1.3'], - ['2.*.*', '3.1.3'], - ['1.2.*', '1.3.3'], - ['1.2.* || 2.*', '3.1.3'], - ['1.2.* || 2.*', '1.1.3'], - ['2', '1.1.2'], - ['2.3', '2.4.1'], - ['~2.4', '2.5.0'], - // >=2.4.0 <2.5.0 - ['~2.4', '2.3.9'], - ['~>3.2.1', '3.3.2'], - // >=3.2.1 <3.3.0 - ['~>3.2.1', '3.2.0'], - // >=3.2.1 <3.3.0 - ['~1', '0.2.3'], - // >=1.0.0 <2.0.0 - ['~>1', '2.2.3'], - ['~1.0', '1.1.0'], - // >=1.0.0 <1.1.0 - ['~0.2', '0.3.3'], - ['~0', '1.2.3'], - ['>=1.2', '1.1.1'], - ['=0.7.x', '0.8.2'], - ['>=0.7.x', '0.6.2'], - ['=1.2.3', '1.2.3-beta'], - ['>1.2', '1.2.8'], - ['^0.0.1', '0.0.2'], - ['^1.2.3', '1.2.2'], - ['^1.2', '1.1.9'], - // invalid ranges never satisfied! - ['blerg', '1.2.3'], - ['git+https://user:password0123@github.com/foo', '123.0.0'], - ], + pass: [ + ...(looseOnly.map(([range, version]) => [range, version, true]) as TestDataItems), + ['1.0.0 - 2.0.0', '1.2.3'], + ['^1.2.3+build', '1.2.3'], + ['^1.2.3+build', '1.3.0'], + ['1.2.3-pre+asdf - 2.4.3-pre+asdf', '1.2.3'], + ['1.2.3-pre+asdf - 2.4.3-pre+asdf', '1.2.3-pre.2'], + ['1.2.3-pre+asdf - 2.4.3-pre+asdf', '2.4.3-alpha'], + ['1.2.3+asdf - 2.4.3+asdf', '1.2.3'], + ['1.0.0', '1.0.0'], + ['>=*', '0.2.4'], + ['>1.0.0', '1.1.0'], + ['<=2.0.0', '2.0.0'], + ['<=2.0.0', '1.9999.9999'], + ['<=2.0.0', '0.2.9'], + ['<2.0.0', '1.9999.9999'], + ['<2.0.0', '0.2.9'], + ['>= 1.0.0', '1.0.0'], + ['>= 1.0.0', '1.0.1'], + ['>= 1.0.0', '1.1.0'], + ['> 1.0.0', '1.0.1'], + ['> 1.0.0', '1.1.0'], + ['<= 2.0.0', '2.0.0'], + ['<= 2.0.0', '1.9999.9999'], + ['<= 2.0.0', '0.2.9'], + ['< 2.0.0', '1.9999.9999'], + ['<\t2.0.0', '0.2.9'], + ['>=0.1.97', '0.1.97'], + ['0.1.20 || 1.2.4', '1.2.4'], + ['>=0.2.3 || <0.0.1', '0.0.0'], + ['>=0.2.3 || <0.0.1', '0.2.3'], + ['>=0.2.3 || <0.0.1', '0.2.4'], + ['2.x.x', '2.1.3'], + ['1.2.x', '1.2.3'], + ['1.2.x || 2.x', '2.1.3'], + ['1.2.x || 2.x', '1.2.3'], + ['x', '1.2.3'], + ['2.*.*', '2.1.3'], + ['1.2.*', '1.2.3'], + ['1.2.* || 2.*', '2.1.3'], + ['1.2.* || 2.*', '1.2.3'], + ['*', '1.2.3'], + ['2', '2.1.2'], + ['2.3', '2.3.1'], + ['~x', '0.0.9'], + // >=2.4.0 <2.5.0 + ['~2', '2.0.9'], + // >=2.0.0 <2.1.0 + ['~2.4', '2.4.0'], + // >=2.4.0 <2.5.0 + ['~2.4', '2.4.5'], + ['~>3.2.1', '3.2.2'], + // >=3.2.1 <3.3.0, + ['~1', '1.2.3'], + // >=1.0.0 <2.0.0 + ['~>1', '1.2.3'], + ['~> 1', '1.2.3'], + ['~1.0', '1.0.2'], + // >=1.0.0 <1.1.0, + ['~ 1.0', '1.0.2'], + ['~ 1.0.3', '1.0.12'], + ['~0.2', '0.2.3'], + ['~0', '0.2.3'], + ['~*.*.*', '0.2.3'], + ['>=1', '1.0.0'], + ['>= 1', '1.0.0'], + ['<1.2', '1.1.1'], + ['< 1.2', '1.1.1'], + ['=0.7.x', '0.7.2'], + ['<=0.7.x', '0.7.2'], + ['>=0.7.x', '0.7.2'], + ['<=0.7.x', '0.6.2'], + ['~1.2.1 >=1.2.3', '1.2.3'], + ['~1.2.1 =1.2.3', '1.2.3'], + ['~1.2.1 1.2.3', '1.2.3'], + ['~1.2.1 >=1.2.3 1.2.3', '1.2.3'], + ['~1.2.1 1.2.3 >=1.2.3', '1.2.3'], + ['~1.2.1 1.2.3', '1.2.3'], + ['>=1.2.1 1.2.3', '1.2.3'], + ['1.2.3 >=1.2.1', '1.2.3'], + ['>=1.2.3 >=1.2.1', '1.2.3'], + ['>=1.2.1 >=1.2.3', '1.2.3'], + ['>=1.2', '1.2.8'], + ['^1.2.3', '1.8.1'], + ['^0.1.2', '0.1.2'], + ['^0.1', '0.1.2'], + ['^0.0.1', '0.0.1'], + ['^1.2', '1.4.2'], + ['^1.2 ^1', '1.4.2'], + ['^1.2.3-alpha', '1.2.3-pre'], + ['^1.2.0-alpha', '1.2.0-pre'], + ['^0.0.1-alpha', '0.0.1-beta'], + ['^0.1.1-alpha', '0.1.1-beta'], + ['^x', '1.2.3'], + ['<=7.x', '7.9.9'], + ], + fail: [ + ...looseOnly, + ['1.0.0 - 2.0.0', '2.2.3'], + ['^1.2.3+build', '2.0.0'], + ['^1.2.3+build', '1.2.0'], + ['^1.2.3', '1.2.3-pre'], + ['1.2.3+asdf - 2.4.3+asdf', '1.2.3-pre.2'], + ['1.2.3+asdf - 2.4.3+asdf', '2.4.3-alpha'], + ['^1.2', '1.2.0-pre'], + ['>1.2', '1.3.0-beta'], + ['<=1.2.3', '1.2.3-beta'], + ['^1.2.3', '1.2.3-beta'], + ['=0.7.x', '0.7.0-asdf'], + ['>=0.7.x', '0.7.0-asdf'], + ['<1.2.3', '1.2.3-beta'], + ['^1.2.3', '2.0.0-alpha'], + ['^1.2.3', '2.0.0-pre'], + ['1', '1.0.0beta', true], + ['<1', '1.0.0beta', true], + ['< 1', '1.0.0beta', true], + ['>=0.1.97', 'v0.1.93', true], + ['1', '2.0.0beta', true], + ['~v0.5.4-beta', '0.5.4-alpha', true], + ['*', 'v1.2.3-foo', true], + ['<1', '1.0.0'], + ['<0.7.x', '0.7.2'], + ['1.0.0', '1.0.1'], + ['>=1.0.0', '0.0.0'], + ['>=1.0.0', '0.0.1'], + ['>=1.0.0', '0.1.0'], + ['>1.0.0', '0.0.1'], + ['>1.0.0', '0.1.0'], + ['<=2.0.0', '3.0.0'], + ['<=2.0.0', '2.9999.9999'], + ['<=2.0.0', '2.2.9'], + ['<2.0.0', '2.9999.9999'], + ['<2.0.0', '2.2.9'], + ['>=0.1.97', '0.1.93'], + ['0.1.20 || 1.2.4', '1.2.3'], + ['>=0.2.3 || <0.0.1', '0.0.3'], + ['>=0.2.3 || <0.0.1', '0.2.2'], + ['2.x.x', '3.1.3'], + ['1.2.x', '1.3.3'], + ['1.2.x || 2.x', '3.1.3'], + ['1.2.x || 2.x', '1.1.3'], + ['2.*.*', '1.1.3'], + ['2.*.*', '3.1.3'], + ['1.2.*', '1.3.3'], + ['1.2.* || 2.*', '3.1.3'], + ['1.2.* || 2.*', '1.1.3'], + ['2', '1.1.2'], + ['2.3', '2.4.1'], + ['~2.4', '2.5.0'], + // >=2.4.0 <2.5.0 + ['~2.4', '2.3.9'], + ['~>3.2.1', '3.3.2'], + // >=3.2.1 <3.3.0 + ['~>3.2.1', '3.2.0'], + // >=3.2.1 <3.3.0 + ['~1', '0.2.3'], + // >=1.0.0 <2.0.0 + ['~>1', '2.2.3'], + ['~1.0', '1.1.0'], + // >=1.0.0 <1.1.0 + ['~0.2', '0.3.3'], + ['~0', '1.2.3'], + ['>=1.2', '1.1.1'], + ['=0.7.x', '0.8.2'], + ['>=0.7.x', '0.6.2'], + ['=1.2.3', '1.2.3-beta'], + ['>1.2', '1.2.8'], + ['^0.0.1', '0.0.2'], + ['^1.2.3', '1.2.2'], + ['^1.2', '1.1.9'], + // invalid ranges never satisfied! + ['blerg', '1.2.3'], + ['git+https://user:password0123@github.com/foo', '123.0.0'], + ], }; test( - 'satisfies pass', - function(t) { - for (const [range, ver, loose] of testData.pass) { - t.true( - satisfiesSemver(ver, range, {loose: loose === true}), - `${range} should be satisfied by ${ver}`, - ); - } - }, + 'satisfies pass', + function(t) { + for (const [range, ver, loose] of testData.pass) { + t.true( + satisfiesSemver(ver, range, {loose: loose === true}), + `${range} should be satisfied by ${ver}`, + ); + } + }, ); test( - 'satisfies fail', - function(t) { - for (const [range, ver, loose] of testData.fail) { - const found = satisfiesSemver(ver, range, {loose: loose === true}); - t.false(found, `${ver} should not be satisfied by ${range}`); - } - }, + 'satisfies fail', + function(t) { + for (const [range, ver, loose] of testData.fail) { + const found = satisfiesSemver(ver, range, {loose: loose === true}); + t.false(found, `${ver} should not be satisfied by ${range}`); + } + }, ); diff --git a/packages/@romejs/codec-semver/satisfies.ts b/packages/@romejs/codec-semver/satisfies.ts index 79b6c7ea458..21b8f918a4f 100644 --- a/packages/@romejs/codec-semver/satisfies.ts +++ b/packages/@romejs/codec-semver/satisfies.ts @@ -6,202 +6,202 @@ */ import { - AbsoluteVersionNode, - ComparatorOperator, - RangeNode, - VersionNode, - WildcardNode, + AbsoluteVersionNode, + ComparatorOperator, + RangeNode, + VersionNode, + WildcardNode, } from './types'; import {compareFromAst} from './compare'; function buildVersion( - major: undefined | number, - minor: undefined | number, - patch: undefined | number, + major: undefined | number, + minor: undefined | number, + patch: undefined | number, ): VersionNode { - return { - type: 'WildcardVersion', - major, - minor, - patch, - prerelease: [], - build: [], - }; + return { + type: 'WildcardVersion', + major, + minor, + patch, + prerelease: [], + build: [], + }; } function compareOp( - op: ComparatorOperator, - version: AbsoluteVersionNode, - range: WildcardNode | VersionNode, + op: ComparatorOperator, + version: AbsoluteVersionNode, + range: WildcardNode | VersionNode, ): boolean { - if (range.type === 'Wildcard') { - return true; - } - - switch (op) { - case '=': - return compareFromAst(version, range) === 0; - - case '<': - return compareFromAst(version, range) < 0; - - case '>': - return compareFromAst(version, range) > 0; - - case '>=': - return compareFromAst(version, range) >= 0; - - case '<=': - return compareFromAst(version, range) <= 0; - - case '^': { - // Make sure that the version isn't less than the range - if (compareOp('>=', version, range) === false) { - return false; - } - - // Deconstruct the range - const {major, minor, patch} = range; - - if (major === 0) { - if (minor === 0) { - // ^0.0.3 := >=0.0.3 <0.0.4 - // @ts-ignore - return compareOp('<', version, buildVersion(0, 0, patch + 1)); - } else { - // ^0.2.3 := >=0.2.3 <0.3.0 - // @ts-ignore - return compareOp('<', version, buildVersion(0, minor + 1, 0)); - } - } - - // ^1.2.3 := >=1.2.3 <2.0.0 - - // @ts-ignore - return compareOp('<', version, buildVersion(major + 1, 0, 0)); - } - - case '~>': - case '~': { - // Make sure that the version isn't less than the range - if (compareOp('>=', version, range) === false) { - return false; - } - - // Deconstruct the range - const {major, minor} = range; - - if (minor === undefined) { - // ~1 := >=1.0.0 <(1+1).0.0 := >=1.0.0 <2.0.0 (Same as 1.x) - // @ts-ignore - return compareOp('<', version, buildVersion(major + 1, minor, 0)); - } - - // ~1.2.3 := >=1.2.3 <1.(2+1).0 := >=1.2.3 <1.3.0 - return compareOp('<', version, buildVersion(major, minor + 1, 0)); - } - - default: - throw new Error(`Unknown operator ${op}`); - } + if (range.type === 'Wildcard') { + return true; + } + + switch (op) { + case '=': + return compareFromAst(version, range) === 0; + + case '<': + return compareFromAst(version, range) < 0; + + case '>': + return compareFromAst(version, range) > 0; + + case '>=': + return compareFromAst(version, range) >= 0; + + case '<=': + return compareFromAst(version, range) <= 0; + + case '^': { + // Make sure that the version isn't less than the range + if (compareOp('>=', version, range) === false) { + return false; + } + + // Deconstruct the range + const {major, minor, patch} = range; + + if (major === 0) { + if (minor === 0) { + // ^0.0.3 := >=0.0.3 <0.0.4 + // @ts-ignore + return compareOp('<', version, buildVersion(0, 0, patch + 1)); + } else { + // ^0.2.3 := >=0.2.3 <0.3.0 + // @ts-ignore + return compareOp('<', version, buildVersion(0, minor + 1, 0)); + } + } + + // ^1.2.3 := >=1.2.3 <2.0.0 + + // @ts-ignore + return compareOp('<', version, buildVersion(major + 1, 0, 0)); + } + + case '~>': + case '~': { + // Make sure that the version isn't less than the range + if (compareOp('>=', version, range) === false) { + return false; + } + + // Deconstruct the range + const {major, minor} = range; + + if (minor === undefined) { + // ~1 := >=1.0.0 <(1+1).0.0 := >=1.0.0 <2.0.0 (Same as 1.x) + // @ts-ignore + return compareOp('<', version, buildVersion(major + 1, minor, 0)); + } + + // ~1.2.3 := >=1.2.3 <1.(2+1).0 := >=1.2.3 <1.3.0 + return compareOp('<', version, buildVersion(major, minor + 1, 0)); + } + + default: + throw new Error(`Unknown operator ${op}`); + } } function inRange( - version: AbsoluteVersionNode, - left: WildcardNode | VersionNode, - right: WildcardNode | VersionNode, + version: AbsoluteVersionNode, + left: WildcardNode | VersionNode, + right: WildcardNode | VersionNode, ): boolean { - if (left.type === 'Wildcard' || right.type === 'Wildcard') { - return true; - } + if (left.type === 'Wildcard' || right.type === 'Wildcard') { + return true; + } - return compareOp('>=', version, left) && compareOp('<=', version, right); + return compareOp('>=', version, left) && compareOp('<=', version, right); } function collectVersions(range: RangeNode): Array { - switch (range.type) { - case 'AbsoluteVersion': - case 'WildcardVersion': - return [range]; + switch (range.type) { + case 'AbsoluteVersion': + case 'WildcardVersion': + return [range]; - case 'Wildcard': - return []; + case 'Wildcard': + return []; - case 'Comparator': - return collectVersions(range.version); + case 'Comparator': + return collectVersions(range.version); - case 'LogicalAnd': - case 'LogicalOr': - case 'VersionRange': - return [...collectVersions(range.left), ...collectVersions(range.right)]; + case 'LogicalAnd': + case 'LogicalOr': + case 'VersionRange': + return [...collectVersions(range.left), ...collectVersions(range.right)]; - default: - throw new Error('Unknown range type'); - } + default: + throw new Error('Unknown range type'); + } } export function satisfiesFromAst( - version: AbsoluteVersionNode, - range: RangeNode, + version: AbsoluteVersionNode, + range: RangeNode, ): boolean { - const res = satisfiesSub(version, range); - if (res === false) { - return false; - } - - if (version.prerelease.length > 0) { - // Find the set of versions that are allowed to have prereleases - // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 - // That should allow `1.2.3-pr.2` to pass. - // However, `1.2.4-alpha.notready` should NOT be allowed, - // even though it's within the range set by the comparators. - const versions = collectVersions(range); - - for (const comparator of versions) { - if (comparator.prerelease.length > 0) { - if ( - comparator.major === version.major && - comparator.minor === version.minor && - comparator.patch === version.patch - ) { - return true; - } - } - } - - // Version has a -pre, but it's not one of the ones we like. - return false; - } - - return true; + const res = satisfiesSub(version, range); + if (res === false) { + return false; + } + + if (version.prerelease.length > 0) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + const versions = collectVersions(range); + + for (const comparator of versions) { + if (comparator.prerelease.length > 0) { + if ( + comparator.major === version.major && + comparator.minor === version.minor && + comparator.patch === version.patch + ) { + return true; + } + } + } + + // Version has a -pre, but it's not one of the ones we like. + return false; + } + + return true; } function satisfiesSub(version: AbsoluteVersionNode, range: RangeNode): boolean { - switch (range.type) { - case 'AbsoluteVersion': - case 'WildcardVersion': - return compareOp('=', version, range); - - case 'Wildcard': - return true; - - case 'Comparator': - return compareOp(range.operator, version, range.version); - - case 'LogicalAnd': - return ( - satisfiesSub(version, range.left) && satisfiesSub(version, range.right) - ); - - case 'LogicalOr': - return ( - satisfiesSub(version, range.left) || satisfiesSub(version, range.right) - ); - - case 'VersionRange': - return ( - inRange(version, range.left, range.right) || - inRange(version, range.right, range.left) - ); - } + switch (range.type) { + case 'AbsoluteVersion': + case 'WildcardVersion': + return compareOp('=', version, range); + + case 'Wildcard': + return true; + + case 'Comparator': + return compareOp(range.operator, version, range.version); + + case 'LogicalAnd': + return ( + satisfiesSub(version, range.left) && satisfiesSub(version, range.right) + ); + + case 'LogicalOr': + return ( + satisfiesSub(version, range.left) || satisfiesSub(version, range.right) + ); + + case 'VersionRange': + return ( + inRange(version, range.left, range.right) || + inRange(version, range.right, range.left) + ); + } } diff --git a/packages/@romejs/codec-semver/sort.test.ts b/packages/@romejs/codec-semver/sort.test.ts index 09c5071a2c3..bc06ce80df6 100644 --- a/packages/@romejs/codec-semver/sort.test.ts +++ b/packages/@romejs/codec-semver/sort.test.ts @@ -10,13 +10,13 @@ import {sortSemverVersions, stringifySemver} from '@romejs/codec-semver'; import {test} from 'rome'; test( - 'sort', - (t) => { - const sorted = sortSemverVersions(['5.3.6', '1.2.3', '3.2.1', '1.2.4']); + 'sort', + (t) => { + const sorted = sortSemverVersions(['5.3.6', '1.2.3', '3.2.1', '1.2.4']); - t.is(stringifySemver(sorted[0]), '1.2.3'); - t.is(stringifySemver(sorted[1]), '1.2.4'); - t.is(stringifySemver(sorted[2]), '3.2.1'); - t.is(stringifySemver(sorted[3]), '5.3.6'); - }, + t.is(stringifySemver(sorted[0]), '1.2.3'); + t.is(stringifySemver(sorted[1]), '1.2.4'); + t.is(stringifySemver(sorted[2]), '3.2.1'); + t.is(stringifySemver(sorted[3]), '5.3.6'); + }, ); diff --git a/packages/@romejs/codec-semver/stringify.test.ts b/packages/@romejs/codec-semver/stringify.test.ts index 0d1e23f3b4b..e7ff05d39e1 100644 --- a/packages/@romejs/codec-semver/stringify.test.ts +++ b/packages/@romejs/codec-semver/stringify.test.ts @@ -10,53 +10,50 @@ import {parseSemverRange, stringifySemver} from '@romejs/codec-semver'; import {test} from 'rome'; test( - 'stringify', - (t) => { - // basic version and whitespace - t.is(stringifySemver(parseSemverRange({input: '1.2.3'})), '1.2.3'); - t.is(stringifySemver(parseSemverRange({input: ' 1.2.3'})), '1.2.3'); - t.is(stringifySemver(parseSemverRange({input: '1.2.3 '})), '1.2.3'); - t.is(stringifySemver(parseSemverRange({input: '\t\t1.2.3\r\n'})), '1.2.3'); - - // retains prerelease and build - t.is( - stringifySemver(parseSemverRange({input: '1.2.3-prerelease'})), - '1.2.3-prerelease', - ); - t.is( - stringifySemver(parseSemverRange({input: '1.2.3+build'})), - '1.2.3+build', - ); - t.is( - stringifySemver(parseSemverRange({input: '1.2.3-prerelease+build'})), - '1.2.3-prerelease+build', - ); - - // comparators - t.is(stringifySemver(parseSemverRange({input: '~1.2.3'})), '~1.2.3'); - t.is(stringifySemver(parseSemverRange({input: '~ 1.2.3'})), '~1.2.3'); - - // wildcards - t.is(stringifySemver(parseSemverRange({input: '1.2'})), '1.2'); - t.is(stringifySemver(parseSemverRange({input: '1.*.3'})), '1.*.3'); - t.is(stringifySemver(parseSemverRange({input: '1.x'})), '1'); - - // ranges - t.is( - stringifySemver(parseSemverRange({input: '1.2.3 - 1.3.4'})), - '1.2.3 - 1.3.4', - ); - - // logical and - t.is( - stringifySemver(parseSemverRange({input: '1.2.3 >= 1.3.4'})), - '1.2.3 >=1.3.4', - ); - - // logical or - t.is( - stringifySemver(parseSemverRange({input: '1.2.3 || 1.3.4'})), - '1.2.3 || 1.3.4', - ); - }, + 'stringify', + (t) => { + // basic version and whitespace + t.is(stringifySemver(parseSemverRange({input: '1.2.3'})), '1.2.3'); + t.is(stringifySemver(parseSemverRange({input: ' 1.2.3'})), '1.2.3'); + t.is(stringifySemver(parseSemverRange({input: '1.2.3 '})), '1.2.3'); + t.is(stringifySemver(parseSemverRange({input: '\t\t1.2.3\r\n'})), '1.2.3'); + + // retains prerelease and build + t.is( + stringifySemver(parseSemverRange({input: '1.2.3-prerelease'})), + '1.2.3-prerelease', + ); + t.is(stringifySemver(parseSemverRange({input: '1.2.3+build'})), '1.2.3+build'); + t.is( + stringifySemver(parseSemverRange({input: '1.2.3-prerelease+build'})), + '1.2.3-prerelease+build', + ); + + // comparators + t.is(stringifySemver(parseSemverRange({input: '~1.2.3'})), '~1.2.3'); + t.is(stringifySemver(parseSemverRange({input: '~ 1.2.3'})), '~1.2.3'); + + // wildcards + t.is(stringifySemver(parseSemverRange({input: '1.2'})), '1.2'); + t.is(stringifySemver(parseSemverRange({input: '1.*.3'})), '1.*.3'); + t.is(stringifySemver(parseSemverRange({input: '1.x'})), '1'); + + // ranges + t.is( + stringifySemver(parseSemverRange({input: '1.2.3 - 1.3.4'})), + '1.2.3 - 1.3.4', + ); + + // logical and + t.is( + stringifySemver(parseSemverRange({input: '1.2.3 >= 1.3.4'})), + '1.2.3 >=1.3.4', + ); + + // logical or + t.is( + stringifySemver(parseSemverRange({input: '1.2.3 || 1.3.4'})), + '1.2.3 || 1.3.4', + ); + }, ); diff --git a/packages/@romejs/codec-semver/stringify.ts b/packages/@romejs/codec-semver/stringify.ts index a5f50388947..94323ac6a6e 100644 --- a/packages/@romejs/codec-semver/stringify.ts +++ b/packages/@romejs/codec-semver/stringify.ts @@ -9,52 +9,52 @@ import {RangeNode} from './types'; // Remove all function compactRight( - parts: Array, + parts: Array, ): Array { - for (let i = parts.length - 1; i >= 0; i--) { - const part = parts[i]; - if (part !== undefined) { - return parts.slice(0, i + 1); - } - } - - return []; + for (let i = parts.length - 1; i >= 0; i--) { + const part = parts[i]; + if (part !== undefined) { + return parts.slice(0, i + 1); + } + } + + return []; } const WILDCARD = '*'; export default function stringify(node: RangeNode): string { - switch (node.type) { - case 'WildcardVersion': - case 'AbsoluteVersion': { - // Build up x.x.x format - let str = compactRight([node.major, node.minor, node.patch]).map((part) => - part === undefined ? WILDCARD : part - ).join('.'); - - // add on qualifiers - if (node.prerelease.length > 0) { - str += `-${node.prerelease.join('.')}`; - } - if (node.build.length > 0) { - str += `+${node.build.join('.')}`; - } - return str; - } - - case 'Wildcard': - return WILDCARD; - - case 'Comparator': - return `${node.operator}${stringify(node.version)}`; - - case 'LogicalAnd': - return `${stringify(node.left)} ${stringify(node.right)}`; - - case 'LogicalOr': - return `${stringify(node.left)} || ${stringify(node.right)}`; - - case 'VersionRange': - return `${stringify(node.left)} - ${stringify(node.right)}`; - } + switch (node.type) { + case 'WildcardVersion': + case 'AbsoluteVersion': { + // Build up x.x.x format + let str = compactRight([node.major, node.minor, node.patch]).map((part) => + part === undefined ? WILDCARD : part + ).join('.'); + + // add on qualifiers + if (node.prerelease.length > 0) { + str += `-${node.prerelease.join('.')}`; + } + if (node.build.length > 0) { + str += `+${node.build.join('.')}`; + } + return str; + } + + case 'Wildcard': + return WILDCARD; + + case 'Comparator': + return `${node.operator}${stringify(node.version)}`; + + case 'LogicalAnd': + return `${stringify(node.left)} ${stringify(node.right)}`; + + case 'LogicalOr': + return `${stringify(node.left)} || ${stringify(node.right)}`; + + case 'VersionRange': + return `${stringify(node.left)} - ${stringify(node.right)}`; + } } diff --git a/packages/@romejs/codec-semver/types.ts b/packages/@romejs/codec-semver/types.ts index 9365bddb851..e67b24c1072 100644 --- a/packages/@romejs/codec-semver/types.ts +++ b/packages/@romejs/codec-semver/types.ts @@ -6,11 +6,11 @@ */ import { - BaseTokens, - ComplexNode, - SimpleNode, - SimpleToken, - ValueToken, + BaseTokens, + ComplexNode, + SimpleNode, + SimpleToken, + ValueToken, } from '@romejs/parser-core'; // PARSER @@ -18,26 +18,26 @@ export type VersionPrereleaseParts = Array; // 1.2, 1, 1.*.2 export type WildcardVersionNode = ComplexNode< - 'WildcardVersion', - { - major: undefined | number; - minor: undefined | number; - patch: undefined | number; - prerelease: VersionPrereleaseParts; - build: VersionPrereleaseParts; - } + 'WildcardVersion', + { + major: undefined | number; + minor: undefined | number; + patch: undefined | number; + prerelease: VersionPrereleaseParts; + build: VersionPrereleaseParts; + } >; // 1.2.3 export type AbsoluteVersionNode = ComplexNode< - 'AbsoluteVersion', - { - major: number; - minor: number; - patch: number; - prerelease: VersionPrereleaseParts; - build: VersionPrereleaseParts; - } + 'AbsoluteVersion', + { + major: number; + minor: number; + patch: number; + prerelease: VersionPrereleaseParts; + build: VersionPrereleaseParts; + } >; // union to treat these as the same @@ -48,70 +48,70 @@ export type WildcardNode = SimpleNode<'Wildcard'>; // >=1.2.3 export type ComparatorOperator = - | '<' - | '>' - | '>=' - | '<=' - | '~>' - | '^' - | '~' - | '='; + | '<' + | '>' + | '>=' + | '<=' + | '~>' + | '^' + | '~' + | '='; export type ComparatorNode = ComplexNode< - 'Comparator', - { - operator: ComparatorOperator; - version: WildcardNode | VersionNode; - } + 'Comparator', + { + operator: ComparatorOperator; + version: WildcardNode | VersionNode; + } >; // 1.2.3 || 4.5.6 export type LogicalOrNode = ComplexNode< - 'LogicalOr', - { - left: RangeNode; - right: RangeNode; - } + 'LogicalOr', + { + left: RangeNode; + right: RangeNode; + } >; // 1.2.3 4.5.6 export type LogicalAndNode = ComplexNode< - 'LogicalAnd', - { - left: RangeNode; - right: RangeNode; - } + 'LogicalAnd', + { + left: RangeNode; + right: RangeNode; + } >; // 1.2.3 - 2.3.4 export type VersionRangeNode = ComplexNode< - 'VersionRange', - { - left: WildcardNode | VersionNode; - right: WildcardNode | VersionNode; - } + 'VersionRange', + { + left: WildcardNode | VersionNode; + right: WildcardNode | VersionNode; + } >; export type RangeNode = - | LogicalAndNode - | VersionRangeNode - | LogicalOrNode - | ComparatorNode - | WildcardNode - | VersionNode; + | LogicalAndNode + | VersionRangeNode + | LogicalOrNode + | ComparatorNode + | WildcardNode + | VersionNode; // TOKENS export type Tokens = BaseTokens & { - Space: SimpleToken<'Space'>; - Number: ValueToken<'Number', number>; - Word: ValueToken<'Word', string>; - Dash: SimpleToken<'Dash'>; - RangeDash: SimpleToken<'RangeDash'>; - Plus: SimpleToken<'Plus'>; - Star: SimpleToken<'Star'>; - Operator: ValueToken<'Operator', ComparatorOperator>; - Dot: SimpleToken<'Dot'>; - Pipe: SimpleToken<'Pipe'>; + Space: SimpleToken<'Space'>; + Number: ValueToken<'Number', number>; + Word: ValueToken<'Word', string>; + Dash: SimpleToken<'Dash'>; + RangeDash: SimpleToken<'RangeDash'>; + Plus: SimpleToken<'Plus'>; + Star: SimpleToken<'Star'>; + Operator: ValueToken<'Operator', ComparatorOperator>; + Dot: SimpleToken<'Dot'>; + Pipe: SimpleToken<'Pipe'>; }; // Types for public API diff --git a/packages/@romejs/codec-semver/utils.ts b/packages/@romejs/codec-semver/utils.ts index a1c38035b55..1ca70fb7043 100644 --- a/packages/@romejs/codec-semver/utils.ts +++ b/packages/@romejs/codec-semver/utils.ts @@ -7,31 +7,31 @@ import {AbsoluteVersionNode, RangeNode, UserRange, UserVersion} from './types'; import { - SemverParserOptions, - parseSemverRange, - parseSemverVersion, + SemverParserOptions, + parseSemverRange, + parseSemverVersion, } from './parse'; export function normalizeUserVersion( - ver: UserVersion, - opts?: SemverParserOptions, + ver: UserVersion, + opts?: SemverParserOptions, ): AbsoluteVersionNode { - if (typeof ver === 'string') { - return parseSemverVersion({...opts, input: ver}); - } else if (ver.type === 'AbsoluteVersion') { - return ver; - } else { - throw new Error(`Not a valid version: ${ver.type}`); - } + if (typeof ver === 'string') { + return parseSemverVersion({...opts, input: ver}); + } else if (ver.type === 'AbsoluteVersion') { + return ver; + } else { + throw new Error(`Not a valid version: ${ver.type}`); + } } export function normalizeUserRange( - range: UserRange, - opts?: SemverParserOptions, + range: UserRange, + opts?: SemverParserOptions, ): RangeNode { - if (typeof range === 'string') { - return parseSemverRange({...opts, input: range}); - } else { - return range; - } + if (typeof range === 'string') { + return parseSemverRange({...opts, input: range}); + } else { + return range; + } } diff --git a/packages/@romejs/codec-source-map/ArraySet.ts b/packages/@romejs/codec-source-map/ArraySet.ts index dc5c9129748..3d64eb11e3a 100644 --- a/packages/@romejs/codec-source-map/ArraySet.ts +++ b/packages/@romejs/codec-source-map/ArraySet.ts @@ -18,76 +18,76 @@ * strings are supported for membership. */ export default class ArraySet { - constructor() { - this.array = []; - this.set = new Map(); - } + constructor() { + this.array = []; + this.set = new Map(); + } - array: Array; - set: Map; + array: Array; + set: Map; - /** + /** * Static method for creating ArraySet instances from 'an existing array. */ - static fromArray(array: Array, allowDuplicates: boolean): ArraySet { - const set = new ArraySet(); - for (const item of array) { - set.add(item, allowDuplicates); - } - return set; - } + static fromArray(array: Array, allowDuplicates: boolean): ArraySet { + const set = new ArraySet(); + for (const item of array) { + set.add(item, allowDuplicates); + } + return set; + } - /** + /** * Add the given string to this set. */ - add(str: string, allowDuplicates?: boolean): void { - const isDuplicate = this.has(str); - const idx = this.array.length; + add(str: string, allowDuplicates?: boolean): void { + const isDuplicate = this.has(str); + const idx = this.array.length; - if (isDuplicate === false || allowDuplicates === true) { - this.array.push(str); - } + if (isDuplicate === false || allowDuplicates === true) { + this.array.push(str); + } - if (isDuplicate === false) { - this.set.set(str, idx); - } - } + if (isDuplicate === false) { + this.set.set(str, idx); + } + } - /** + /** * Is the given string a member of this set? */ - has(str: string): boolean { - return this.set.has(str); - } + has(str: string): boolean { + return this.set.has(str); + } - /** + /** * What is the index of the given string in the array? */ - indexOf(str: string): number { - const idx = this.set.get(str); - if (idx === undefined || idx < 0) { - throw new Error(`${str} is not in the set`); - } - return idx; - } + indexOf(str: string): number { + const idx = this.set.get(str); + if (idx === undefined || idx < 0) { + throw new Error(`${str} is not in the set`); + } + return idx; + } - /** + /** * What is the element at the given index? */ - at(idx: number): string { - if (idx >= 0 && idx < this.array.length) { - return this.array[idx]; - } else { - throw new Error(`No element indexed by ${idx}`); - } - } + at(idx: number): string { + if (idx >= 0 && idx < this.array.length) { + return this.array[idx]; + } else { + throw new Error(`No element indexed by ${idx}`); + } + } - /** + /** * Returns the array representation of this set (which has the proper indices * indicated by indexOf). Note that this is a copy of the internal array used * for storing the members so that no one can mess with internal state. */ - toArray(): Array { - return this.array.slice(); - } + toArray(): Array { + return this.array.slice(); + } } diff --git a/packages/@romejs/codec-source-map/MappingList.ts b/packages/@romejs/codec-source-map/MappingList.ts index 3d41773de54..63d52ecd24d 100644 --- a/packages/@romejs/codec-source-map/MappingList.ts +++ b/packages/@romejs/codec-source-map/MappingList.ts @@ -20,16 +20,16 @@ import {ob1Number0, ob1Number1Neg1} from '@romejs/ob1'; * position. */ function generatedPositionAfter(mappingA: Mapping, mappingB: Mapping): boolean { - // Optimized for most common case - const lineA = mappingA.generated.line; - const lineB = mappingB.generated.line; - const columnA = mappingA.generated.column; - const columnB = mappingB.generated.column; - return ( - lineB > lineA || - (lineB === lineA && columnB >= columnA) || - compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0 - ); + // Optimized for most common case + const lineA = mappingA.generated.line; + const lineB = mappingB.generated.line; + const columnA = mappingA.generated.column; + const columnB = mappingB.generated.column; + return ( + lineB > lineA || + (lineB === lineA && columnB >= columnA) || + compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0 + ); } /** @@ -38,38 +38,38 @@ function generatedPositionAfter(mappingA: Mapping, mappingB: Mapping): boolean { * case for a large speedup in case of mappings being added in order. */ export default class MappingList { - constructor() { - this.array = []; - this.sorted = true; - this.last = { - generated: {index: ob1Number0, line: ob1Number1Neg1, column: ob1Number0}, - // TODO: original: undefined - original: {line: ob1Number1Neg1, column: ob1Number0}, - source: undefined, - name: undefined, - }; - } + constructor() { + this.array = []; + this.sorted = true; + this.last = { + generated: {index: ob1Number0, line: ob1Number1Neg1, column: ob1Number0}, + // TODO: original: undefined + original: {line: ob1Number1Neg1, column: ob1Number0}, + source: undefined, + name: undefined, + }; + } - array: Array; - sorted: boolean; + array: Array; + sorted: boolean; - // Serves as infimum - last: Mapping; + // Serves as infimum + last: Mapping; - /** + /** * Add the given source mapping. */ - add(mapping: Mapping) { - if (generatedPositionAfter(this.last, mapping)) { - this.last = mapping; - this.array.push(mapping); - } else { - this.sorted = false; - this.array.push(mapping); - } - } + add(mapping: Mapping) { + if (generatedPositionAfter(this.last, mapping)) { + this.last = mapping; + this.array.push(mapping); + } else { + this.sorted = false; + this.array.push(mapping); + } + } - /** + /** * Returns the flat, sorted array of mappings. The mappings are sorted by * generated position. * @@ -78,11 +78,11 @@ export default class MappingList { * an immutable borrow. If you want to take ownership, you must make your own * copy. */ - toArray(): Array { - if (this.sorted === false) { - this.array.sort(compareByGeneratedPositionsInflated); - this.sorted = true; - } - return this.array; - } + toArray(): Array { + if (this.sorted === false) { + this.array.sort(compareByGeneratedPositionsInflated); + this.sorted = true; + } + return this.array; + } } diff --git a/packages/@romejs/codec-source-map/SourceMapConsumer.ts b/packages/@romejs/codec-source-map/SourceMapConsumer.ts index 972339b4728..9f565dcf2ba 100644 --- a/packages/@romejs/codec-source-map/SourceMapConsumer.ts +++ b/packages/@romejs/codec-source-map/SourceMapConsumer.ts @@ -6,213 +6,224 @@ */ import { - ParsedMapping, - ParsedMappings, - ResolvedLocation, - SourceMap, + ParsedMapping, + ParsedMappings, + ResolvedLocation, + SourceMap, } from './types'; import {decodeVLQ} from './base64'; import { - Number0, - Number1, - ob1Add, - ob1Dec, - ob1Get0, - ob1Inc, - ob1Number0, - ob1Number1, + Number0, + Number1, + ob1Add, + ob1Dec, + ob1Get0, + ob1Inc, + ob1Number0, + ob1Number1, } from '@romejs/ob1'; import {Dict} from '@romejs/typescript-helpers'; export function getParsedMappingKey(line: Number1, column: Number0): string { - return `${String(line)}:${String(column)}`; + return `${String(line)}:${String(column)}`; } type GetMappings = () => ParsedMappings; export default class SourceMapConsumer { - constructor(file: string, getMappings: GetMappings) { - this.file = file; - this._getMappings = getMappings; - this.mappings = undefined; - } - - file: string; - _getMappings: GetMappings; - mappings: undefined | ParsedMappings; - - static charIsMappingSeparator(str: string, index: number): boolean { - const c = str.charAt(index); - return c === ';' || c === ','; - } - - static fromJSON(sourceMap: SourceMap): SourceMapConsumer { - return new SourceMapConsumer( - sourceMap.file, - () => SourceMapConsumer.parseMappings(sourceMap), - ); - } - - static parseMappings(sourceMap: SourceMap): ParsedMappings { - const rawStr: string = sourceMap.mappings; - const map: ParsedMappings = new Map(); - - let generatedLine = ob1Number1; - let previousGeneratedColumn = ob1Number0; - let previousOriginalLine = ob1Number1; - let previousOriginalColumn = ob1Number0; - let previousSource = 0; - let previousName = 0; - let length = rawStr.length; - let index: number = 0; - let cachedSegments: Dict> = {}; - let value; - - while (index < length) { - const char = rawStr[index]; - if (char === ';') { - generatedLine = ob1Inc(generatedLine); - index++; - previousGeneratedColumn = ob1Number0; - } else if (char === ',') { - index++; - } else { - const mapping: ParsedMapping = { - generated: { - line: generatedLine, - column: ob1Number0, - }, - original: { - line: ob1Number1, - column: ob1Number0, - }, - source: undefined, - name: undefined, - }; - - // Because each offset is encoded relative to the previous one, - // many segments often have the same encoding. We can exploit this - // fact by caching the parsed variable length fields of each segment, - // allowing us to avoid a second parse if we encounter the same - // segment again. - let end = index; - for (; end < length; end++) { - if (SourceMapConsumer.charIsMappingSeparator(rawStr, end)) { - break; - } - } - const str = rawStr.slice(index, end); - - let segment = cachedSegments[str]; - if (segment) { - index += str.length; - } else { - segment = []; - while (index < end) { - [value, index] = decodeVLQ(rawStr, index); - segment.push(value); - } - - if (segment.length === 2) { - throw new Error('Found a source, but no line and column'); - } - - if (segment.length === 3) { - throw new Error('Found a source and line, but no column'); - } - - cachedSegments[str] = segment; - } - - // Generated column - mapping.generated.column = ob1Add(previousGeneratedColumn, segment[0]); - previousGeneratedColumn = mapping.generated.column; - - if (segment.length > 1) { - // Original source - mapping.source = sourceMap.sources[previousSource + segment[1]]; - previousSource += segment[1]; - - // Original line - const newOriginalLine = ob1Add(previousOriginalLine, segment[2]); - previousOriginalLine = newOriginalLine; - - // Lines are stored 0-based - mapping.original.line = ob1Add(newOriginalLine, 1); - - // Original column - const newOriginalColumn = ob1Add(previousOriginalColumn, segment[3]); - mapping.original.column = newOriginalColumn; - previousOriginalColumn = newOriginalColumn; - - if (segment.length > 4) { - // Original name - mapping.name = sourceMap.names[previousName + segment[4]]; - previousName += segment[4]; - } - } - - map.set( - getParsedMappingKey(mapping.generated.line, mapping.generated.column), - mapping, - ); - } - } - - return map; - } - - clearCache() { - this.mappings = undefined; - } - - getMappings(): ParsedMappings { - if (this.mappings === undefined) { - const mappings = this._getMappings(); - this.mappings = mappings; - return mappings; - } else { - return this.mappings; - } - } - - approxOriginalPositionFor( - line: Number1, - column: Number0, - ): undefined | ResolvedLocation { - while (ob1Get0(column) >= 0) { - const mapping = this.exactOriginalPositionFor(line, column); - if (mapping === undefined) { - column = ob1Dec(column); - continue; - } else { - return mapping; - } - } - - return undefined; - } - - exactOriginalPositionFor( - line: Number1, - column: Number0, - ): undefined | ResolvedLocation { - const key = getParsedMappingKey(line, column); - const mapping = this.getMappings().get(key); - if (mapping === undefined) { - return undefined; - } - - const source = mapping.source === undefined ? this.file : mapping.source; - if (source === undefined) { - throw new Error('Mapping provided unknown source'); - } - - return { - source, - line: mapping.original.line, - column: mapping.original.column, - name: mapping.name, - }; - } + constructor(file: string, getMappings: GetMappings) { + this.file = file; + this._getMappings = getMappings; + this.mappings = undefined; + } + + file: string; + _getMappings: GetMappings; + mappings: undefined | ParsedMappings; + + static charIsMappingSeparator(str: string, index: number): boolean { + const c = str.charAt(index); + return c === ';' || c === ','; + } + + static fromJSON(sourceMap: SourceMap): SourceMapConsumer { + return new SourceMapConsumer( + sourceMap.file, + () => SourceMapConsumer.parseMappings(sourceMap), + ); + } + + static fromJSONLazy( + file: string, + getSourceMap: () => SourceMap, + ): SourceMapConsumer { + return new SourceMapConsumer( + file, + () => SourceMapConsumer.parseMappings(getSourceMap()), + ); + } + + static parseMappings(sourceMap: SourceMap): ParsedMappings { + const rawStr: string = sourceMap.mappings; + const map: ParsedMappings = new Map(); + + let generatedLine = ob1Number1; + let previousGeneratedColumn = ob1Number0; + let previousOriginalLine = ob1Number1; + let previousOriginalColumn = ob1Number0; + let previousSource = 0; + let previousName = 0; + let length = rawStr.length; + let index: number = 0; + let cachedSegments: Dict> = {}; + let value; + + while (index < length) { + const char = rawStr[index]; + if (char === ';') { + generatedLine = ob1Inc(generatedLine); + index++; + previousGeneratedColumn = ob1Number0; + } else if (char === ',') { + index++; + } else { + const mapping: ParsedMapping = { + generated: { + line: generatedLine, + column: ob1Number0, + }, + original: { + line: ob1Number1, + column: ob1Number0, + }, + source: undefined, + name: undefined, + }; + + // Because each offset is encoded relative to the previous one, + // many segments often have the same encoding. We can exploit this + // fact by caching the parsed variable length fields of each segment, + // allowing us to avoid a second parse if we encounter the same + // segment again. + let end = index; + for (; end < length; end++) { + if (SourceMapConsumer.charIsMappingSeparator(rawStr, end)) { + break; + } + } + const str = rawStr.slice(index, end); + + let segment = cachedSegments[str]; + if (segment) { + index += str.length; + } else { + segment = []; + while (index < end) { + [value, index] = decodeVLQ(rawStr, index); + segment.push(value); + } + + if (segment.length === 2) { + throw new Error('Found a source, but no line and column'); + } + + if (segment.length === 3) { + throw new Error('Found a source and line, but no column'); + } + + cachedSegments[str] = segment; + } + + // Generated column + mapping.generated.column = ob1Add(previousGeneratedColumn, segment[0]); + previousGeneratedColumn = mapping.generated.column; + + if (segment.length > 1) { + // Original source + mapping.source = sourceMap.sources[previousSource + segment[1]]; + previousSource += segment[1]; + + // Original line + const newOriginalLine = ob1Add(previousOriginalLine, segment[2]); + previousOriginalLine = newOriginalLine; + + // Lines are stored 0-based + mapping.original.line = ob1Add(newOriginalLine, 1); + + // Original column + const newOriginalColumn = ob1Add(previousOriginalColumn, segment[3]); + mapping.original.column = newOriginalColumn; + previousOriginalColumn = newOriginalColumn; + + if (segment.length > 4) { + // Original name + mapping.name = sourceMap.names[previousName + segment[4]]; + previousName += segment[4]; + } + } + + map.set( + getParsedMappingKey(mapping.generated.line, mapping.generated.column), + mapping, + ); + } + } + + return map; + } + + clearCache() { + this.mappings = undefined; + } + + getMappings(): ParsedMappings { + if (this.mappings === undefined) { + const mappings = this._getMappings(); + this.mappings = mappings; + return mappings; + } else { + return this.mappings; + } + } + + approxOriginalPositionFor( + line: Number1, + column: Number0, + ): undefined | ResolvedLocation { + while (ob1Get0(column) >= 0) { + const mapping = this.exactOriginalPositionFor(line, column); + if (mapping === undefined) { + column = ob1Dec(column); + continue; + } else { + return mapping; + } + } + + return undefined; + } + + exactOriginalPositionFor( + line: Number1, + column: Number0, + ): undefined | ResolvedLocation { + const key = getParsedMappingKey(line, column); + const mapping = this.getMappings().get(key); + if (mapping === undefined) { + return undefined; + } + + const source = mapping.source === undefined ? this.file : mapping.source; + if (source === undefined) { + throw new Error('Mapping provided unknown source'); + } + + return { + found: true, + source, + line: mapping.original.line, + column: mapping.original.column, + name: mapping.name, + }; + } } diff --git a/packages/@romejs/codec-source-map/SourceMapConsumerCollection.ts b/packages/@romejs/codec-source-map/SourceMapConsumerCollection.ts index d6a98a5e46b..bb0f3649d97 100644 --- a/packages/@romejs/codec-source-map/SourceMapConsumerCollection.ts +++ b/packages/@romejs/codec-source-map/SourceMapConsumerCollection.ts @@ -10,51 +10,96 @@ import {Number0, Number1} from '@romejs/ob1'; import {ResolvedLocation} from './types'; export default class SourceMapConsumerCollection { - constructor() { - this.maps = new Map(); - } - - maps: Map; - - hasAny(): boolean { - return this.maps.size > 0; - } - - has(file: undefined | string): boolean { - return file !== undefined && this.maps.has(file); - } - - add(file: string, map: SourceMapConsumer) { - this.maps.set(file, map); - } - - get(file: string): undefined | SourceMapConsumer { - return this.maps.get(file); - } - - approxOriginalPositionFor( - file: string, - line: Number1, - column: Number0, - ): undefined | ResolvedLocation { - const map = this.get(file); - if (map === undefined) { - return undefined; - } else { - return map.approxOriginalPositionFor(line, column); - } - } - - exactOriginalPositionFor( - file: string, - line: Number1, - column: Number0, - ): undefined | ResolvedLocation { - const map = this.get(file); - if (map === undefined) { - return undefined; - } else { - return map.exactOriginalPositionFor(line, column); - } - } + constructor() { + this.maps = new Map(); + } + + maps: Map; + + hasAny(): boolean { + return this.maps.size > 0; + } + + has(file: undefined | string): boolean { + return file !== undefined && this.maps.has(file); + } + + add(file: string, map: SourceMapConsumer) { + this.maps.set(file, map); + } + + get(file: string): undefined | SourceMapConsumer { + return this.maps.get(file); + } + + normalizeResolved( + source: string, + line: Number1, + column: Number0, + loc: undefined | ResolvedLocation, + ): ResolvedLocation { + if (loc === undefined) { + return { + found: false, + source, + line, + column, + name: undefined, + }; + } else { + return loc; + } + } + + assertApproxOriginalPositionFor( + file: string, + line: Number1, + column: Number0, + ): ResolvedLocation { + return this.normalizeResolved( + file, + line, + column, + this.approxOriginalPositionFor(file, line, column), + ); + } + + assertExactOriginalPositionFor( + file: string, + line: Number1, + column: Number0, + ): ResolvedLocation { + return this.normalizeResolved( + file, + line, + column, + this.exactOriginalPositionFor(file, line, column), + ); + } + + approxOriginalPositionFor( + file: string, + line: Number1, + column: Number0, + ): undefined | ResolvedLocation { + const map = this.get(file); + if (map === undefined) { + return undefined; + } else { + return map.approxOriginalPositionFor(line, column); + } + } + + exactOriginalPositionFor( + file: string, + line: Number1, + column: Number0, + ): undefined | ResolvedLocation { + const map = this.get(file); + if (map === undefined) { + return undefined; + } else { + return map.exactOriginalPositionFor(line, column); + } + } } diff --git a/packages/@romejs/codec-source-map/SourceMapGenerator.ts b/packages/@romejs/codec-source-map/SourceMapGenerator.ts index 5fdb158a674..b317b17c9ec 100644 --- a/packages/@romejs/codec-source-map/SourceMapGenerator.ts +++ b/packages/@romejs/codec-source-map/SourceMapGenerator.ts @@ -17,58 +17,58 @@ import {compareByGeneratedPositionsInflated, toRelativeUrl} from './util'; import ArraySet from './ArraySet'; import MappingList from './MappingList'; import { - Number0, - Number1, - ob1Get0, - ob1Get1, - ob1Inc, - ob1Number0, - ob1Number1, + Number0, + Number1, + ob1Get0, + ob1Get1, + ob1Inc, + ob1Number0, + ob1Number1, } from '@romejs/ob1'; import SourceMapConsumer, {getParsedMappingKey} from './SourceMapConsumer'; type MaterializeCallback = () => void; export default class SourceMapGenerator { - constructor( - args: { - file: string; - sourceRoot?: string; - }, - ) { - this.file = args.file; - this.sourceRoot = args.sourceRoot; - - this.sourcesContents = new Map(); - this.map = undefined; - this.sources = new ArraySet(); - this.names = new ArraySet(); - this.mappings = new MappingList(); - this.materializeCallbacks = []; - } - - file: string; - materializeCallbacks: Array; - sourceRoot: undefined | string; - sources: ArraySet; - names: ArraySet; - mappings: MappingList; - sourcesContents: Map; - map: undefined | SourceMap; - - assertUnlocked() { - if (this.map !== undefined) { - throw new Error( - 'Source map has already been materialized, serialize() should be your final call', - ); - } - } - - addMaterializer(fn: MaterializeCallback) { - this.materializeCallbacks.push(fn); - } - - /** + constructor( + args: { + file: string; + sourceRoot?: string; + }, + ) { + this.file = args.file; + this.sourceRoot = args.sourceRoot; + + this.sourcesContents = new Map(); + this.map = undefined; + this.sources = new ArraySet(); + this.names = new ArraySet(); + this.mappings = new MappingList(); + this.materializeCallbacks = []; + } + + file: string; + materializeCallbacks: Array; + sourceRoot: undefined | string; + sources: ArraySet; + names: ArraySet; + mappings: MappingList; + sourcesContents: Map; + map: undefined | SourceMap; + + assertUnlocked() { + if (this.map !== undefined) { + throw new Error( + 'Source map has already been materialized, serialize() should be your final call', + ); + } + } + + addMaterializer(fn: MaterializeCallback) { + this.materializeCallbacks.push(fn); + } + + /** * Add a single mapping from 'original source line and column to the generated * source's line and column for this source map being created. The mapping * object should have the following properties: @@ -78,214 +78,211 @@ export default class SourceMapGenerator { * - source: The original source file (relative to the sourceRoot). * - name: An optional original token name for this mapping. */ - addMapping(mapping: Mapping): void { - this.assertUnlocked(); + addMapping(mapping: Mapping): void { + this.assertUnlocked(); - const {name, source} = mapping; + const {name, source} = mapping; - this.validatePosition( - 'generated', - mapping.generated.line, - mapping.generated.column, - ); + this.validatePosition( + 'generated', + mapping.generated.line, + mapping.generated.column, + ); - if (mapping.original) { - this.validatePosition( - 'original', - mapping.original.line, - mapping.original.column, - ); - } + if (mapping.original) { + this.validatePosition( + 'original', + mapping.original.line, + mapping.original.column, + ); + } - if (source !== undefined) { - this.sources.add(source); - } + if (source !== undefined) { + this.sources.add(source); + } - if (name !== undefined) { - this.names.add(name); - } + if (name !== undefined) { + this.names.add(name); + } - this.mappings.add(mapping); - } + this.mappings.add(mapping); + } - /** + /** * Set the source content for a source file. */ - setSourceContent(source: string, sourceContent: undefined | string): void { - this.assertUnlocked(); - - if (this.sourceRoot !== undefined) { - source = toRelativeUrl(this.sourceRoot, source); - } - - if (sourceContent !== undefined) { - // Add the source content to the _sourcesContents map. - this.sourcesContents.set(source, sourceContent); - } else { - // Remove the source file from the _sourcesContents map. - this.sourcesContents.delete(source); - } - } - - validatePosition(key: string, line: Number1, column: Number0): void { - if (ob1Get1(line) <= 0) { - throw new Error(`${key} line should be >= 1 but is ${line}`); - } - - if (ob1Get0(column) < 0) { - throw new Error(`${key} column should be >= 0 but is ${column}`); - } - } - - /** + setSourceContent(source: string, sourceContent: undefined | string): void { + this.assertUnlocked(); + + if (this.sourceRoot !== undefined) { + source = toRelativeUrl(this.sourceRoot, source); + } + + if (sourceContent !== undefined) { + // Add the source content to the _sourcesContents map. + this.sourcesContents.set(source, sourceContent); + } else { + // Remove the source file from the _sourcesContents map. + this.sourcesContents.delete(source); + } + } + + validatePosition(key: string, line: Number1, column: Number0): void { + if (ob1Get1(line) <= 0) { + throw new Error(`${key} line should be >= 1 but is ${line}`); + } + + if (ob1Get0(column) < 0) { + throw new Error(`${key} column should be >= 0 but is ${column}`); + } + } + + /** * Serialize the accumulated mappings in to the stream of base 64 VLQs * specified by the source map format. */ - serializeMappings(): string { - let previousGeneratedColumn: Number0 = ob1Number0; - let previousGeneratedLine: Number1 = ob1Number1; - let previousOriginalColumn: Number0 = ob1Number0; - let previousOriginalLine: Number1 = ob1Number1; - let previousName: number = 0; - let previousSource: number = 0; - let result: string = ''; - - const mappings = this.mappings.toArray(); - for (let i = 0; i < mappings.length; i++) { - const mapping = mappings[i]; - let next = ''; - - if (mapping.generated.line !== previousGeneratedLine) { - previousGeneratedColumn = ob1Number0; - while (mapping.generated.line !== previousGeneratedLine) { - next += ';'; - previousGeneratedLine = ob1Inc(previousGeneratedLine); - } - } else if (i > 0) { - if (!compareByGeneratedPositionsInflated(mapping, mappings[i - 1])) { - continue; - } - next += ','; - } - - next += base64.encodeVLQ( - ob1Get0(mapping.generated.column) - ob1Get0(previousGeneratedColumn), - ); - previousGeneratedColumn = mapping.generated.column; - - if (mapping.source !== undefined) { - const sourceIdx = this.sources.indexOf(mapping.source); - next += base64.encodeVLQ(sourceIdx - previousSource); - previousSource = sourceIdx; - - if (mapping.original) { - next += base64.encodeVLQ( - ob1Get1(mapping.original.line) - ob1Get1(previousOriginalLine), - ); - previousOriginalLine = mapping.original.line; - - next += base64.encodeVLQ( - ob1Get0(mapping.original.column) - ob1Get0(previousOriginalColumn), - ); - previousOriginalColumn = mapping.original.column; - - if (mapping.name !== undefined) { - const nameIdx = this.names.indexOf(mapping.name); - next += base64.encodeVLQ(nameIdx - previousName); - previousName = nameIdx; - } - } - - // TODO: else, assert mapping.name is undefined since it can't be encoded without an original position - } - - // TODO: else, assert mapping.original is undefined since it can't be encoded without a source - result += next; - } - - return result; - } - - generateSourcesContent( - sources: Array, - sourceRoot: undefined | string, - ): Array { - return sources.map((source) => { - if (sourceRoot !== undefined) { - source = toRelativeUrl(sourceRoot, source); - } - const content = this.sourcesContents.get(source); - if (content === undefined) { - throw new Error('Expected content'); - } - return content; - }); - } - - materialize() { - for (const fn of this.materializeCallbacks) { - fn(); - } - this.materializeCallbacks = []; - } - - /** + serializeMappings(): string { + let previousGeneratedColumn: Number0 = ob1Number0; + let previousGeneratedLine: Number1 = ob1Number1; + let previousOriginalColumn: Number0 = ob1Number0; + let previousOriginalLine: Number1 = ob1Number1; + let previousName: number = 0; + let previousSource: number = 0; + let result: string = ''; + + const mappings = this.mappings.toArray(); + for (let i = 0; i < mappings.length; i++) { + const mapping = mappings[i]; + let next = ''; + + if (mapping.generated.line !== previousGeneratedLine) { + previousGeneratedColumn = ob1Number0; + while (mapping.generated.line !== previousGeneratedLine) { + next += ';'; + previousGeneratedLine = ob1Inc(previousGeneratedLine); + } + } else if (i > 0) { + if (!compareByGeneratedPositionsInflated(mapping, mappings[i - 1])) { + continue; + } + next += ','; + } + + next += base64.encodeVLQ( + ob1Get0(mapping.generated.column) - ob1Get0(previousGeneratedColumn), + ); + previousGeneratedColumn = mapping.generated.column; + + if (mapping.source !== undefined) { + const sourceIdx = this.sources.indexOf(mapping.source); + next += base64.encodeVLQ(sourceIdx - previousSource); + previousSource = sourceIdx; + + if (mapping.original) { + next += base64.encodeVLQ( + ob1Get1(mapping.original.line) - ob1Get1(previousOriginalLine), + ); + previousOriginalLine = mapping.original.line; + + next += base64.encodeVLQ( + ob1Get0(mapping.original.column) - ob1Get0(previousOriginalColumn), + ); + previousOriginalColumn = mapping.original.column; + + if (mapping.name !== undefined) { + const nameIdx = this.names.indexOf(mapping.name); + next += base64.encodeVLQ(nameIdx - previousName); + previousName = nameIdx; + } + } + + // TODO: else, assert mapping.name is undefined since it can't be encoded without an original position + } + + // TODO: else, assert mapping.original is undefined since it can't be encoded without a source + result += next; + } + + return result; + } + + generateSourcesContent( + sources: Array, + sourceRoot: undefined | string, + ): Array { + return sources.map((source) => { + if (sourceRoot !== undefined) { + source = toRelativeUrl(sourceRoot, source); + } + const content = this.sourcesContents.get(source); + if (content === undefined) { + throw new Error('Expected content'); + } + return content; + }); + } + + materialize() { + for (const fn of this.materializeCallbacks) { + fn(); + } + this.materializeCallbacks = []; + } + + /** * Externalize the source map. */ - serialize(): SourceMap { - if (this.map !== undefined) { - return this.map; - } - - this.materialize(); - - const sources = this.sources.toArray(); - this.map = { - version: 3, - file: this.file, - names: this.names.toArray(), - mappings: this.serializeMappings(), - sourceRoot: this.sourceRoot, - sources, - sourcesContent: this.generateSourcesContent(sources, this.sourceRoot), - }; - return this.map; - } - - toComment(): string { - const jsonMap = this.toJSON(); - const base64Map = new Buffer(jsonMap).toString('base64'); - const comment = `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${base64Map}`; - return comment; - } - - toConsumer(): SourceMapConsumer { - return new SourceMapConsumer( - this.file, - () => { - const parsedMappings: ParsedMappings = new Map(); - - for (const mapping of this.getMappings()) { - parsedMappings.set( - getParsedMappingKey( - mapping.generated.line, - mapping.generated.column, - ), - mapping, - ); - } - - return parsedMappings; - }, - ); - } - - getMappings(): Mappings { - this.materialize(); - return this.mappings.toArray(); - } - - toJSON(): string { - return JSON.stringify(this.serialize()); - } + serialize(): SourceMap { + if (this.map !== undefined) { + return this.map; + } + + this.materialize(); + + const sources = this.sources.toArray(); + this.map = { + version: 3, + file: this.file, + names: this.names.toArray(), + mappings: this.serializeMappings(), + sourceRoot: this.sourceRoot, + sources, + sourcesContent: this.generateSourcesContent(sources, this.sourceRoot), + }; + return this.map; + } + + toComment(): string { + const jsonMap = this.toJSON(); + const base64Map = new Buffer(jsonMap).toString('base64'); + const comment = `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${base64Map}`; + return comment; + } + + toConsumer(): SourceMapConsumer { + return new SourceMapConsumer( + this.file, + () => { + const parsedMappings: ParsedMappings = new Map(); + + for (const mapping of this.getMappings()) { + parsedMappings.set( + getParsedMappingKey(mapping.generated.line, mapping.generated.column), + mapping, + ); + } + + return parsedMappings; + }, + ); + } + + getMappings(): Mappings { + this.materialize(); + return this.mappings.toArray(); + } + + toJSON(): string { + return JSON.stringify(this.serialize()); + } } diff --git a/packages/@romejs/codec-source-map/base64.ts b/packages/@romejs/codec-source-map/base64.ts index 779620a6413..7b684a0695d 100644 --- a/packages/@romejs/codec-source-map/base64.ts +++ b/packages/@romejs/codec-source-map/base64.ts @@ -43,18 +43,18 @@ */ const intToCharMap = Array.from( - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', ); /** * Encode an integer in the range of 0 to 63 to a single base 64 digit. */ export function encode(number: number): string { - if (0 <= number && number < intToCharMap.length) { - return intToCharMap[number]; - } else { - throw new TypeError(`Must be between 0 and 63: ${number}`); - } + if (0 <= number && number < intToCharMap.length) { + return intToCharMap[number]; + } else { + throw new TypeError(`Must be between 0 and 63: ${number}`); + } } // A single base 64 digit can contain 6 bits of data. For the base 64 variable @@ -86,7 +86,7 @@ const VLQ_CONTINUATION_BIT = VLQ_BASE; * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) */ function toVLQSigned(aValue: number): number { - return aValue < 0 ? (-aValue << 1) + 1 : (aValue << 1) + 0; + return aValue < 0 ? (-aValue << 1) + 1 : (aValue << 1) + 0; } /** @@ -97,30 +97,30 @@ function toVLQSigned(aValue: number): number { */ // eslint-disable-next-line no-unused-vars function fromVLQSigned(value: number): number { - const isNegative = (value & 1) === 1; - const shifted = value >> 1; - return isNegative ? -shifted : shifted; + const isNegative = (value & 1) === 1; + const shifted = value >> 1; + return isNegative ? -shifted : shifted; } /** * Returns the base 64 VLQ encoded value. */ export function encodeVLQ(value: number): string { - let encoded: string = ''; - let vlq = toVLQSigned(value); - - do { - let digit: number = vlq & VLQ_BASE_MASK; - vlq >>>= VLQ_BASE_SHIFT; - if (vlq > 0) { - // There are still more digits in this value, so we must make sure the - // continuation bit is marked. - digit |= VLQ_CONTINUATION_BIT; - } - encoded += encode(digit); - } while (vlq > 0); - - return encoded; + let encoded: string = ''; + let vlq = toVLQSigned(value); + + do { + let digit: number = vlq & VLQ_BASE_MASK; + vlq >>>= VLQ_BASE_SHIFT; + if (vlq > 0) { + // There are still more digits in this value, so we must make sure the + // continuation bit is marked. + digit |= VLQ_CONTINUATION_BIT; + } + encoded += encode(digit); + } while (vlq > 0); + + return encoded; } /** @@ -128,67 +128,67 @@ export function encodeVLQ(value: number): string { * failure. */ export function decode(charCode: number): number { - const uppercaseA = 65; // 'A' - const uppercaseZ = 90; // 'Z' - const lowercaseA = 97; // 'a' - const lowercaseZ = 122; // 'z' - const zero = 48; // '0' - const nine = 57; // '9' - const plus = 43; // '+' - const slash = 47; // '/' - const lowercaseOffset = 26; - const numberOffset = 52; - - // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ - if (uppercaseA <= charCode && charCode <= uppercaseZ) { - return charCode - uppercaseA; - } - - // 26 - 51: abcdefghijklmnopqrstuvwxyz - if (lowercaseA <= charCode && charCode <= lowercaseZ) { - return charCode - lowercaseA + lowercaseOffset; - } - - // 52 - 61: 0123456789 - if (zero <= charCode && charCode <= nine) { - return charCode - zero + numberOffset; - } - - // 62: + - if (charCode === plus) { - return 62; - } - - // 63: / - if (charCode === slash) { - return 63; - } - - // Invalid base64 digit. - return -1; + const uppercaseA = 65; // 'A' + const uppercaseZ = 90; // 'Z' + const lowercaseA = 97; // 'a' + const lowercaseZ = 122; // 'z' + const zero = 48; // '0' + const nine = 57; // '9' + const plus = 43; // '+' + const slash = 47; // '/' + const lowercaseOffset = 26; + const numberOffset = 52; + + // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ + if (uppercaseA <= charCode && charCode <= uppercaseZ) { + return charCode - uppercaseA; + } + + // 26 - 51: abcdefghijklmnopqrstuvwxyz + if (lowercaseA <= charCode && charCode <= lowercaseZ) { + return charCode - lowercaseA + lowercaseOffset; + } + + // 52 - 61: 0123456789 + if (zero <= charCode && charCode <= nine) { + return charCode - zero + numberOffset; + } + + // 62: + + if (charCode === plus) { + return 62; + } + + // 63: / + if (charCode === slash) { + return 63; + } + + // Invalid base64 digit. + return -1; } export function decodeVLQ(aStr: string, aIndex: number): [number, number] { - let strLen = aStr.length; - let result = 0; - let shift = 0; - let continuation = false; - - do { - if (aIndex >= strLen) { - throw new Error('Expected more digits in base 64 VLQ value.'); - } - - let digit = decode(aStr.charCodeAt(aIndex++)); - if (digit === -1) { - throw new Error(`Invalid base64 digit: ${aStr.charAt(aIndex - 1)}`); - } - - continuation = !!(digit & VLQ_CONTINUATION_BIT); - digit &= VLQ_BASE_MASK; - result = result + (digit << shift); - shift += VLQ_BASE_SHIFT; - } while (continuation); - - return [fromVLQSigned(result), aIndex]; + let strLen = aStr.length; + let result = 0; + let shift = 0; + let continuation = false; + + do { + if (aIndex >= strLen) { + throw new Error('Expected more digits in base 64 VLQ value.'); + } + + let digit = decode(aStr.charCodeAt(aIndex++)); + if (digit === -1) { + throw new Error(`Invalid base64 digit: ${aStr.charAt(aIndex - 1)}`); + } + + continuation = !!(digit & VLQ_CONTINUATION_BIT); + digit &= VLQ_BASE_MASK; + result = result + (digit << shift); + shift += VLQ_BASE_SHIFT; + } while (continuation); + + return [fromVLQSigned(result), aIndex]; } diff --git a/packages/@romejs/codec-source-map/types.ts b/packages/@romejs/codec-source-map/types.ts index 10a6862b1f9..e26401c8245 100644 --- a/packages/@romejs/codec-source-map/types.ts +++ b/packages/@romejs/codec-source-map/types.ts @@ -10,44 +10,45 @@ import {Number0, Number1} from '@romejs/ob1'; export type Mappings = Array; export type ParsedMapping = { - generated: { - line: Number1; - column: Number0; - }; - original: { - line: Number1; - column: Number0; - }; - source: undefined | string; - name: undefined | string; + generated: { + line: Number1; + column: Number0; + }; + original: { + line: Number1; + column: Number0; + }; + source: undefined | string; + name: undefined | string; }; export type Mapping = Omit & { - generated: ParsedMapping['generated'] & { - index: Number0; - }; + generated: ParsedMapping['generated'] & { + index: Number0; + }; }; export type ParsedMappings = Map; export type ResolvedLocation = { - source: string; - line: Number1; - column: Number0; - name: undefined | string; + found: boolean; + source: string; + line: Number1; + column: Number0; + name: undefined | string; }; export type SourceMapGeneratorOptions = { - file?: string; - sourceRoot?: string; + file?: string; + sourceRoot?: string; }; export type SourceMap = { - version: number; - file: string; - names: Array; - mappings: string; - sourceRoot: undefined | string; - sources: Array; - sourcesContent: Array; + version: number; + file: string; + names: Array; + mappings: string; + sourceRoot: undefined | string; + sources: Array; + sourcesContent: Array; }; diff --git a/packages/@romejs/codec-source-map/util.ts b/packages/@romejs/codec-source-map/util.ts index 72fcfdfba85..d8a562c29d5 100644 --- a/packages/@romejs/codec-source-map/util.ts +++ b/packages/@romejs/codec-source-map/util.ts @@ -15,23 +15,23 @@ import {Mapping} from './types'; import {ob1Get0, ob1Get1} from '@romejs/ob1'; function strcmp(a: undefined | string, b: undefined | string): number { - if (a === b) { - return 0; - } + if (a === b) { + return 0; + } - if (a === undefined) { - return 1; - } + if (a === undefined) { + return 1; + } - if (b === undefined) { - return -1; - } + if (b === undefined) { + return -1; + } - if (a > b) { - return 1; - } + if (a > b) { + return 1; + } - return -1; + return -1; } /** @@ -39,84 +39,84 @@ function strcmp(a: undefined | string, b: undefined | string): number { * the generated positions are compared. */ export function compareByGeneratedPositionsInflated( - mappingA: Mapping, - mappingB: Mapping, + mappingA: Mapping, + mappingB: Mapping, ): number { - let cmp: number = - ob1Get1(mappingA.generated.line) - ob1Get1(mappingB.generated.line); - if (cmp !== 0) { - return cmp; - } - - cmp = ob1Get0(mappingA.generated.column) - ob1Get0(mappingB.generated.column); - if (cmp !== 0) { - return cmp; - } - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp !== 0) { - return cmp; - } - - if (mappingA.original == null) { - if (mappingB.original != null) { - return 1; - } - } else if (mappingB.original == null) { - return -1; - } else { - cmp = ob1Get1(mappingA.original.line) - ob1Get1(mappingB.original.line); - if (cmp !== 0) { - return cmp; - } - - cmp = ob1Get0(mappingA.original.column) - ob1Get0(mappingB.original.column); - if (cmp !== 0) { - return cmp; - } - } - - return strcmp(mappingA.name, mappingB.name); + let cmp: number = + ob1Get1(mappingA.generated.line) - ob1Get1(mappingB.generated.line); + if (cmp !== 0) { + return cmp; + } + + cmp = ob1Get0(mappingA.generated.column) - ob1Get0(mappingB.generated.column); + if (cmp !== 0) { + return cmp; + } + + cmp = strcmp(mappingA.source, mappingB.source); + if (cmp !== 0) { + return cmp; + } + + if (mappingA.original == null) { + if (mappingB.original != null) { + return 1; + } + } else if (mappingB.original == null) { + return -1; + } else { + cmp = ob1Get1(mappingA.original.line) - ob1Get1(mappingB.original.line); + if (cmp !== 0) { + return cmp; + } + + cmp = ob1Get0(mappingA.original.column) - ob1Get0(mappingB.original.column); + if (cmp !== 0) { + return cmp; + } + } + + return strcmp(mappingA.name, mappingB.name); } /** * Make a path relative to a URL or another path. */ export function toRelativeUrl(root: string, path: string): string { - if (root === '') { - root = '.'; - } + if (root === '') { + root = '.'; + } - root = root.replace(/\/$/, ''); + root = root.replace(/\/$/, ''); - // It is possible for the path to be above the root. In this case, simply + // It is possible for the path to be above the root. In this case, simply - // checking whether the root is a prefix of the path won't work. Instead, we + // checking whether the root is a prefix of the path won't work. Instead, we - // need to remove components from the root one by one, until either we find + // need to remove components from the root one by one, until either we find - // a prefix that fits, or we run out of components to remove. - let level = 0; - while (path.indexOf(`${root}/`) !== 0) { - const index = root.lastIndexOf('/'); - if (index < 0) { - return path; - } + // a prefix that fits, or we run out of components to remove. + let level = 0; + while (path.indexOf(`${root}/`) !== 0) { + const index = root.lastIndexOf('/'); + if (index < 0) { + return path; + } - // If the only part of the root that is left is the scheme (i.e. http://, + // If the only part of the root that is left is the scheme (i.e. http://, - // file:///, etc.), one or more slashes (/), or simply nothing at all, we + // file:///, etc.), one or more slashes (/), or simply nothing at all, we - // have exhausted all components, so the path is not relative to the root. - root = root.slice(0, index); - if (root.match(/^([^\/]+:\/)?\/*$/)) { - return path; - } + // have exhausted all components, so the path is not relative to the root. + root = root.slice(0, index); + if (root.match(/^([^\/]+:\/)?\/*$/)) { + return path; + } - ++level; - } + ++level; + } - // Make sure we add a '../' for each component we removed from the root. + // Make sure we add a '../' for each component we removed from the root. - return '../'.repeat(level) + path.substr(root.length + 1); + return '../'.repeat(level) + path.substr(root.length + 1); } diff --git a/packages/@romejs/codec-spdx-license/data.ts b/packages/@romejs/codec-spdx-license/data.ts index 687357a28cb..ce9ce34b5b3 100644 --- a/packages/@romejs/codec-spdx-license/data.ts +++ b/packages/@romejs/codec-spdx-license/data.ts @@ -6,4638 +6,4619 @@ */ export default { - licenseListVersion: '3.8-4-gd79c632', - licenses: [ - { - reference: './0BSD.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/0BSD.json', - referenceNumber: '231', - name: 'BSD Zero Clause License', - licenseId: '0BSD', - seeAlso: ['http://landley.net/toybox/license.html'], - isOsiApproved: true, - }, - { - reference: './AAL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/AAL.json', - referenceNumber: '57', - name: 'Attribution Assurance License', - licenseId: 'AAL', - seeAlso: ['https://opensource.org/licenses/attribution'], - isOsiApproved: true, - }, - { - reference: './ADSL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/ADSL.json', - referenceNumber: '210', - name: 'Amazon Digital Services License', - licenseId: 'ADSL', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/AmazonDigitalServicesLicense', - ], - isOsiApproved: false, - }, - { - reference: './AFL-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/AFL-1.1.json', - referenceNumber: '28', - name: 'Academic Free License v1.1', - licenseId: 'AFL-1.1', - seeAlso: [ - 'http://opensource.linux-mirror.org/licenses/afl-1.1.txt', - 'http://wayback.archive.org/web/20021004124254/http://www.opensource.org/licenses/academic.php', - ], - isOsiApproved: true, - }, - { - reference: './AFL-1.2.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/AFL-1.2.json', - referenceNumber: '215', - name: 'Academic Free License v1.2', - licenseId: 'AFL-1.2', - seeAlso: [ - 'http://opensource.linux-mirror.org/licenses/afl-1.2.txt', - 'http://wayback.archive.org/web/20021204204652/http://www.opensource.org/licenses/academic.php', - ], - isOsiApproved: true, - }, - { - reference: './AFL-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/AFL-2.0.json', - referenceNumber: '330', - name: 'Academic Free License v2.0', - licenseId: 'AFL-2.0', - seeAlso: [ - 'http://wayback.archive.org/web/20060924134533/http://www.opensource.org/licenses/afl-2.0.txt', - ], - isOsiApproved: true, - }, - { - reference: './AFL-2.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/AFL-2.1.json', - referenceNumber: '241', - name: 'Academic Free License v2.1', - licenseId: 'AFL-2.1', - seeAlso: ['http://opensource.linux-mirror.org/licenses/afl-2.1.txt'], - isOsiApproved: true, - }, - { - reference: './AFL-3.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/AFL-3.0.json', - referenceNumber: '350', - name: 'Academic Free License v3.0', - licenseId: 'AFL-3.0', - seeAlso: [ - 'http://www.rosenlaw.com/AFL3.0.htm', - 'https://opensource.org/licenses/afl-3.0', - ], - isOsiApproved: true, - }, - { - reference: './AGPL-1.0.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/AGPL-1.0.json', - referenceNumber: '169', - name: 'Affero General Public License v1.0', - licenseId: 'AGPL-1.0', - seeAlso: ['http://www.affero.org/oagpl.html'], - isOsiApproved: false, - }, - { - reference: './AGPL-1.0-only.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/AGPL-1.0-only.json', - referenceNumber: '68', - name: 'Affero General Public License v1.0 only', - licenseId: 'AGPL-1.0-only', - seeAlso: ['http://www.affero.org/oagpl.html'], - isOsiApproved: false, - }, - { - reference: './AGPL-1.0-or-later.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/AGPL-1.0-or-later.json', - referenceNumber: '162', - name: 'Affero General Public License v1.0 or later', - licenseId: 'AGPL-1.0-or-later', - seeAlso: ['http://www.affero.org/oagpl.html'], - isOsiApproved: false, - }, - { - reference: './AGPL-3.0.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/AGPL-3.0.json', - referenceNumber: '143', - name: 'GNU Affero General Public License v3.0', - licenseId: 'AGPL-3.0', - seeAlso: [ - 'https://www.gnu.org/licenses/agpl.txt', - 'https://opensource.org/licenses/AGPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './AGPL-3.0-only.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/AGPL-3.0-only.json', - referenceNumber: '287', - name: 'GNU Affero General Public License v3.0 only', - licenseId: 'AGPL-3.0-only', - seeAlso: [ - 'https://www.gnu.org/licenses/agpl.txt', - 'https://opensource.org/licenses/AGPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './AGPL-3.0-or-later.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/AGPL-3.0-or-later.json', - referenceNumber: '154', - name: 'GNU Affero General Public License v3.0 or later', - licenseId: 'AGPL-3.0-or-later', - seeAlso: [ - 'https://www.gnu.org/licenses/agpl.txt', - 'https://opensource.org/licenses/AGPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './AMDPLPA.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/AMDPLPA.json', - referenceNumber: '128', - name: "AMD's plpa_map.c License", - licenseId: 'AMDPLPA', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/AMD_plpa_map_License'], - isOsiApproved: false, - }, - { - reference: './AML.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/AML.json', - referenceNumber: '151', - name: 'Apple MIT License', - licenseId: 'AML', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Apple_MIT_License'], - isOsiApproved: false, - }, - { - reference: './AMPAS.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/AMPAS.json', - referenceNumber: '131', - name: 'Academy of Motion Picture Arts and Sciences BSD', - licenseId: 'AMPAS', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/BSD#AMPASBSD'], - isOsiApproved: false, - }, - { - reference: './ANTLR-PD.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/ANTLR-PD.json', - referenceNumber: '44', - name: 'ANTLR Software Rights Notice', - licenseId: 'ANTLR-PD', - seeAlso: ['http://www.antlr2.org/license.html'], - isOsiApproved: false, - }, - { - reference: './APAFML.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/APAFML.json', - referenceNumber: '240', - name: 'Adobe Postscript AFM License', - licenseId: 'APAFML', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/AdobePostscriptAFM'], - isOsiApproved: false, - }, - { - reference: './APL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/APL-1.0.json', - referenceNumber: '268', - name: 'Adaptive Public License 1.0', - licenseId: 'APL-1.0', - seeAlso: ['https://opensource.org/licenses/APL-1.0'], - isOsiApproved: true, - }, - { - reference: './APSL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/APSL-1.0.json', - referenceNumber: '375', - name: 'Apple Public Source License 1.0', - licenseId: 'APSL-1.0', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/Apple_Public_Source_License_1.0', - ], - isOsiApproved: true, - }, - { - reference: './APSL-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/APSL-1.1.json', - referenceNumber: '327', - name: 'Apple Public Source License 1.1', - licenseId: 'APSL-1.1', - seeAlso: [ - 'http://www.opensource.apple.com/source/IOSerialFamily/IOSerialFamily-7/APPLE_LICENSE', - ], - isOsiApproved: true, - }, - { - reference: './APSL-1.2.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/APSL-1.2.json', - referenceNumber: '194', - name: 'Apple Public Source License 1.2', - licenseId: 'APSL-1.2', - seeAlso: ['http://www.samurajdata.se/opensource/mirror/licenses/apsl.php'], - isOsiApproved: true, - }, - { - reference: './APSL-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/APSL-2.0.json', - referenceNumber: '136', - name: 'Apple Public Source License 2.0', - licenseId: 'APSL-2.0', - seeAlso: ['http://www.opensource.apple.com/license/apsl/'], - isOsiApproved: true, - }, - { - reference: './Abstyles.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Abstyles.json', - referenceNumber: '72', - name: 'Abstyles License', - licenseId: 'Abstyles', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Abstyles'], - isOsiApproved: false, - }, - { - reference: './Adobe-2006.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Adobe-2006.json', - referenceNumber: '299', - name: 'Adobe Systems Incorporated Source Code License Agreement', - licenseId: 'Adobe-2006', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/AdobeLicense'], - isOsiApproved: false, - }, - { - reference: './Adobe-Glyph.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Adobe-Glyph.json', - referenceNumber: '332', - name: 'Adobe Glyph List License', - licenseId: 'Adobe-Glyph', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/MIT#AdobeGlyph'], - isOsiApproved: false, - }, - { - reference: './Afmparse.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Afmparse.json', - referenceNumber: '321', - name: 'Afmparse License', - licenseId: 'Afmparse', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Afmparse'], - isOsiApproved: false, - }, - { - reference: './Aladdin.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Aladdin.json', - referenceNumber: '304', - name: 'Aladdin Free Public License', - licenseId: 'Aladdin', - seeAlso: ['http://pages.cs.wisc.edu/~ghost/doc/AFPL/6.01/Public.htm'], - isOsiApproved: false, - }, - { - reference: './Apache-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Apache-1.0.json', - referenceNumber: '32', - name: 'Apache License 1.0', - licenseId: 'Apache-1.0', - seeAlso: ['http://www.apache.org/licenses/LICENSE-1.0'], - isOsiApproved: false, - }, - { - reference: './Apache-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Apache-1.1.json', - referenceNumber: '264', - name: 'Apache License 1.1', - licenseId: 'Apache-1.1', - seeAlso: [ - 'http://apache.org/licenses/LICENSE-1.1', - 'https://opensource.org/licenses/Apache-1.1', - ], - isOsiApproved: true, - }, - { - reference: './Apache-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Apache-2.0.json', - referenceNumber: '355', - name: 'Apache License 2.0', - licenseId: 'Apache-2.0', - seeAlso: [ - 'http://www.apache.org/licenses/LICENSE-2.0', - 'https://opensource.org/licenses/Apache-2.0', - ], - isOsiApproved: true, - }, - { - reference: './Artistic-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Artistic-1.0.json', - referenceNumber: '263', - name: 'Artistic License 1.0', - licenseId: 'Artistic-1.0', - seeAlso: ['https://opensource.org/licenses/Artistic-1.0'], - isOsiApproved: true, - }, - { - reference: './Artistic-1.0-Perl.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Artistic-1.0-Perl.json', - referenceNumber: '295', - name: 'Artistic License 1.0 (Perl)', - licenseId: 'Artistic-1.0-Perl', - seeAlso: ['http://dev.perl.org/licenses/artistic.html'], - isOsiApproved: true, - }, - { - reference: './Artistic-1.0-cl8.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Artistic-1.0-cl8.json', - referenceNumber: '221', - name: 'Artistic License 1.0 w/clause 8', - licenseId: 'Artistic-1.0-cl8', - seeAlso: ['https://opensource.org/licenses/Artistic-1.0'], - isOsiApproved: true, - }, - { - reference: './Artistic-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Artistic-2.0.json', - referenceNumber: '75', - name: 'Artistic License 2.0', - licenseId: 'Artistic-2.0', - seeAlso: [ - 'http://www.perlfoundation.org/artistic_license_2_0', - 'https://opensource.org/licenses/artistic-license-2.0', - ], - isOsiApproved: true, - }, - { - reference: './BSD-1-Clause.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-1-Clause.json', - referenceNumber: '377', - name: 'BSD 1-Clause License', - licenseId: 'BSD-1-Clause', - seeAlso: [ - 'https://svnweb.freebsd.org/base/head/include/ifaddrs.h?revision=326823', - ], - isOsiApproved: false, - }, - { - reference: './BSD-2-Clause.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-2-Clause.json', - referenceNumber: '296', - name: 'BSD 2-Clause "Simplified" License', - licenseId: 'BSD-2-Clause', - seeAlso: ['https://opensource.org/licenses/BSD-2-Clause'], - isOsiApproved: true, - }, - { - reference: './BSD-2-Clause-FreeBSD.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/BSD-2-Clause-FreeBSD.json', - referenceNumber: '271', - name: 'BSD 2-Clause FreeBSD License', - licenseId: 'BSD-2-Clause-FreeBSD', - seeAlso: ['http://www.freebsd.org/copyright/freebsd-license.html'], - isOsiApproved: false, - }, - { - reference: './BSD-2-Clause-NetBSD.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-2-Clause-NetBSD.json', - referenceNumber: '179', - name: 'BSD 2-Clause NetBSD License', - licenseId: 'BSD-2-Clause-NetBSD', - seeAlso: ['http://www.netbsd.org/about/redistribution.html#default'], - isOsiApproved: false, - }, - { - reference: './BSD-2-Clause-Patent.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-2-Clause-Patent.json', - referenceNumber: '351', - name: 'BSD-2-Clause Plus Patent License', - licenseId: 'BSD-2-Clause-Patent', - seeAlso: ['https://opensource.org/licenses/BSDplusPatent'], - isOsiApproved: true, - }, - { - reference: './BSD-3-Clause.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause.json', - referenceNumber: '195', - name: 'BSD 3-Clause "New" or "Revised" License', - licenseId: 'BSD-3-Clause', - seeAlso: ['https://opensource.org/licenses/BSD-3-Clause'], - isOsiApproved: true, - }, - { - reference: './BSD-3-Clause-Attribution.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-Attribution.json', - referenceNumber: '39', - name: 'BSD with attribution', - licenseId: 'BSD-3-Clause-Attribution', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/BSD_with_Attribution'], - isOsiApproved: false, - }, - { - reference: './BSD-3-Clause-Clear.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-Clear.json', - referenceNumber: '84', - name: 'BSD 3-Clause Clear License', - licenseId: 'BSD-3-Clause-Clear', - seeAlso: ['http://labs.metacarta.com/license-explanation.html#license'], - isOsiApproved: false, - }, - { - reference: './BSD-3-Clause-LBNL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-LBNL.json', - referenceNumber: '142', - name: 'Lawrence Berkeley National Labs BSD variant license', - licenseId: 'BSD-3-Clause-LBNL', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/LBNLBSD'], - isOsiApproved: true, - }, - { - reference: './BSD-3-Clause-No-Nuclear-License.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.json', - referenceNumber: '59', - name: 'BSD 3-Clause No Nuclear License', - licenseId: 'BSD-3-Clause-No-Nuclear-License', - seeAlso: [ - 'http://download.oracle.com/otn-pub/java/licenses/bsd.txt?AuthParam=1467140197_43d516ce1776bd08a58235a7785be1cc', - ], - isOsiApproved: false, - }, - { - reference: './BSD-3-Clause-No-Nuclear-License-2014.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.json', - referenceNumber: '331', - name: 'BSD 3-Clause No Nuclear License 2014', - licenseId: 'BSD-3-Clause-No-Nuclear-License-2014', - seeAlso: [ - 'https://java.net/projects/javaeetutorial/pages/BerkeleyLicense', - ], - isOsiApproved: false, - }, - { - reference: './BSD-3-Clause-No-Nuclear-Warranty.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.json', - referenceNumber: '113', - name: 'BSD 3-Clause No Nuclear Warranty', - licenseId: 'BSD-3-Clause-No-Nuclear-Warranty', - seeAlso: [ - 'https://jogamp.org/git/?p=gluegen.git;a=blob_plain;f=LICENSE.txt', - ], - isOsiApproved: false, - }, - { - reference: './BSD-3-Clause-Open-MPI.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-Open-MPI.json', - referenceNumber: '207', - name: 'BSD 3-Clause Open MPI variant', - licenseId: 'BSD-3-Clause-Open-MPI', - seeAlso: [ - 'https://www.open-mpi.org/community/license.php', - 'http://www.netlib.org/lapack/LICENSE.txt', - ], - isOsiApproved: false, - }, - { - reference: './BSD-4-Clause.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/BSD-4-Clause.json', - referenceNumber: '66', - name: 'BSD 4-Clause "Original" or "Old" License', - licenseId: 'BSD-4-Clause', - seeAlso: ['http://directory.fsf.org/wiki/License:BSD_4Clause'], - isOsiApproved: false, - }, - { - reference: './BSD-4-Clause-UC.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-4-Clause-UC.json', - referenceNumber: '361', - name: 'BSD-4-Clause (University of California-Specific)', - licenseId: 'BSD-4-Clause-UC', - seeAlso: ['http://www.freebsd.org/copyright/license.html'], - isOsiApproved: false, - }, - { - reference: './BSD-Protection.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-Protection.json', - referenceNumber: '388', - name: 'BSD Protection License', - licenseId: 'BSD-Protection', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/BSD_Protection_License', - ], - isOsiApproved: false, - }, - { - reference: './BSD-Source-Code.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BSD-Source-Code.json', - referenceNumber: '163', - name: 'BSD Source Code Attribution', - licenseId: 'BSD-Source-Code', - seeAlso: [ - 'https://github.com/robbiehanson/CocoaHTTPServer/blob/master/LICENSE.txt', - ], - isOsiApproved: false, - }, - { - reference: './BSL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/BSL-1.0.json', - referenceNumber: '284', - name: 'Boost Software License 1.0', - licenseId: 'BSL-1.0', - seeAlso: [ - 'http://www.boost.org/LICENSE_1_0.txt', - 'https://opensource.org/licenses/BSL-1.0', - ], - isOsiApproved: true, - }, - { - reference: './Bahyph.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Bahyph.json', - referenceNumber: '146', - name: 'Bahyph License', - licenseId: 'Bahyph', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Bahyph'], - isOsiApproved: false, - }, - { - reference: './Barr.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Barr.json', - referenceNumber: '123', - name: 'Barr License', - licenseId: 'Barr', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Barr'], - isOsiApproved: false, - }, - { - reference: './Beerware.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Beerware.json', - referenceNumber: '243', - name: 'Beerware License', - licenseId: 'Beerware', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/Beerware', - 'https://people.freebsd.org/~phk/', - ], - isOsiApproved: false, - }, - { - reference: './BitTorrent-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BitTorrent-1.0.json', - referenceNumber: '200', - name: 'BitTorrent Open Source License v1.0', - licenseId: 'BitTorrent-1.0', - seeAlso: [ - 'http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1=1.1&r2=1.1.1.1&diff_format=s', - ], - isOsiApproved: false, - }, - { - reference: './BitTorrent-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/BitTorrent-1.1.json', - referenceNumber: '181', - name: 'BitTorrent Open Source License v1.1', - licenseId: 'BitTorrent-1.1', - seeAlso: ['http://directory.fsf.org/wiki/License:BitTorrentOSL1.1'], - isOsiApproved: false, - }, - { - reference: './BlueOak-1.0.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/BlueOak-1.0.0.json', - referenceNumber: '204', - name: 'Blue Oak Model License 1.0.0', - licenseId: 'BlueOak-1.0.0', - seeAlso: ['https://blueoakcouncil.org/license/1.0.0'], - isOsiApproved: false, - }, - { - reference: './Borceux.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Borceux.json', - referenceNumber: '294', - name: 'Borceux license', - licenseId: 'Borceux', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Borceux'], - isOsiApproved: false, - }, - { - reference: './CATOSL-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CATOSL-1.1.json', - referenceNumber: '226', - name: 'Computer Associates Trusted Open Source License 1.1', - licenseId: 'CATOSL-1.1', - seeAlso: ['https://opensource.org/licenses/CATOSL-1.1'], - isOsiApproved: true, - }, - { - reference: './CC-BY-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-1.0.json', - referenceNumber: '23', - name: 'Creative Commons Attribution 1.0 Generic', - licenseId: 'CC-BY-1.0', - seeAlso: ['https://creativecommons.org/licenses/by/1.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-2.0.json', - referenceNumber: '58', - name: 'Creative Commons Attribution 2.0 Generic', - licenseId: 'CC-BY-2.0', - seeAlso: ['https://creativecommons.org/licenses/by/2.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-2.5.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-2.5.json', - referenceNumber: '180', - name: 'Creative Commons Attribution 2.5 Generic', - licenseId: 'CC-BY-2.5', - seeAlso: ['https://creativecommons.org/licenses/by/2.5/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-3.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-3.0.json', - referenceNumber: '333', - name: 'Creative Commons Attribution 3.0 Unported', - licenseId: 'CC-BY-3.0', - seeAlso: ['https://creativecommons.org/licenses/by/3.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-4.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/CC-BY-4.0.json', - referenceNumber: '211', - name: 'Creative Commons Attribution 4.0 International', - licenseId: 'CC-BY-4.0', - seeAlso: ['https://creativecommons.org/licenses/by/4.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-1.0.json', - referenceNumber: '223', - name: 'Creative Commons Attribution Non Commercial 1.0 Generic', - licenseId: 'CC-BY-NC-1.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc/1.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-2.0.json', - referenceNumber: '315', - name: 'Creative Commons Attribution Non Commercial 2.0 Generic', - licenseId: 'CC-BY-NC-2.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc/2.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-2.5.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-2.5.json', - referenceNumber: '386', - name: 'Creative Commons Attribution Non Commercial 2.5 Generic', - licenseId: 'CC-BY-NC-2.5', - seeAlso: ['https://creativecommons.org/licenses/by-nc/2.5/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-3.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-3.0.json', - referenceNumber: '325', - name: 'Creative Commons Attribution Non Commercial 3.0 Unported', - licenseId: 'CC-BY-NC-3.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc/3.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-4.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-4.0.json', - referenceNumber: '262', - name: 'Creative Commons Attribution Non Commercial 4.0 International', - licenseId: 'CC-BY-NC-4.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc/4.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-ND-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-ND-1.0.json', - referenceNumber: '98', - name: 'Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic', - licenseId: 'CC-BY-NC-ND-1.0', - seeAlso: ['https://creativecommons.org/licenses/by-nd-nc/1.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-ND-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-ND-2.0.json', - referenceNumber: '133', - name: 'Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic', - licenseId: 'CC-BY-NC-ND-2.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc-nd/2.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-ND-2.5.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-ND-2.5.json', - referenceNumber: '30', - name: 'Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic', - licenseId: 'CC-BY-NC-ND-2.5', - seeAlso: ['https://creativecommons.org/licenses/by-nc-nd/2.5/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-ND-3.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-ND-3.0.json', - referenceNumber: '41', - name: 'Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported', - licenseId: 'CC-BY-NC-ND-3.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc-nd/3.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-ND-4.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-ND-4.0.json', - referenceNumber: '166', - name: 'Creative Commons Attribution Non Commercial No Derivatives 4.0 International', - licenseId: 'CC-BY-NC-ND-4.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-SA-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-SA-1.0.json', - referenceNumber: '116', - name: 'Creative Commons Attribution Non Commercial Share Alike 1.0 Generic', - licenseId: 'CC-BY-NC-SA-1.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc-sa/1.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-SA-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-SA-2.0.json', - referenceNumber: '398', - name: 'Creative Commons Attribution Non Commercial Share Alike 2.0 Generic', - licenseId: 'CC-BY-NC-SA-2.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc-sa/2.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-SA-2.5.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-SA-2.5.json', - referenceNumber: '208', - name: 'Creative Commons Attribution Non Commercial Share Alike 2.5 Generic', - licenseId: 'CC-BY-NC-SA-2.5', - seeAlso: ['https://creativecommons.org/licenses/by-nc-sa/2.5/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-SA-3.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-SA-3.0.json', - referenceNumber: '349', - name: 'Creative Commons Attribution Non Commercial Share Alike 3.0 Unported', - licenseId: 'CC-BY-NC-SA-3.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-NC-SA-4.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-SA-4.0.json', - referenceNumber: '320', - name: 'Creative Commons Attribution Non Commercial Share Alike 4.0 International', - licenseId: 'CC-BY-NC-SA-4.0', - seeAlso: ['https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-ND-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-ND-1.0.json', - referenceNumber: '90', - name: 'Creative Commons Attribution No Derivatives 1.0 Generic', - licenseId: 'CC-BY-ND-1.0', - seeAlso: ['https://creativecommons.org/licenses/by-nd/1.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-ND-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-ND-2.0.json', - referenceNumber: '46', - name: 'Creative Commons Attribution No Derivatives 2.0 Generic', - licenseId: 'CC-BY-ND-2.0', - seeAlso: ['https://creativecommons.org/licenses/by-nd/2.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-ND-2.5.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-ND-2.5.json', - referenceNumber: '27', - name: 'Creative Commons Attribution No Derivatives 2.5 Generic', - licenseId: 'CC-BY-ND-2.5', - seeAlso: ['https://creativecommons.org/licenses/by-nd/2.5/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-ND-3.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-ND-3.0.json', - referenceNumber: '274', - name: 'Creative Commons Attribution No Derivatives 3.0 Unported', - licenseId: 'CC-BY-ND-3.0', - seeAlso: ['https://creativecommons.org/licenses/by-nd/3.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-ND-4.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-ND-4.0.json', - referenceNumber: '307', - name: 'Creative Commons Attribution No Derivatives 4.0 International', - licenseId: 'CC-BY-ND-4.0', - seeAlso: ['https://creativecommons.org/licenses/by-nd/4.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-SA-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-SA-1.0.json', - referenceNumber: '391', - name: 'Creative Commons Attribution Share Alike 1.0 Generic', - licenseId: 'CC-BY-SA-1.0', - seeAlso: ['https://creativecommons.org/licenses/by-sa/1.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-SA-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-SA-2.0.json', - referenceNumber: '356', - name: 'Creative Commons Attribution Share Alike 2.0 Generic', - licenseId: 'CC-BY-SA-2.0', - seeAlso: ['https://creativecommons.org/licenses/by-sa/2.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-SA-2.5.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-SA-2.5.json', - referenceNumber: '196', - name: 'Creative Commons Attribution Share Alike 2.5 Generic', - licenseId: 'CC-BY-SA-2.5', - seeAlso: ['https://creativecommons.org/licenses/by-sa/2.5/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-SA-3.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-BY-SA-3.0.json', - referenceNumber: '225', - name: 'Creative Commons Attribution Share Alike 3.0 Unported', - licenseId: 'CC-BY-SA-3.0', - seeAlso: ['https://creativecommons.org/licenses/by-sa/3.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-BY-SA-4.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/CC-BY-SA-4.0.json', - referenceNumber: '286', - name: 'Creative Commons Attribution Share Alike 4.0 International', - licenseId: 'CC-BY-SA-4.0', - seeAlso: ['https://creativecommons.org/licenses/by-sa/4.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CC-PDDC.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CC-PDDC.json', - referenceNumber: '88', - name: 'Creative Commons Public Domain Dedication and Certification', - licenseId: 'CC-PDDC', - seeAlso: ['https://creativecommons.org/licenses/publicdomain/'], - isOsiApproved: false, - }, - { - reference: './CC0-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/CC0-1.0.json', - referenceNumber: '63', - name: 'Creative Commons Zero v1.0 Universal', - licenseId: 'CC0-1.0', - seeAlso: ['https://creativecommons.org/publicdomain/zero/1.0/legalcode'], - isOsiApproved: false, - }, - { - reference: './CDDL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/CDDL-1.0.json', - referenceNumber: '324', - name: 'Common Development and Distribution License 1.0', - licenseId: 'CDDL-1.0', - seeAlso: ['https://opensource.org/licenses/cddl1'], - isOsiApproved: true, - }, - { - reference: './CDDL-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CDDL-1.1.json', - referenceNumber: '273', - name: 'Common Development and Distribution License 1.1', - licenseId: 'CDDL-1.1', - seeAlso: [ - 'http://glassfish.java.net/public/CDDL+GPL_1_1.html', - 'https://javaee.github.io/glassfish/LICENSE', - ], - isOsiApproved: false, - }, - { - reference: './CDLA-Permissive-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CDLA-Permissive-1.0.json', - referenceNumber: '104', - name: 'Community Data License Agreement Permissive 1.0', - licenseId: 'CDLA-Permissive-1.0', - seeAlso: ['https://cdla.io/permissive-1-0'], - isOsiApproved: false, - }, - { - reference: './CDLA-Sharing-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CDLA-Sharing-1.0.json', - referenceNumber: '172', - name: 'Community Data License Agreement Sharing 1.0', - licenseId: 'CDLA-Sharing-1.0', - seeAlso: ['https://cdla.io/sharing-1-0'], - isOsiApproved: false, - }, - { - reference: './CECILL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CECILL-1.0.json', - referenceNumber: '10', - name: 'CeCILL Free Software License Agreement v1.0', - licenseId: 'CECILL-1.0', - seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL_V1-fr.html'], - isOsiApproved: false, - }, - { - reference: './CECILL-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CECILL-1.1.json', - referenceNumber: '130', - name: 'CeCILL Free Software License Agreement v1.1', - licenseId: 'CECILL-1.1', - seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL_V1.1-US.html'], - isOsiApproved: false, - }, - { - reference: './CECILL-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/CECILL-2.0.json', - referenceNumber: '5', - name: 'CeCILL Free Software License Agreement v2.0', - licenseId: 'CECILL-2.0', - seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL_V2-en.html'], - isOsiApproved: false, - }, - { - reference: './CECILL-2.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CECILL-2.1.json', - referenceNumber: '140', - name: 'CeCILL Free Software License Agreement v2.1', - licenseId: 'CECILL-2.1', - seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html'], - isOsiApproved: true, - }, - { - reference: './CECILL-B.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/CECILL-B.json', - referenceNumber: '89', - name: 'CeCILL-B Free Software License Agreement', - licenseId: 'CECILL-B', - seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html'], - isOsiApproved: false, - }, - { - reference: './CECILL-C.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/CECILL-C.json', - referenceNumber: '232', - name: 'CeCILL-C Free Software License Agreement', - licenseId: 'CECILL-C', - seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html'], - isOsiApproved: false, - }, - { - reference: './CERN-OHL-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CERN-OHL-1.1.json', - referenceNumber: '118', - name: 'CERN Open Hardware Licence v1.1', - licenseId: 'CERN-OHL-1.1', - seeAlso: ['https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1'], - isOsiApproved: false, - }, - { - reference: './CERN-OHL-1.2.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CERN-OHL-1.2.json', - referenceNumber: '161', - name: 'CERN Open Hardware Licence v1.2', - licenseId: 'CERN-OHL-1.2', - seeAlso: ['https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2'], - isOsiApproved: false, - }, - { - reference: './CNRI-Jython.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CNRI-Jython.json', - referenceNumber: '74', - name: 'CNRI Jython License', - licenseId: 'CNRI-Jython', - seeAlso: ['http://www.jython.org/license.html'], - isOsiApproved: false, - }, - { - reference: './CNRI-Python.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CNRI-Python.json', - referenceNumber: '92', - name: 'CNRI Python License', - licenseId: 'CNRI-Python', - seeAlso: ['https://opensource.org/licenses/CNRI-Python'], - isOsiApproved: true, - }, - { - reference: './CNRI-Python-GPL-Compatible.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CNRI-Python-GPL-Compatible.json', - referenceNumber: '337', - name: 'CNRI Python Open Source GPL Compatible License Agreement', - licenseId: 'CNRI-Python-GPL-Compatible', - seeAlso: ['http://www.python.org/download/releases/1.6.1/download_win/'], - isOsiApproved: false, - }, - { - reference: './CPAL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/CPAL-1.0.json', - referenceNumber: '275', - name: 'Common Public Attribution License 1.0', - licenseId: 'CPAL-1.0', - seeAlso: ['https://opensource.org/licenses/CPAL-1.0'], - isOsiApproved: true, - }, - { - reference: './CPL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/CPL-1.0.json', - referenceNumber: '228', - name: 'Common Public License 1.0', - licenseId: 'CPL-1.0', - seeAlso: ['https://opensource.org/licenses/CPL-1.0'], - isOsiApproved: true, - }, - { - reference: './CPOL-1.02.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CPOL-1.02.json', - referenceNumber: '222', - name: 'Code Project Open License 1.02', - licenseId: 'CPOL-1.02', - seeAlso: ['http://www.codeproject.com/info/cpol10.aspx'], - isOsiApproved: false, - }, - { - reference: './CUA-OPL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CUA-OPL-1.0.json', - referenceNumber: '159', - name: 'CUA Office Public License v1.0', - licenseId: 'CUA-OPL-1.0', - seeAlso: ['https://opensource.org/licenses/CUA-OPL-1.0'], - isOsiApproved: true, - }, - { - reference: './Caldera.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Caldera.json', - referenceNumber: '242', - name: 'Caldera License', - licenseId: 'Caldera', - seeAlso: ['http://www.lemis.com/grog/UNIX/ancient-source-all.pdf'], - isOsiApproved: false, - }, - { - reference: './ClArtistic.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/ClArtistic.json', - referenceNumber: '237', - name: 'Clarified Artistic License', - licenseId: 'ClArtistic', - seeAlso: [ - 'http://gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/', - 'http://www.ncftp.com/ncftp/doc/LICENSE.txt', - ], - isOsiApproved: false, - }, - { - reference: './Condor-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Condor-1.1.json', - referenceNumber: '145', - name: 'Condor Public License v1.1', - licenseId: 'Condor-1.1', - seeAlso: [ - 'http://research.cs.wisc.edu/condor/license.html#condor', - 'http://web.archive.org/web/20111123062036/http://research.cs.wisc.edu/condor/license.html#condor', - ], - isOsiApproved: false, - }, - { - reference: './Crossword.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Crossword.json', - referenceNumber: '97', - name: 'Crossword License', - licenseId: 'Crossword', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Crossword'], - isOsiApproved: false, - }, - { - reference: './CrystalStacker.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/CrystalStacker.json', - referenceNumber: '38', - name: 'CrystalStacker License', - licenseId: 'CrystalStacker', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing:CrystalStacker?rd=Licensing/CrystalStacker', - ], - isOsiApproved: false, - }, - { - reference: './Cube.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Cube.json', - referenceNumber: '379', - name: 'Cube License', - licenseId: 'Cube', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Cube'], - isOsiApproved: false, - }, - { - reference: './D-FSL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/D-FSL-1.0.json', - referenceNumber: '347', - name: 'Deutsche Freie Software Lizenz', - licenseId: 'D-FSL-1.0', - seeAlso: [ - 'http://www.dipp.nrw.de/d-fsl/lizenzen/', - 'http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/de/D-FSL-1_0_de.txt', - 'http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/en/D-FSL-1_0_en.txt', - 'https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl', - 'https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/deutsche-freie-software-lizenz', - 'https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/german-free-software-license', - 'https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_de.txt/at_download/file', - 'https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_en.txt/at_download/file', - ], - isOsiApproved: false, - }, - { - reference: './DOC.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/DOC.json', - referenceNumber: '265', - name: 'DOC License', - licenseId: 'DOC', - seeAlso: ['http://www.cs.wustl.edu/~schmidt/ACE-copying.html'], - isOsiApproved: false, - }, - { - reference: './DSDP.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/DSDP.json', - referenceNumber: '257', - name: 'DSDP License', - licenseId: 'DSDP', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/DSDP'], - isOsiApproved: false, - }, - { - reference: './Dotseqn.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Dotseqn.json', - referenceNumber: '33', - name: 'Dotseqn License', - licenseId: 'Dotseqn', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Dotseqn'], - isOsiApproved: false, - }, - { - reference: './ECL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/ECL-1.0.json', - referenceNumber: '405', - name: 'Educational Community License v1.0', - licenseId: 'ECL-1.0', - seeAlso: ['https://opensource.org/licenses/ECL-1.0'], - isOsiApproved: true, - }, - { - reference: './ECL-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/ECL-2.0.json', - referenceNumber: '1', - name: 'Educational Community License v2.0', - licenseId: 'ECL-2.0', - seeAlso: ['https://opensource.org/licenses/ECL-2.0'], - isOsiApproved: true, - }, - { - reference: './EFL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/EFL-1.0.json', - referenceNumber: '73', - name: 'Eiffel Forum License v1.0', - licenseId: 'EFL-1.0', - seeAlso: [ - 'http://www.eiffel-nice.org/license/forum.txt', - 'https://opensource.org/licenses/EFL-1.0', - ], - isOsiApproved: true, - }, - { - reference: './EFL-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/EFL-2.0.json', - referenceNumber: '7', - name: 'Eiffel Forum License v2.0', - licenseId: 'EFL-2.0', - seeAlso: [ - 'http://www.eiffel-nice.org/license/eiffel-forum-license-2.html', - 'https://opensource.org/licenses/EFL-2.0', - ], - isOsiApproved: true, - }, - { - reference: './EPL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/EPL-1.0.json', - referenceNumber: '267', - name: 'Eclipse Public License 1.0', - licenseId: 'EPL-1.0', - seeAlso: [ - 'http://www.eclipse.org/legal/epl-v10.html', - 'https://opensource.org/licenses/EPL-1.0', - ], - isOsiApproved: true, - }, - { - reference: './EPL-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/EPL-2.0.json', - referenceNumber: '403', - name: 'Eclipse Public License 2.0', - licenseId: 'EPL-2.0', - seeAlso: [ - 'https://www.eclipse.org/legal/epl-2.0', - 'https://www.opensource.org/licenses/EPL-2.0', - ], - isOsiApproved: true, - }, - { - reference: './EUDatagrid.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/EUDatagrid.json', - referenceNumber: '254', - name: 'EU DataGrid Software License', - licenseId: 'EUDatagrid', - seeAlso: [ - 'http://eu-datagrid.web.cern.ch/eu-datagrid/license.html', - 'https://opensource.org/licenses/EUDatagrid', - ], - isOsiApproved: true, - }, - { - reference: './EUPL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/EUPL-1.0.json', - referenceNumber: '94', - name: 'European Union Public License 1.0', - licenseId: 'EUPL-1.0', - seeAlso: [ - 'http://ec.europa.eu/idabc/en/document/7330.html', - 'http://ec.europa.eu/idabc/servlets/Doc027f.pdf?id=31096', - ], - isOsiApproved: false, - }, - { - reference: './EUPL-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/EUPL-1.1.json', - referenceNumber: '396', - name: 'European Union Public License 1.1', - licenseId: 'EUPL-1.1', - seeAlso: [ - 'https://joinup.ec.europa.eu/software/page/eupl/licence-eupl', - 'https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl1.1.-licence-en_0.pdf', - 'https://opensource.org/licenses/EUPL-1.1', - ], - isOsiApproved: true, - }, - { - reference: './EUPL-1.2.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/EUPL-1.2.json', - referenceNumber: '261', - name: 'European Union Public License 1.2', - licenseId: 'EUPL-1.2', - seeAlso: [ - 'https://joinup.ec.europa.eu/page/eupl-text-11-12', - 'https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf', - 'https://joinup.ec.europa.eu/sites/default/files/inline-files/EUPL%20v1_2%20EN(1).txt', - 'http://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=CELEX:32017D0863', - 'https://opensource.org/licenses/EUPL-1.1', - ], - isOsiApproved: true, - }, - { - reference: './Entessa.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Entessa.json', - referenceNumber: '374', - name: 'Entessa Public License v1.0', - licenseId: 'Entessa', - seeAlso: ['https://opensource.org/licenses/Entessa'], - isOsiApproved: true, - }, - { - reference: './ErlPL-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/ErlPL-1.1.json', - referenceNumber: '395', - name: 'Erlang Public License v1.1', - licenseId: 'ErlPL-1.1', - seeAlso: ['http://www.erlang.org/EPLICENSE'], - isOsiApproved: false, - }, - { - reference: './Eurosym.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Eurosym.json', - referenceNumber: '165', - name: 'Eurosym License', - licenseId: 'Eurosym', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Eurosym'], - isOsiApproved: false, - }, - { - reference: './FSFAP.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/FSFAP.json', - referenceNumber: '382', - name: 'FSF All Permissive License', - licenseId: 'FSFAP', - seeAlso: [ - 'https://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html', - ], - isOsiApproved: false, - }, - { - reference: './FSFUL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/FSFUL.json', - referenceNumber: '2', - name: 'FSF Unlimited License', - licenseId: 'FSFUL', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License', - ], - isOsiApproved: false, - }, - { - reference: './FSFULLR.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/FSFULLR.json', - referenceNumber: '297', - name: 'FSF Unlimited License (with License Retention)', - licenseId: 'FSFULLR', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License#License_Retention_Variant', - ], - isOsiApproved: false, - }, - { - reference: './FTL.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/FTL.json', - referenceNumber: '363', - name: 'Freetype Project License', - licenseId: 'FTL', - seeAlso: [ - 'http://freetype.fis.uniroma2.it/FTL.TXT', - 'http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT', - ], - isOsiApproved: false, - }, - { - reference: './Fair.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Fair.json', - referenceNumber: '253', - name: 'Fair License', - licenseId: 'Fair', - seeAlso: [ - 'http://fairlicense.org/', - 'https://opensource.org/licenses/Fair', - ], - isOsiApproved: true, - }, - { - reference: './Frameworx-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Frameworx-1.0.json', - referenceNumber: '362', - name: 'Frameworx Open License 1.0', - licenseId: 'Frameworx-1.0', - seeAlso: ['https://opensource.org/licenses/Frameworx-1.0'], - isOsiApproved: true, - }, - { - reference: './FreeImage.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/FreeImage.json', - referenceNumber: '359', - name: 'FreeImage Public License v1.0', - licenseId: 'FreeImage', - seeAlso: ['http://freeimage.sourceforge.net/freeimage-license.txt'], - isOsiApproved: false, - }, - { - reference: './GFDL-1.1.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GFDL-1.1.json', - referenceNumber: '248', - name: 'GNU Free Documentation License v1.1', - licenseId: 'GFDL-1.1', - seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt'], - isOsiApproved: false, - }, - { - reference: './GFDL-1.1-only.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GFDL-1.1-only.json', - referenceNumber: '100', - name: 'GNU Free Documentation License v1.1 only', - licenseId: 'GFDL-1.1-only', - seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt'], - isOsiApproved: false, - }, - { - reference: './GFDL-1.1-or-later.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GFDL-1.1-or-later.json', - referenceNumber: '119', - name: 'GNU Free Documentation License v1.1 or later', - licenseId: 'GFDL-1.1-or-later', - seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt'], - isOsiApproved: false, - }, - { - reference: './GFDL-1.2.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GFDL-1.2.json', - referenceNumber: '190', - name: 'GNU Free Documentation License v1.2', - licenseId: 'GFDL-1.2', - seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt'], - isOsiApproved: false, - }, - { - reference: './GFDL-1.2-only.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GFDL-1.2-only.json', - referenceNumber: '86', - name: 'GNU Free Documentation License v1.2 only', - licenseId: 'GFDL-1.2-only', - seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt'], - isOsiApproved: false, - }, - { - reference: './GFDL-1.2-or-later.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GFDL-1.2-or-later.json', - referenceNumber: '127', - name: 'GNU Free Documentation License v1.2 or later', - licenseId: 'GFDL-1.2-or-later', - seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt'], - isOsiApproved: false, - }, - { - reference: './GFDL-1.3.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GFDL-1.3.json', - referenceNumber: '354', - name: 'GNU Free Documentation License v1.3', - licenseId: 'GFDL-1.3', - seeAlso: ['https://www.gnu.org/licenses/fdl-1.3.txt'], - isOsiApproved: false, - }, - { - reference: './GFDL-1.3-only.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GFDL-1.3-only.json', - referenceNumber: '193', - name: 'GNU Free Documentation License v1.3 only', - licenseId: 'GFDL-1.3-only', - seeAlso: ['https://www.gnu.org/licenses/fdl-1.3.txt'], - isOsiApproved: false, - }, - { - reference: './GFDL-1.3-or-later.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GFDL-1.3-or-later.json', - referenceNumber: '51', - name: 'GNU Free Documentation License v1.3 or later', - licenseId: 'GFDL-1.3-or-later', - seeAlso: ['https://www.gnu.org/licenses/fdl-1.3.txt'], - isOsiApproved: false, - }, - { - reference: './GL2PS.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/GL2PS.json', - referenceNumber: '303', - name: 'GL2PS License', - licenseId: 'GL2PS', - seeAlso: ['http://www.geuz.org/gl2ps/COPYING.GL2PS'], - isOsiApproved: false, - }, - { - reference: './GPL-1.0.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/GPL-1.0.json', - referenceNumber: '319', - name: 'GNU General Public License v1.0 only', - licenseId: 'GPL-1.0', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html', - ], - isOsiApproved: false, - }, - { - reference: './GPL-1.0+.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/GPL-1.0+.json', - referenceNumber: '198', - name: 'GNU General Public License v1.0 or later', - licenseId: 'GPL-1.0+', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html', - ], - isOsiApproved: false, - }, - { - reference: './GPL-1.0-only.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/GPL-1.0-only.json', - referenceNumber: '15', - name: 'GNU General Public License v1.0 only', - licenseId: 'GPL-1.0-only', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html', - ], - isOsiApproved: false, - }, - { - reference: './GPL-1.0-or-later.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/GPL-1.0-or-later.json', - referenceNumber: '129', - name: 'GNU General Public License v1.0 or later', - licenseId: 'GPL-1.0-or-later', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html', - ], - isOsiApproved: false, - }, - { - reference: './GPL-2.0.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GPL-2.0.json', - referenceNumber: '345', - name: 'GNU General Public License v2.0 only', - licenseId: 'GPL-2.0', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html', - 'https://opensource.org/licenses/GPL-2.0', - ], - isOsiApproved: true, - }, - { - reference: './GPL-2.0+.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GPL-2.0+.json', - referenceNumber: '389', - name: 'GNU General Public License v2.0 or later', - licenseId: 'GPL-2.0+', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html', - 'https://opensource.org/licenses/GPL-2.0', - ], - isOsiApproved: true, - }, - { - reference: './GPL-2.0-only.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GPL-2.0-only.json', - referenceNumber: '227', - name: 'GNU General Public License v2.0 only', - licenseId: 'GPL-2.0-only', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html', - 'https://opensource.org/licenses/GPL-2.0', - ], - isOsiApproved: true, - }, - { - reference: './GPL-2.0-or-later.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GPL-2.0-or-later.json', - referenceNumber: '249', - name: 'GNU General Public License v2.0 or later', - licenseId: 'GPL-2.0-or-later', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html', - 'https://opensource.org/licenses/GPL-2.0', - ], - isOsiApproved: true, - }, - { - reference: './GPL-2.0-with-GCC-exception.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/GPL-2.0-with-GCC-exception.json', - referenceNumber: '329', - name: 'GNU General Public License v2.0 w/GCC Runtime Library exception', - licenseId: 'GPL-2.0-with-GCC-exception', - seeAlso: [ - 'https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/libgcc1.c;h=762f5143fc6eed57b6797c82710f3538aa52b40b;hb=cb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10', - ], - isOsiApproved: false, - }, - { - reference: './GPL-2.0-with-autoconf-exception.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/GPL-2.0-with-autoconf-exception.json', - referenceNumber: '36', - name: 'GNU General Public License v2.0 w/Autoconf exception', - licenseId: 'GPL-2.0-with-autoconf-exception', - seeAlso: ['http://ac-archive.sourceforge.net/doc/copyright.html'], - isOsiApproved: false, - }, - { - reference: './GPL-2.0-with-bison-exception.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/GPL-2.0-with-bison-exception.json', - referenceNumber: '360', - name: 'GNU General Public License v2.0 w/Bison exception', - licenseId: 'GPL-2.0-with-bison-exception', - seeAlso: [ - 'http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id=193d7c7054ba7197b0789e14965b739162319b5e#n141', - ], - isOsiApproved: false, - }, - { - reference: './GPL-2.0-with-classpath-exception.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/GPL-2.0-with-classpath-exception.json', - referenceNumber: '219', - name: 'GNU General Public License v2.0 w/Classpath exception', - licenseId: 'GPL-2.0-with-classpath-exception', - seeAlso: ['https://www.gnu.org/software/classpath/license.html'], - isOsiApproved: false, - }, - { - reference: './GPL-2.0-with-font-exception.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/GPL-2.0-with-font-exception.json', - referenceNumber: '24', - name: 'GNU General Public License v2.0 w/Font exception', - licenseId: 'GPL-2.0-with-font-exception', - seeAlso: ['https://www.gnu.org/licenses/gpl-faq.html#FontException'], - isOsiApproved: false, - }, - { - reference: './GPL-3.0.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GPL-3.0.json', - referenceNumber: '401', - name: 'GNU General Public License v3.0 only', - licenseId: 'GPL-3.0', - seeAlso: [ - 'https://www.gnu.org/licenses/gpl-3.0-standalone.html', - 'https://opensource.org/licenses/GPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './GPL-3.0+.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GPL-3.0+.json', - referenceNumber: '147', - name: 'GNU General Public License v3.0 or later', - licenseId: 'GPL-3.0+', - seeAlso: [ - 'https://www.gnu.org/licenses/gpl-3.0-standalone.html', - 'https://opensource.org/licenses/GPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './GPL-3.0-only.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GPL-3.0-only.json', - referenceNumber: '122', - name: 'GNU General Public License v3.0 only', - licenseId: 'GPL-3.0-only', - seeAlso: [ - 'https://www.gnu.org/licenses/gpl-3.0-standalone.html', - 'https://opensource.org/licenses/GPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './GPL-3.0-or-later.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/GPL-3.0-or-later.json', - referenceNumber: '387', - name: 'GNU General Public License v3.0 or later', - licenseId: 'GPL-3.0-or-later', - seeAlso: [ - 'https://www.gnu.org/licenses/gpl-3.0-standalone.html', - 'https://opensource.org/licenses/GPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './GPL-3.0-with-GCC-exception.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/GPL-3.0-with-GCC-exception.json', - referenceNumber: '6', - name: 'GNU General Public License v3.0 w/GCC Runtime Library exception', - licenseId: 'GPL-3.0-with-GCC-exception', - seeAlso: ['https://www.gnu.org/licenses/gcc-exception-3.1.html'], - isOsiApproved: true, - }, - { - reference: './GPL-3.0-with-autoconf-exception.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/GPL-3.0-with-autoconf-exception.json', - referenceNumber: '8', - name: 'GNU General Public License v3.0 w/Autoconf exception', - licenseId: 'GPL-3.0-with-autoconf-exception', - seeAlso: ['https://www.gnu.org/licenses/autoconf-exception-3.0.html'], - isOsiApproved: false, - }, - { - reference: './Giftware.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Giftware.json', - referenceNumber: '367', - name: 'Giftware License', - licenseId: 'Giftware', - seeAlso: [ - 'http://liballeg.org/license.html#allegro-4-the-giftware-license', - ], - isOsiApproved: false, - }, - { - reference: './Glide.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Glide.json', - referenceNumber: '117', - name: '3dfx Glide License', - licenseId: 'Glide', - seeAlso: ['http://www.users.on.net/~triforce/glidexp/COPYING.txt'], - isOsiApproved: false, - }, - { - reference: './Glulxe.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Glulxe.json', - referenceNumber: '199', - name: 'Glulxe License', - licenseId: 'Glulxe', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Glulxe'], - isOsiApproved: false, - }, - { - reference: './HPND.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/HPND.json', - referenceNumber: '148', - name: 'Historical Permission Notice and Disclaimer', - licenseId: 'HPND', - seeAlso: ['https://opensource.org/licenses/HPND'], - isOsiApproved: true, - }, - { - reference: './HPND-sell-variant.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/HPND-sell-variant.json', - referenceNumber: '158', - name: 'Historical Permission Notice and Disclaimer - sell variant', - licenseId: 'HPND-sell-variant', - seeAlso: [ - 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h=v4.19', - ], - isOsiApproved: false, - }, - { - reference: './HaskellReport.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/HaskellReport.json', - referenceNumber: '205', - name: 'Haskell Language Report License', - licenseId: 'HaskellReport', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/Haskell_Language_Report_License', - ], - isOsiApproved: false, - }, - { - reference: './IBM-pibs.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/IBM-pibs.json', - referenceNumber: '233', - name: 'IBM PowerPC Initialization and Boot Software', - licenseId: 'IBM-pibs', - seeAlso: [ - 'http://git.denx.de/?p=u-boot.git;a=blob;f=arch/powerpc/cpu/ppc4xx/miiphy.c;h=297155fdafa064b955e53e9832de93bfb0cfb85b;hb=9fab4bf4cc077c21e43941866f3f2c196f28670d', - ], - isOsiApproved: false, - }, - { - reference: './ICU.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/ICU.json', - referenceNumber: '174', - name: 'ICU License', - licenseId: 'ICU', - seeAlso: [ - 'http://source.icu-project.org/repos/icu/icu/trunk/license.html', - ], - isOsiApproved: false, - }, - { - reference: './IJG.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/IJG.json', - referenceNumber: '236', - name: 'Independent JPEG Group License', - licenseId: 'IJG', - seeAlso: ['http://dev.w3.org/cvsweb/Amaya/libjpeg/Attic/README?rev=1.2'], - isOsiApproved: false, - }, - { - reference: './IPA.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/IPA.json', - referenceNumber: '310', - name: 'IPA Font License', - licenseId: 'IPA', - seeAlso: ['https://opensource.org/licenses/IPA'], - isOsiApproved: true, - }, - { - reference: './IPL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/IPL-1.0.json', - referenceNumber: '309', - name: 'IBM Public License v1.0', - licenseId: 'IPL-1.0', - seeAlso: ['https://opensource.org/licenses/IPL-1.0'], - isOsiApproved: true, - }, - { - reference: './ISC.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/ISC.json', - referenceNumber: '353', - name: 'ISC License', - licenseId: 'ISC', - seeAlso: [ - 'https://www.isc.org/downloads/software-support-policy/isc-license/', - 'https://opensource.org/licenses/ISC', - ], - isOsiApproved: true, - }, - { - reference: './ImageMagick.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/ImageMagick.json', - referenceNumber: '326', - name: 'ImageMagick License', - licenseId: 'ImageMagick', - seeAlso: ['http://www.imagemagick.org/script/license.php'], - isOsiApproved: false, - }, - { - reference: './Imlib2.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Imlib2.json', - referenceNumber: '135', - name: 'Imlib2 License', - licenseId: 'Imlib2', - seeAlso: [ - 'http://trac.enlightenment.org/e/browser/trunk/imlib2/COPYING', - 'https://git.enlightenment.org/legacy/imlib2.git/tree/COPYING', - ], - isOsiApproved: false, - }, - { - reference: './Info-ZIP.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Info-ZIP.json', - referenceNumber: '283', - name: 'Info-ZIP License', - licenseId: 'Info-ZIP', - seeAlso: ['http://www.info-zip.org/license.html'], - isOsiApproved: false, - }, - { - reference: './Intel.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Intel.json', - referenceNumber: '29', - name: 'Intel Open Source License', - licenseId: 'Intel', - seeAlso: ['https://opensource.org/licenses/Intel'], - isOsiApproved: true, - }, - { - reference: './Intel-ACPI.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Intel-ACPI.json', - referenceNumber: '235', - name: 'Intel ACPI Software License Agreement', - licenseId: 'Intel-ACPI', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/Intel_ACPI_Software_License_Agreement', - ], - isOsiApproved: false, - }, - { - reference: './Interbase-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Interbase-1.0.json', - referenceNumber: '323', - name: 'Interbase Public License v1.0', - licenseId: 'Interbase-1.0', - seeAlso: [ - 'https://web.archive.org/web/20060319014854/http://info.borland.com/devsupport/interbase/opensource/IPL.html', - ], - isOsiApproved: false, - }, - { - reference: './JPNIC.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/JPNIC.json', - referenceNumber: '316', - name: 'Japan Network Information Center License', - licenseId: 'JPNIC', - seeAlso: [ - 'https://gitlab.isc.org/isc-projects/bind9/blob/master/COPYRIGHT#L366', - ], - isOsiApproved: false, - }, - { - reference: './JSON.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/JSON.json', - referenceNumber: '197', - name: 'JSON License', - licenseId: 'JSON', - seeAlso: ['http://www.json.org/license.html'], - isOsiApproved: false, - }, - { - reference: './JasPer-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/JasPer-2.0.json', - referenceNumber: '77', - name: 'JasPer License', - licenseId: 'JasPer-2.0', - seeAlso: ['http://www.ece.uvic.ca/~mdadams/jasper/LICENSE'], - isOsiApproved: false, - }, - { - reference: './LAL-1.2.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LAL-1.2.json', - referenceNumber: '153', - name: 'Licence Art Libre 1.2', - licenseId: 'LAL-1.2', - seeAlso: ['http://artlibre.org/licence/lal/licence-art-libre-12/'], - isOsiApproved: false, - }, - { - reference: './LAL-1.3.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LAL-1.3.json', - referenceNumber: '357', - name: 'Licence Art Libre 1.3', - licenseId: 'LAL-1.3', - seeAlso: ['https://artlibre.org/'], - isOsiApproved: false, - }, - { - reference: './LGPL-2.0.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/LGPL-2.0.json', - referenceNumber: '276', - name: 'GNU Library General Public License v2 only', - licenseId: 'LGPL-2.0', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-2.0+.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/LGPL-2.0+.json', - referenceNumber: '139', - name: 'GNU Library General Public License v2 or later', - licenseId: 'LGPL-2.0+', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-2.0-only.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LGPL-2.0-only.json', - referenceNumber: '328', - name: 'GNU Library General Public License v2 only', - licenseId: 'LGPL-2.0-only', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-2.0-or-later.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LGPL-2.0-or-later.json', - referenceNumber: '35', - name: 'GNU Library General Public License v2 or later', - licenseId: 'LGPL-2.0-or-later', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-2.1.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LGPL-2.1.json', - referenceNumber: '182', - name: 'GNU Lesser General Public License v2.1 only', - licenseId: 'LGPL-2.1', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html', - 'https://opensource.org/licenses/LGPL-2.1', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-2.1+.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LGPL-2.1+.json', - referenceNumber: '202', - name: 'GNU Library General Public License v2.1 or later', - licenseId: 'LGPL-2.1+', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html', - 'https://opensource.org/licenses/LGPL-2.1', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-2.1-only.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LGPL-2.1-only.json', - referenceNumber: '138', - name: 'GNU Lesser General Public License v2.1 only', - licenseId: 'LGPL-2.1-only', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html', - 'https://opensource.org/licenses/LGPL-2.1', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-2.1-or-later.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LGPL-2.1-or-later.json', - referenceNumber: '269', - name: 'GNU Lesser General Public License v2.1 or later', - licenseId: 'LGPL-2.1-or-later', - seeAlso: [ - 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html', - 'https://opensource.org/licenses/LGPL-2.1', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-3.0.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LGPL-3.0.json', - referenceNumber: '209', - name: 'GNU Lesser General Public License v3.0 only', - licenseId: 'LGPL-3.0', - seeAlso: [ - 'https://www.gnu.org/licenses/lgpl-3.0-standalone.html', - 'https://opensource.org/licenses/LGPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-3.0+.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LGPL-3.0+.json', - referenceNumber: '220', - name: 'GNU Lesser General Public License v3.0 or later', - licenseId: 'LGPL-3.0+', - seeAlso: [ - 'https://www.gnu.org/licenses/lgpl-3.0-standalone.html', - 'https://opensource.org/licenses/LGPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-3.0-only.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LGPL-3.0-only.json', - referenceNumber: '47', - name: 'GNU Lesser General Public License v3.0 only', - licenseId: 'LGPL-3.0-only', - seeAlso: [ - 'https://www.gnu.org/licenses/lgpl-3.0-standalone.html', - 'https://opensource.org/licenses/LGPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './LGPL-3.0-or-later.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LGPL-3.0-or-later.json', - referenceNumber: '317', - name: 'GNU Lesser General Public License v3.0 or later', - licenseId: 'LGPL-3.0-or-later', - seeAlso: [ - 'https://www.gnu.org/licenses/lgpl-3.0-standalone.html', - 'https://opensource.org/licenses/LGPL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './LGPLLR.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LGPLLR.json', - referenceNumber: '404', - name: 'Lesser General Public License For Linguistic Resources', - licenseId: 'LGPLLR', - seeAlso: ['http://www-igm.univ-mlv.fr/~unitex/lgpllr.html'], - isOsiApproved: false, - }, - { - reference: './LPL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LPL-1.0.json', - referenceNumber: '371', - name: 'Lucent Public License Version 1.0', - licenseId: 'LPL-1.0', - seeAlso: ['https://opensource.org/licenses/LPL-1.0'], - isOsiApproved: true, - }, - { - reference: './LPL-1.02.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LPL-1.02.json', - referenceNumber: '120', - name: 'Lucent Public License v1.02', - licenseId: 'LPL-1.02', - seeAlso: [ - 'http://plan9.bell-labs.com/plan9/license.html', - 'https://opensource.org/licenses/LPL-1.02', - ], - isOsiApproved: true, - }, - { - reference: './LPPL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LPPL-1.0.json', - referenceNumber: '82', - name: 'LaTeX Project Public License v1.0', - licenseId: 'LPPL-1.0', - seeAlso: ['http://www.latex-project.org/lppl/lppl-1-0.txt'], - isOsiApproved: false, - }, - { - reference: './LPPL-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LPPL-1.1.json', - referenceNumber: '168', - name: 'LaTeX Project Public License v1.1', - licenseId: 'LPPL-1.1', - seeAlso: ['http://www.latex-project.org/lppl/lppl-1-1.txt'], - isOsiApproved: false, - }, - { - reference: './LPPL-1.2.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LPPL-1.2.json', - referenceNumber: '160', - name: 'LaTeX Project Public License v1.2', - licenseId: 'LPPL-1.2', - seeAlso: ['http://www.latex-project.org/lppl/lppl-1-2.txt'], - isOsiApproved: false, - }, - { - reference: './LPPL-1.3a.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/LPPL-1.3a.json', - referenceNumber: '270', - name: 'LaTeX Project Public License v1.3a', - licenseId: 'LPPL-1.3a', - seeAlso: ['http://www.latex-project.org/lppl/lppl-1-3a.txt'], - isOsiApproved: false, - }, - { - reference: './LPPL-1.3c.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LPPL-1.3c.json', - referenceNumber: '126', - name: 'LaTeX Project Public License v1.3c', - licenseId: 'LPPL-1.3c', - seeAlso: [ - 'http://www.latex-project.org/lppl/lppl-1-3c.txt', - 'https://opensource.org/licenses/LPPL-1.3c', - ], - isOsiApproved: true, - }, - { - reference: './Latex2e.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Latex2e.json', - referenceNumber: '37', - name: 'Latex2e License', - licenseId: 'Latex2e', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Latex2e'], - isOsiApproved: false, - }, - { - reference: './Leptonica.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Leptonica.json', - referenceNumber: '300', - name: 'Leptonica License', - licenseId: 'Leptonica', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Leptonica'], - isOsiApproved: false, - }, - { - reference: './LiLiQ-P-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LiLiQ-P-1.1.json', - referenceNumber: '79', - name: 'Licence Libre du Qu\xe9bec \u2013 Permissive version 1.1', - licenseId: 'LiLiQ-P-1.1', - seeAlso: [ - 'https://forge.gouv.qc.ca/licence/fr/liliq-v1-1/', - 'http://opensource.org/licenses/LiLiQ-P-1.1', - ], - isOsiApproved: true, - }, - { - reference: './LiLiQ-R-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LiLiQ-R-1.1.json', - referenceNumber: '290', - name: 'Licence Libre du Qu\xe9bec \u2013 R\xe9ciprocit\xe9 version 1.1', - licenseId: 'LiLiQ-R-1.1', - seeAlso: [ - 'https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-liliq-r-v1-1/', - 'http://opensource.org/licenses/LiLiQ-R-1.1', - ], - isOsiApproved: true, - }, - { - reference: './LiLiQ-Rplus-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/LiLiQ-Rplus-1.1.json', - referenceNumber: '335', - name: 'Licence Libre du Qu\xe9bec \u2013 R\xe9ciprocit\xe9 forte version 1.1', - licenseId: 'LiLiQ-Rplus-1.1', - seeAlso: [ - 'https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-forte-liliq-r-v1-1/', - 'http://opensource.org/licenses/LiLiQ-Rplus-1.1', - ], - isOsiApproved: true, - }, - { - reference: './Libpng.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Libpng.json', - referenceNumber: '376', - name: 'libpng License', - licenseId: 'Libpng', - seeAlso: ['http://www.libpng.org/pub/png/src/libpng-LICENSE.txt'], - isOsiApproved: false, - }, - { - reference: './Linux-OpenIB.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Linux-OpenIB.json', - referenceNumber: '217', - name: 'Linux Kernel Variant of OpenIB.org license', - licenseId: 'Linux-OpenIB', - seeAlso: [ - 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h', - ], - isOsiApproved: false, - }, - { - reference: './MIT.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/MIT.json', - referenceNumber: '256', - name: 'MIT License', - licenseId: 'MIT', - seeAlso: ['https://opensource.org/licenses/MIT'], - isOsiApproved: true, - }, - { - reference: './MIT-0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MIT-0.json', - referenceNumber: '76', - name: 'MIT No Attribution', - licenseId: 'MIT-0', - seeAlso: [ - 'https://github.com/aws/mit-0', - 'https://romanrm.net/mit-zero', - 'https://github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE', - ], - isOsiApproved: false, - }, - { - reference: './MIT-CMU.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MIT-CMU.json', - referenceNumber: '344', - name: 'CMU License', - licenseId: 'MIT-CMU', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing:MIT?rd=Licensing/MIT#CMU_Style', - 'https://github.com/python-pillow/Pillow/blob/fffb426092c8db24a5f4b6df243a8a3c01fb63cd/LICENSE', - ], - isOsiApproved: false, - }, - { - reference: './MIT-advertising.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MIT-advertising.json', - referenceNumber: '192', - name: 'Enlightenment License (e16)', - licenseId: 'MIT-advertising', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/MIT_With_Advertising'], - isOsiApproved: false, - }, - { - reference: './MIT-enna.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MIT-enna.json', - referenceNumber: '52', - name: 'enna License', - licenseId: 'MIT-enna', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/MIT#enna'], - isOsiApproved: false, - }, - { - reference: './MIT-feh.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MIT-feh.json', - referenceNumber: '365', - name: 'feh License', - licenseId: 'MIT-feh', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/MIT#feh'], - isOsiApproved: false, - }, - { - reference: './MITNFA.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MITNFA.json', - referenceNumber: '336', - name: 'MIT +no-false-attribs license', - licenseId: 'MITNFA', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/MITNFA'], - isOsiApproved: false, - }, - { - reference: './MPL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MPL-1.0.json', - referenceNumber: '239', - name: 'Mozilla Public License 1.0', - licenseId: 'MPL-1.0', - seeAlso: [ - 'http://www.mozilla.org/MPL/MPL-1.0.html', - 'https://opensource.org/licenses/MPL-1.0', - ], - isOsiApproved: true, - }, - { - reference: './MPL-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/MPL-1.1.json', - referenceNumber: '394', - name: 'Mozilla Public License 1.1', - licenseId: 'MPL-1.1', - seeAlso: [ - 'http://www.mozilla.org/MPL/MPL-1.1.html', - 'https://opensource.org/licenses/MPL-1.1', - ], - isOsiApproved: true, - }, - { - reference: './MPL-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/MPL-2.0.json', - referenceNumber: '121', - name: 'Mozilla Public License 2.0', - licenseId: 'MPL-2.0', - seeAlso: [ - 'http://www.mozilla.org/MPL/2.0/', - 'https://opensource.org/licenses/MPL-2.0', - ], - isOsiApproved: true, - }, - { - reference: './MPL-2.0-no-copyleft-exception.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MPL-2.0-no-copyleft-exception.json', - referenceNumber: '183', - name: 'Mozilla Public License 2.0 (no copyleft exception)', - licenseId: 'MPL-2.0-no-copyleft-exception', - seeAlso: [ - 'http://www.mozilla.org/MPL/2.0/', - 'https://opensource.org/licenses/MPL-2.0', - ], - isOsiApproved: true, - }, - { - reference: './MS-PL.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/MS-PL.json', - referenceNumber: '366', - name: 'Microsoft Public License', - licenseId: 'MS-PL', - seeAlso: [ - 'http://www.microsoft.com/opensource/licenses.mspx', - 'https://opensource.org/licenses/MS-PL', - ], - isOsiApproved: true, - }, - { - reference: './MS-RL.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/MS-RL.json', - referenceNumber: '4', - name: 'Microsoft Reciprocal License', - licenseId: 'MS-RL', - seeAlso: [ - 'http://www.microsoft.com/opensource/licenses.mspx', - 'https://opensource.org/licenses/MS-RL', - ], - isOsiApproved: true, - }, - { - reference: './MTLL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MTLL.json', - referenceNumber: '102', - name: 'Matrix Template Library License', - licenseId: 'MTLL', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/Matrix_Template_Library_License', - ], - isOsiApproved: false, - }, - { - reference: './MakeIndex.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MakeIndex.json', - referenceNumber: '343', - name: 'MakeIndex License', - licenseId: 'MakeIndex', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/MakeIndex'], - isOsiApproved: false, - }, - { - reference: './MirOS.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MirOS.json', - referenceNumber: '369', - name: 'The MirOS Licence', - licenseId: 'MirOS', - seeAlso: ['https://opensource.org/licenses/MirOS'], - isOsiApproved: true, - }, - { - reference: './Motosoto.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Motosoto.json', - referenceNumber: '12', - name: 'Motosoto License', - licenseId: 'Motosoto', - seeAlso: ['https://opensource.org/licenses/Motosoto'], - isOsiApproved: true, - }, - { - reference: './MulanPSL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/MulanPSL-1.0.json', - referenceNumber: '201', - name: 'Mulan Permissive Software License, Version 1', - licenseId: 'MulanPSL-1.0', - seeAlso: [ - 'https://license.coscl.org.cn/MulanPSL/', - 'https://github.com/yuwenlong/longphp/blob/25dfb70cc2a466dc4bb55ba30901cbce08d164b5/LICENSE', - ], - isOsiApproved: false, - }, - { - reference: './Multics.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Multics.json', - referenceNumber: '164', - name: 'Multics License', - licenseId: 'Multics', - seeAlso: ['https://opensource.org/licenses/Multics'], - isOsiApproved: true, - }, - { - reference: './Mup.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Mup.json', - referenceNumber: '305', - name: 'Mup License', - licenseId: 'Mup', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Mup'], - isOsiApproved: false, - }, - { - reference: './NASA-1.3.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/NASA-1.3.json', - referenceNumber: '110', - name: 'NASA Open Source Agreement 1.3', - licenseId: 'NASA-1.3', - seeAlso: [ - 'http://ti.arc.nasa.gov/opensource/nosa/', - 'https://opensource.org/licenses/NASA-1.3', - ], - isOsiApproved: true, - }, - { - reference: './NBPL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/NBPL-1.0.json', - referenceNumber: '17', - name: 'Net Boolean Public License v1', - licenseId: 'NBPL-1.0', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=37b4b3f6cc4bf34e1d3dec61e69914b9819d8894', - ], - isOsiApproved: false, - }, - { - reference: './NCSA.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/NCSA.json', - referenceNumber: '187', - name: 'University of Illinois/NCSA Open Source License', - licenseId: 'NCSA', - seeAlso: [ - 'http://otm.illinois.edu/uiuc_openSource', - 'https://opensource.org/licenses/NCSA', - ], - isOsiApproved: true, - }, - { - reference: './NGPL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/NGPL.json', - referenceNumber: '308', - name: 'Nethack General Public License', - licenseId: 'NGPL', - seeAlso: ['https://opensource.org/licenses/NGPL'], - isOsiApproved: true, - }, - { - reference: './NLOD-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/NLOD-1.0.json', - referenceNumber: '134', - name: 'Norwegian Licence for Open Government Data', - licenseId: 'NLOD-1.0', - seeAlso: ['http://data.norge.no/nlod/en/1.0'], - isOsiApproved: false, - }, - { - reference: './NLPL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/NLPL.json', - referenceNumber: '306', - name: 'No Limit Public License', - licenseId: 'NLPL', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/NLPL'], - isOsiApproved: false, - }, - { - reference: './NOSL.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/NOSL.json', - referenceNumber: '380', - name: 'Netizen Open Source License', - licenseId: 'NOSL', - seeAlso: ['http://bits.netizen.com.au/licenses/NOSL/nosl.txt'], - isOsiApproved: false, - }, - { - reference: './NPL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/NPL-1.0.json', - referenceNumber: '245', - name: 'Netscape Public License v1.0', - licenseId: 'NPL-1.0', - seeAlso: ['http://www.mozilla.org/MPL/NPL/1.0/'], - isOsiApproved: false, - }, - { - reference: './NPL-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/NPL-1.1.json', - referenceNumber: '409', - name: 'Netscape Public License v1.1', - licenseId: 'NPL-1.1', - seeAlso: ['http://www.mozilla.org/MPL/NPL/1.1/'], - isOsiApproved: false, - }, - { - reference: './NPOSL-3.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/NPOSL-3.0.json', - referenceNumber: '150', - name: 'Non-Profit Open Software License 3.0', - licenseId: 'NPOSL-3.0', - seeAlso: ['https://opensource.org/licenses/NOSL3.0'], - isOsiApproved: true, - }, - { - reference: './NRL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/NRL.json', - referenceNumber: '101', - name: 'NRL License', - licenseId: 'NRL', - seeAlso: ['http://web.mit.edu/network/isakmp/nrllicense.html'], - isOsiApproved: false, - }, - { - reference: './NTP.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/NTP.json', - referenceNumber: '258', - name: 'NTP License', - licenseId: 'NTP', - seeAlso: ['https://opensource.org/licenses/NTP'], - isOsiApproved: true, - }, - { - reference: './NTP-0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/NTP-0.json', - referenceNumber: '185', - name: 'NTP No Attribution', - licenseId: 'NTP-0', - seeAlso: [ - 'https://github.com/tytso/e2fsprogs/blob/master/lib/et/et_name.c', - ], - isOsiApproved: false, - }, - { - reference: './Naumen.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Naumen.json', - referenceNumber: '282', - name: 'Naumen Public License', - licenseId: 'Naumen', - seeAlso: ['https://opensource.org/licenses/Naumen'], - isOsiApproved: true, - }, - { - reference: './Net-SNMP.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Net-SNMP.json', - referenceNumber: '278', - name: 'Net-SNMP License', - licenseId: 'Net-SNMP', - seeAlso: ['http://net-snmp.sourceforge.net/about/license.html'], - isOsiApproved: false, - }, - { - reference: './NetCDF.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/NetCDF.json', - referenceNumber: '212', - name: 'NetCDF license', - licenseId: 'NetCDF', - seeAlso: ['http://www.unidata.ucar.edu/software/netcdf/copyright.html'], - isOsiApproved: false, - }, - { - reference: './Newsletr.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Newsletr.json', - referenceNumber: '358', - name: 'Newsletr License', - licenseId: 'Newsletr', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Newsletr'], - isOsiApproved: false, - }, - { - reference: './Nokia.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Nokia.json', - referenceNumber: '125', - name: 'Nokia Open Source License', - licenseId: 'Nokia', - seeAlso: ['https://opensource.org/licenses/nokia'], - isOsiApproved: true, - }, - { - reference: './Noweb.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Noweb.json', - referenceNumber: '70', - name: 'Noweb License', - licenseId: 'Noweb', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Noweb'], - isOsiApproved: false, - }, - { - reference: './Nunit.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Nunit.json', - referenceNumber: '87', - name: 'Nunit License', - licenseId: 'Nunit', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Nunit'], - isOsiApproved: false, - }, - { - reference: './OCCT-PL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OCCT-PL.json', - referenceNumber: '65', - name: 'Open CASCADE Technology Public License', - licenseId: 'OCCT-PL', - seeAlso: ['http://www.opencascade.com/content/occt-public-license'], - isOsiApproved: false, - }, - { - reference: './OCLC-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OCLC-2.0.json', - referenceNumber: '341', - name: 'OCLC Research Public License 2.0', - licenseId: 'OCLC-2.0', - seeAlso: [ - 'http://www.oclc.org/research/activities/software/license/v2final.htm', - 'https://opensource.org/licenses/OCLC-2.0', - ], - isOsiApproved: true, - }, - { - reference: './ODC-By-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/ODC-By-1.0.json', - referenceNumber: '381', - name: 'Open Data Commons Attribution License v1.0', - licenseId: 'ODC-By-1.0', - seeAlso: ['https://opendatacommons.org/licenses/by/1.0/'], - isOsiApproved: false, - }, - { - reference: './ODbL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/ODbL-1.0.json', - referenceNumber: '340', - name: 'ODC Open Database License v1.0', - licenseId: 'ODbL-1.0', - seeAlso: ['http://www.opendatacommons.org/licenses/odbl/1.0/'], - isOsiApproved: false, - }, - { - reference: './OFL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/OFL-1.0.json', - referenceNumber: '80', - name: 'SIL Open Font License 1.0', - licenseId: 'OFL-1.0', - seeAlso: ['http://scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web'], - isOsiApproved: false, - }, - { - reference: './OFL-1.0-RFN.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OFL-1.0-RFN.json', - referenceNumber: '298', - name: 'SIL Open Font License 1.0 with Reserved Font Name', - licenseId: 'OFL-1.0-RFN', - seeAlso: ['http://scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web'], - isOsiApproved: false, - }, - { - reference: './OFL-1.0-no-RFN.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OFL-1.0-no-RFN.json', - referenceNumber: '71', - name: 'SIL Open Font License 1.0 with no Reserved Font Name', - licenseId: 'OFL-1.0-no-RFN', - seeAlso: ['http://scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web'], - isOsiApproved: false, - }, - { - reference: './OFL-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/OFL-1.1.json', - referenceNumber: '311', - name: 'SIL Open Font License 1.1', - licenseId: 'OFL-1.1', - seeAlso: [ - 'http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web', - 'https://opensource.org/licenses/OFL-1.1', - ], - isOsiApproved: true, - }, - { - reference: './OFL-1.1-RFN.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OFL-1.1-RFN.json', - referenceNumber: '43', - name: 'SIL Open Font License 1.1 with Reserved Font Name', - licenseId: 'OFL-1.1-RFN', - seeAlso: [ - 'http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web', - 'https://opensource.org/licenses/OFL-1.1', - ], - isOsiApproved: true, - }, - { - reference: './OFL-1.1-no-RFN.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OFL-1.1-no-RFN.json', - referenceNumber: '238', - name: 'SIL Open Font License 1.1 with no Reserved Font Name', - licenseId: 'OFL-1.1-no-RFN', - seeAlso: [ - 'http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web', - 'https://opensource.org/licenses/OFL-1.1', - ], - isOsiApproved: true, - }, - { - reference: './OGL-Canada-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OGL-Canada-2.0.json', - referenceNumber: '346', - name: 'Open Government Licence - Canada', - licenseId: 'OGL-Canada-2.0', - seeAlso: ['https://open.canada.ca/en/open-government-licence-canada'], - isOsiApproved: false, - }, - { - reference: './OGL-UK-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OGL-UK-1.0.json', - referenceNumber: '348', - name: 'Open Government Licence v1.0', - licenseId: 'OGL-UK-1.0', - seeAlso: [ - 'http://www.nationalarchives.gov.uk/doc/open-government-licence/version/1/', - ], - isOsiApproved: false, - }, - { - reference: './OGL-UK-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OGL-UK-2.0.json', - referenceNumber: '13', - name: 'Open Government Licence v2.0', - licenseId: 'OGL-UK-2.0', - seeAlso: [ - 'http://www.nationalarchives.gov.uk/doc/open-government-licence/version/2/', - ], - isOsiApproved: false, - }, - { - reference: './OGL-UK-3.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OGL-UK-3.0.json', - referenceNumber: '21', - name: 'Open Government Licence v3.0', - licenseId: 'OGL-UK-3.0', - seeAlso: [ - 'http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/', - ], - isOsiApproved: false, - }, - { - reference: './OGTSL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OGTSL.json', - referenceNumber: '26', - name: 'Open Group Test Suite License', - licenseId: 'OGTSL', - seeAlso: [ - 'http://www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt', - 'https://opensource.org/licenses/OGTSL', - ], - isOsiApproved: true, - }, - { - reference: './OLDAP-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-1.1.json', - referenceNumber: '55', - name: 'Open LDAP Public License v1.1', - licenseId: 'OLDAP-1.1', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=806557a5ad59804ef3a44d5abfbe91d706b0791f', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-1.2.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-1.2.json', - referenceNumber: '48', - name: 'Open LDAP Public License v1.2', - licenseId: 'OLDAP-1.2', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=42b0383c50c299977b5893ee695cf4e486fb0dc7', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-1.3.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-1.3.json', - referenceNumber: '42', - name: 'Open LDAP Public License v1.3', - licenseId: 'OLDAP-1.3', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=e5f8117f0ce088d0bd7a8e18ddf37eaa40eb09b1', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-1.4.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-1.4.json', - referenceNumber: '50', - name: 'Open LDAP Public License v1.4', - licenseId: 'OLDAP-1.4', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=c9f95c2f3f2ffb5e0ae55fe7388af75547660941', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.0.json', - referenceNumber: '25', - name: 'Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)', - licenseId: 'OLDAP-2.0', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=cbf50f4e1185a21abd4c0a54d3f4341fe28f36ea', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.0.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.0.1.json', - referenceNumber: '280', - name: 'Open LDAP Public License v2.0.1', - licenseId: 'OLDAP-2.0.1', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=b6d68acd14e51ca3aab4428bf26522aa74873f0e', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.1.json', - referenceNumber: '400', - name: 'Open LDAP Public License v2.1', - licenseId: 'OLDAP-2.1', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=b0d176738e96a0d3b9f85cb51e140a86f21be715', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.2.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.2.json', - referenceNumber: '318', - name: 'Open LDAP Public License v2.2', - licenseId: 'OLDAP-2.2', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=470b0c18ec67621c85881b2733057fecf4a1acc3', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.2.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.2.1.json', - referenceNumber: '384', - name: 'Open LDAP Public License v2.2.1', - licenseId: 'OLDAP-2.2.1', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=4bc786f34b50aa301be6f5600f58a980070f481e', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.2.2.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.2.2.json', - referenceNumber: '170', - name: 'Open LDAP Public License 2.2.2', - licenseId: 'OLDAP-2.2.2', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=df2cc1e21eb7c160695f5b7cffd6296c151ba188', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.3.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.3.json', - referenceNumber: '230', - name: 'Open LDAP Public License v2.3', - licenseId: 'OLDAP-2.3', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=d32cf54a32d581ab475d23c810b0a7fbaf8d63c3', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.4.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.4.json', - referenceNumber: '115', - name: 'Open LDAP Public License v2.4', - licenseId: 'OLDAP-2.4', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=cd1284c4a91a8a380d904eee68d1583f989ed386', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.5.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.5.json', - referenceNumber: '108', - name: 'Open LDAP Public License v2.5', - licenseId: 'OLDAP-2.5', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=6852b9d90022e8593c98205413380536b1b5a7cf', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.6.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.6.json', - referenceNumber: '109', - name: 'Open LDAP Public License v2.6', - licenseId: 'OLDAP-2.6', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=1cae062821881f41b73012ba816434897abf4205', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.7.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.7.json', - referenceNumber: '229', - name: 'Open LDAP Public License v2.7', - licenseId: 'OLDAP-2.7', - seeAlso: [ - 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=47c2415c1df81556eeb39be6cad458ef87c534a2', - ], - isOsiApproved: false, - }, - { - reference: './OLDAP-2.8.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OLDAP-2.8.json', - referenceNumber: '252', - name: 'Open LDAP Public License v2.8', - licenseId: 'OLDAP-2.8', - seeAlso: ['http://www.openldap.org/software/release/license.html'], - isOsiApproved: false, - }, - { - reference: './OML.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OML.json', - referenceNumber: '171', - name: 'Open Market License', - licenseId: 'OML', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Open_Market_License'], - isOsiApproved: false, - }, - { - reference: './OPL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OPL-1.0.json', - referenceNumber: '338', - name: 'Open Public License v1.0', - licenseId: 'OPL-1.0', - seeAlso: [ - 'http://old.koalateam.com/jackaroo/OPL_1_0.TXT', - 'https://fedoraproject.org/wiki/Licensing/Open_Public_License', - ], - isOsiApproved: false, - }, - { - reference: './OSET-PL-2.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/OSET-PL-2.1.json', - referenceNumber: '203', - name: 'OSET Public License version 2.1', - licenseId: 'OSET-PL-2.1', - seeAlso: [ - 'http://www.osetfoundation.org/public-license', - 'https://opensource.org/licenses/OPL-2.1', - ], - isOsiApproved: true, - }, - { - reference: './OSL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/OSL-1.0.json', - referenceNumber: '95', - name: 'Open Software License 1.0', - licenseId: 'OSL-1.0', - seeAlso: ['https://opensource.org/licenses/OSL-1.0'], - isOsiApproved: true, - }, - { - reference: './OSL-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/OSL-1.1.json', - referenceNumber: '178', - name: 'Open Software License 1.1', - licenseId: 'OSL-1.1', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/OSL1.1'], - isOsiApproved: false, - }, - { - reference: './OSL-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/OSL-2.0.json', - referenceNumber: '364', - name: 'Open Software License 2.0', - licenseId: 'OSL-2.0', - seeAlso: [ - 'http://web.archive.org/web/20041020171434/http://www.rosenlaw.com/osl2.0.html', - ], - isOsiApproved: true, - }, - { - reference: './OSL-2.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/OSL-2.1.json', - referenceNumber: '157', - name: 'Open Software License 2.1', - licenseId: 'OSL-2.1', - seeAlso: [ - 'http://web.archive.org/web/20050212003940/http://www.rosenlaw.com/osl21.htm', - 'https://opensource.org/licenses/OSL-2.1', - ], - isOsiApproved: true, - }, - { - reference: './OSL-3.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/OSL-3.0.json', - referenceNumber: '149', - name: 'Open Software License 3.0', - licenseId: 'OSL-3.0', - seeAlso: [ - 'https://web.archive.org/web/20120101081418/http://rosenlaw.com:80/OSL3.0.htm', - 'https://opensource.org/licenses/OSL-3.0', - ], - isOsiApproved: true, - }, - { - reference: './OpenSSL.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/OpenSSL.json', - referenceNumber: '83', - name: 'OpenSSL License', - licenseId: 'OpenSSL', - seeAlso: ['http://www.openssl.org/source/license.html'], - isOsiApproved: false, - }, - { - reference: './PDDL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/PDDL-1.0.json', - referenceNumber: '132', - name: 'ODC Public Domain Dedication & License 1.0', - licenseId: 'PDDL-1.0', - seeAlso: ['http://opendatacommons.org/licenses/pddl/1.0/'], - isOsiApproved: false, - }, - { - reference: './PHP-3.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/PHP-3.0.json', - referenceNumber: '191', - name: 'PHP License v3.0', - licenseId: 'PHP-3.0', - seeAlso: [ - 'http://www.php.net/license/3_0.txt', - 'https://opensource.org/licenses/PHP-3.0', - ], - isOsiApproved: true, - }, - { - reference: './PHP-3.01.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/PHP-3.01.json', - referenceNumber: '3', - name: 'PHP License v3.01', - licenseId: 'PHP-3.01', - seeAlso: ['http://www.php.net/license/3_01.txt'], - isOsiApproved: false, - }, - { - reference: './PSF-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/PSF-2.0.json', - referenceNumber: '91', - name: 'Python Software Foundation License 2.0', - licenseId: 'PSF-2.0', - seeAlso: ['https://opensource.org/licenses/Python-2.0'], - isOsiApproved: false, - }, - { - reference: './Parity-6.0.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Parity-6.0.0.json', - referenceNumber: '406', - name: 'The Parity Public License 6.0.0', - licenseId: 'Parity-6.0.0', - seeAlso: ['https://paritylicense.com/versions/6.0.0.html'], - isOsiApproved: false, - }, - { - reference: './Plexus.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Plexus.json', - referenceNumber: '156', - name: 'Plexus Classworlds License', - licenseId: 'Plexus', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/Plexus_Classworlds_License', - ], - isOsiApproved: false, - }, - { - reference: './PostgreSQL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/PostgreSQL.json', - referenceNumber: '11', - name: 'PostgreSQL License', - licenseId: 'PostgreSQL', - seeAlso: [ - 'http://www.postgresql.org/about/licence', - 'https://opensource.org/licenses/PostgreSQL', - ], - isOsiApproved: true, - }, - { - reference: './Python-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Python-2.0.json', - referenceNumber: '393', - name: 'Python License 2.0', - licenseId: 'Python-2.0', - seeAlso: ['https://opensource.org/licenses/Python-2.0'], - isOsiApproved: true, - }, - { - reference: './QPL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/QPL-1.0.json', - referenceNumber: '289', - name: 'Q Public License 1.0', - licenseId: 'QPL-1.0', - seeAlso: [ - 'http://doc.qt.nokia.com/3.3/license.html', - 'https://opensource.org/licenses/QPL-1.0', - ], - isOsiApproved: true, - }, - { - reference: './Qhull.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Qhull.json', - referenceNumber: '124', - name: 'Qhull License', - licenseId: 'Qhull', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Qhull'], - isOsiApproved: false, - }, - { - reference: './RHeCos-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/RHeCos-1.1.json', - referenceNumber: '61', - name: 'Red Hat eCos Public License v1.1', - licenseId: 'RHeCos-1.1', - seeAlso: ['http://ecos.sourceware.org/old-license.html'], - isOsiApproved: false, - }, - { - reference: './RPL-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/RPL-1.1.json', - referenceNumber: '216', - name: 'Reciprocal Public License 1.1', - licenseId: 'RPL-1.1', - seeAlso: ['https://opensource.org/licenses/RPL-1.1'], - isOsiApproved: true, - }, - { - reference: './RPL-1.5.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/RPL-1.5.json', - referenceNumber: '105', - name: 'Reciprocal Public License 1.5', - licenseId: 'RPL-1.5', - seeAlso: ['https://opensource.org/licenses/RPL-1.5'], - isOsiApproved: true, - }, - { - reference: './RPSL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/RPSL-1.0.json', - referenceNumber: '53', - name: 'RealNetworks Public Source License v1.0', - licenseId: 'RPSL-1.0', - seeAlso: [ - 'https://helixcommunity.org/content/rpsl', - 'https://opensource.org/licenses/RPSL-1.0', - ], - isOsiApproved: true, - }, - { - reference: './RSA-MD.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/RSA-MD.json', - referenceNumber: '279', - name: 'RSA Message-Digest License ', - licenseId: 'RSA-MD', - seeAlso: ['http://www.faqs.org/rfcs/rfc1321.html'], - isOsiApproved: false, - }, - { - reference: './RSCPL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/RSCPL.json', - referenceNumber: '339', - name: 'Ricoh Source Code Public License', - licenseId: 'RSCPL', - seeAlso: [ - 'http://wayback.archive.org/web/20060715140826/http://www.risource.org/RPL/RPL-1.0A.shtml', - 'https://opensource.org/licenses/RSCPL', - ], - isOsiApproved: true, - }, - { - reference: './Rdisc.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Rdisc.json', - referenceNumber: '322', - name: 'Rdisc License', - licenseId: 'Rdisc', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Rdisc_License'], - isOsiApproved: false, - }, - { - reference: './Ruby.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Ruby.json', - referenceNumber: '14', - name: 'Ruby License', - licenseId: 'Ruby', - seeAlso: ['http://www.ruby-lang.org/en/LICENSE.txt'], - isOsiApproved: false, - }, - { - reference: './SAX-PD.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SAX-PD.json', - referenceNumber: '152', - name: 'Sax Public Domain Notice', - licenseId: 'SAX-PD', - seeAlso: ['http://www.saxproject.org/copying.html'], - isOsiApproved: false, - }, - { - reference: './SCEA.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SCEA.json', - referenceNumber: '137', - name: 'SCEA Shared Source License', - licenseId: 'SCEA', - seeAlso: ['http://research.scea.com/scea_shared_source_license.html'], - isOsiApproved: false, - }, - { - reference: './SGI-B-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SGI-B-1.0.json', - referenceNumber: '189', - name: 'SGI Free Software License B v1.0', - licenseId: 'SGI-B-1.0', - seeAlso: ['http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.1.0.html'], - isOsiApproved: false, - }, - { - reference: './SGI-B-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SGI-B-1.1.json', - referenceNumber: '288', - name: 'SGI Free Software License B v1.1', - licenseId: 'SGI-B-1.1', - seeAlso: ['http://oss.sgi.com/projects/FreeB/'], - isOsiApproved: false, - }, - { - reference: './SGI-B-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/SGI-B-2.0.json', - referenceNumber: '31', - name: 'SGI Free Software License B v2.0', - licenseId: 'SGI-B-2.0', - seeAlso: ['http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf'], - isOsiApproved: false, - }, - { - reference: './SHL-0.5.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SHL-0.5.json', - referenceNumber: '49', - name: 'Solderpad Hardware License v0.5', - licenseId: 'SHL-0.5', - seeAlso: ['https://solderpad.org/licenses/SHL-0.5/'], - isOsiApproved: false, - }, - { - reference: './SHL-0.51.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SHL-0.51.json', - referenceNumber: '281', - name: 'Solderpad Hardware License, Version 0.51', - licenseId: 'SHL-0.51', - seeAlso: ['https://solderpad.org/licenses/SHL-0.51/'], - isOsiApproved: false, - }, - { - reference: './SISSL.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/SISSL.json', - referenceNumber: '78', - name: 'Sun Industry Standards Source License v1.1', - licenseId: 'SISSL', - seeAlso: [ - 'http://www.openoffice.org/licenses/sissl_license.html', - 'https://opensource.org/licenses/SISSL', - ], - isOsiApproved: true, - }, - { - reference: './SISSL-1.2.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SISSL-1.2.json', - referenceNumber: '62', - name: 'Sun Industry Standards Source License v1.2', - licenseId: 'SISSL-1.2', - seeAlso: [ - 'http://gridscheduler.sourceforge.net/Gridengine_SISSL_license.html', - ], - isOsiApproved: false, - }, - { - reference: './SMLNJ.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/SMLNJ.json', - referenceNumber: '218', - name: 'Standard ML of New Jersey License', - licenseId: 'SMLNJ', - seeAlso: ['https://www.smlnj.org/license.html'], - isOsiApproved: false, - }, - { - reference: './SMPPL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SMPPL.json', - referenceNumber: '106', - name: 'Secure Messaging Protocol Public License', - licenseId: 'SMPPL', - seeAlso: [ - 'https://github.com/dcblake/SMP/blob/master/Documentation/License.txt', - ], - isOsiApproved: false, - }, - { - reference: './SNIA.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SNIA.json', - referenceNumber: '302', - name: 'SNIA Public License 1.1', - licenseId: 'SNIA', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/SNIA_Public_License'], - isOsiApproved: false, - }, - { - reference: './SPL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/SPL-1.0.json', - referenceNumber: '247', - name: 'Sun Public License v1.0', - licenseId: 'SPL-1.0', - seeAlso: ['https://opensource.org/licenses/SPL-1.0'], - isOsiApproved: true, - }, - { - reference: './SSH-OpenSSH.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SSH-OpenSSH.json', - referenceNumber: '22', - name: 'SSH OpenSSH license', - licenseId: 'SSH-OpenSSH', - seeAlso: [ - 'https://github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/LICENCE#L10', - ], - isOsiApproved: false, - }, - { - reference: './SSH-short.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SSH-short.json', - referenceNumber: '67', - name: 'SSH short notice', - licenseId: 'SSH-short', - seeAlso: [ - 'https://github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/pathnames.h', - 'http://web.mit.edu/kolya/.f/root/athena.mit.edu/sipb.mit.edu/project/openssh/OldFiles/src/openssh-2.9.9p2/ssh-add.1', - 'https://joinup.ec.europa.eu/svn/lesoll/trunk/italc/lib/src/dsa_key.cpp', - ], - isOsiApproved: false, - }, - { - reference: './SSPL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SSPL-1.0.json', - referenceNumber: '334', - name: 'Server Side Public License, v 1', - licenseId: 'SSPL-1.0', - seeAlso: ['https://www.mongodb.com/licensing/server-side-public-license'], - isOsiApproved: false, - }, - { - reference: './SWL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SWL.json', - referenceNumber: '93', - name: 'Scheme Widget Library (SWL) Software License Agreement', - licenseId: 'SWL', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/SWL'], - isOsiApproved: false, - }, - { - reference: './Saxpath.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Saxpath.json', - referenceNumber: '34', - name: 'Saxpath License', - licenseId: 'Saxpath', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Saxpath_License'], - isOsiApproved: false, - }, - { - reference: './Sendmail.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Sendmail.json', - referenceNumber: '293', - name: 'Sendmail License', - licenseId: 'Sendmail', - seeAlso: [ - 'http://www.sendmail.com/pdfs/open_source/sendmail_license.pdf', - 'https://web.archive.org/web/20160322142305/https://www.sendmail.com/pdfs/open_source/sendmail_license.pdf', - ], - isOsiApproved: false, - }, - { - reference: './Sendmail-8.23.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Sendmail-8.23.json', - referenceNumber: '176', - name: 'Sendmail License 8.23', - licenseId: 'Sendmail-8.23', - seeAlso: [ - 'https://www.proofpoint.com/sites/default/files/sendmail-license.pdf', - 'https://web.archive.org/web/20181003101040/https://www.proofpoint.com/sites/default/files/sendmail-license.pdf', - ], - isOsiApproved: false, - }, - { - reference: './SimPL-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SimPL-2.0.json', - referenceNumber: '250', - name: 'Simple Public License 2.0', - licenseId: 'SimPL-2.0', - seeAlso: ['https://opensource.org/licenses/SimPL-2.0'], - isOsiApproved: true, - }, - { - reference: './Sleepycat.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Sleepycat.json', - referenceNumber: '56', - name: 'Sleepycat License', - licenseId: 'Sleepycat', - seeAlso: ['https://opensource.org/licenses/Sleepycat'], - isOsiApproved: true, - }, - { - reference: './Spencer-86.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Spencer-86.json', - referenceNumber: '184', - name: 'Spencer License 86', - licenseId: 'Spencer-86', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License', - ], - isOsiApproved: false, - }, - { - reference: './Spencer-94.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Spencer-94.json', - referenceNumber: '213', - name: 'Spencer License 94', - licenseId: 'Spencer-94', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License', - ], - isOsiApproved: false, - }, - { - reference: './Spencer-99.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Spencer-99.json', - referenceNumber: '64', - name: 'Spencer License 99', - licenseId: 'Spencer-99', - seeAlso: [ - 'http://www.opensource.apple.com/source/tcl/tcl-5/tcl/generic/regfronts.c', - ], - isOsiApproved: false, - }, - { - reference: './StandardML-NJ.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/StandardML-NJ.json', - referenceNumber: '285', - name: 'Standard ML of New Jersey License', - licenseId: 'StandardML-NJ', - seeAlso: ['http://www.smlnj.org//license.html'], - isOsiApproved: false, - }, - { - reference: './SugarCRM-1.1.3.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/SugarCRM-1.1.3.json', - referenceNumber: '342', - name: 'SugarCRM Public License v1.1.3', - licenseId: 'SugarCRM-1.1.3', - seeAlso: ['http://www.sugarcrm.com/crm/SPL'], - isOsiApproved: false, - }, - { - reference: './TAPR-OHL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/TAPR-OHL-1.0.json', - referenceNumber: '9', - name: 'TAPR Open Hardware License v1.0', - licenseId: 'TAPR-OHL-1.0', - seeAlso: ['https://www.tapr.org/OHL'], - isOsiApproved: false, - }, - { - reference: './TCL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/TCL.json', - referenceNumber: '54', - name: 'TCL/TK License', - licenseId: 'TCL', - seeAlso: [ - 'http://www.tcl.tk/software/tcltk/license.html', - 'https://fedoraproject.org/wiki/Licensing/TCL', - ], - isOsiApproved: false, - }, - { - reference: './TCP-wrappers.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/TCP-wrappers.json', - referenceNumber: '234', - name: 'TCP Wrappers License', - licenseId: 'TCP-wrappers', - seeAlso: ['http://rc.quest.com/topics/openssh/license.php#tcpwrappers'], - isOsiApproved: false, - }, - { - reference: './TMate.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/TMate.json', - referenceNumber: '402', - name: 'TMate Open Source License', - licenseId: 'TMate', - seeAlso: ['http://svnkit.com/license.html'], - isOsiApproved: false, - }, - { - reference: './TORQUE-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/TORQUE-1.1.json', - referenceNumber: '188', - name: 'TORQUE v2.5+ Software License v1.1', - licenseId: 'TORQUE-1.1', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/TORQUEv1.1'], - isOsiApproved: false, - }, - { - reference: './TOSL.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/TOSL.json', - referenceNumber: '251', - name: 'Trusster Open Source License', - licenseId: 'TOSL', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/TOSL'], - isOsiApproved: false, - }, - { - reference: './TU-Berlin-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/TU-Berlin-1.0.json', - referenceNumber: '372', - name: 'Technische Universitaet Berlin License 1.0', - licenseId: 'TU-Berlin-1.0', - seeAlso: [ - 'https://github.com/swh/ladspa/blob/7bf6f3799fdba70fda297c2d8fd9f526803d9680/gsm/COPYRIGHT', - ], - isOsiApproved: false, - }, - { - reference: './TU-Berlin-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/TU-Berlin-2.0.json', - referenceNumber: '392', - name: 'Technische Universitaet Berlin License 2.0', - licenseId: 'TU-Berlin-2.0', - seeAlso: [ - 'https://github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt', - ], - isOsiApproved: false, - }, - { - reference: './UCL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/UCL-1.0.json', - referenceNumber: '291', - name: 'Upstream Compatibility License v1.0', - licenseId: 'UCL-1.0', - seeAlso: ['https://opensource.org/licenses/UCL-1.0'], - isOsiApproved: true, - }, - { - reference: './UPL-1.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/UPL-1.0.json', - referenceNumber: '144', - name: 'Universal Permissive License v1.0', - licenseId: 'UPL-1.0', - seeAlso: ['https://opensource.org/licenses/UPL'], - isOsiApproved: true, - }, - { - reference: './Unicode-DFS-2015.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Unicode-DFS-2015.json', - referenceNumber: '260', - name: 'Unicode License Agreement - Data Files and Software (2015)', - licenseId: 'Unicode-DFS-2015', - seeAlso: [ - 'https://web.archive.org/web/20151224134844/http://unicode.org/copyright.html', - ], - isOsiApproved: false, - }, - { - reference: './Unicode-DFS-2016.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Unicode-DFS-2016.json', - referenceNumber: '370', - name: 'Unicode License Agreement - Data Files and Software (2016)', - licenseId: 'Unicode-DFS-2016', - seeAlso: ['http://www.unicode.org/copyright.html'], - isOsiApproved: false, - }, - { - reference: './Unicode-TOU.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Unicode-TOU.json', - referenceNumber: '16', - name: 'Unicode Terms of Use', - licenseId: 'Unicode-TOU', - seeAlso: ['http://www.unicode.org/copyright.html'], - isOsiApproved: false, - }, - { - reference: './Unlicense.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Unlicense.json', - referenceNumber: '173', - name: 'The Unlicense', - licenseId: 'Unlicense', - seeAlso: ['https://unlicense.org/'], - isOsiApproved: false, - }, - { - reference: './VOSTROM.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/VOSTROM.json', - referenceNumber: '352', - name: 'VOSTROM Public License for Open Source', - licenseId: 'VOSTROM', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/VOSTROM'], - isOsiApproved: false, - }, - { - reference: './VSL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/VSL-1.0.json', - referenceNumber: '390', - name: 'Vovida Software License v1.0', - licenseId: 'VSL-1.0', - seeAlso: ['https://opensource.org/licenses/VSL-1.0'], - isOsiApproved: true, - }, - { - reference: './Vim.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Vim.json', - referenceNumber: '206', - name: 'Vim License', - licenseId: 'Vim', - seeAlso: ['http://vimdoc.sourceforge.net/htmldoc/uganda.html'], - isOsiApproved: false, - }, - { - reference: './W3C.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/W3C.json', - referenceNumber: '107', - name: 'W3C Software Notice and License (2002-12-31)', - licenseId: 'W3C', - seeAlso: [ - 'http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231.html', - 'https://opensource.org/licenses/W3C', - ], - isOsiApproved: true, - }, - { - reference: './W3C-19980720.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/W3C-19980720.json', - referenceNumber: '266', - name: 'W3C Software Notice and License (1998-07-20)', - licenseId: 'W3C-19980720', - seeAlso: [ - 'http://www.w3.org/Consortium/Legal/copyright-software-19980720.html', - ], - isOsiApproved: false, - }, - { - reference: './W3C-20150513.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/W3C-20150513.json', - referenceNumber: '111', - name: 'W3C Software Notice and Document License (2015-05-13)', - licenseId: 'W3C-20150513', - seeAlso: [ - 'https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document', - ], - isOsiApproved: false, - }, - { - reference: './WTFPL.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/WTFPL.json', - referenceNumber: '20', - name: 'Do What The F*ck You Want To Public License', - licenseId: 'WTFPL', - seeAlso: ['http://sam.zoy.org/wtfpl/COPYING'], - isOsiApproved: false, - }, - { - reference: './Watcom-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Watcom-1.0.json', - referenceNumber: '141', - name: 'Sybase Open Watcom Public License 1.0', - licenseId: 'Watcom-1.0', - seeAlso: ['https://opensource.org/licenses/Watcom-1.0'], - isOsiApproved: true, - }, - { - reference: './Wsuipa.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Wsuipa.json', - referenceNumber: '255', - name: 'Wsuipa License', - licenseId: 'Wsuipa', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Wsuipa'], - isOsiApproved: false, - }, - { - reference: './X11.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/X11.json', - referenceNumber: '99', - name: 'X11 License', - licenseId: 'X11', - seeAlso: ['http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3'], - isOsiApproved: false, - }, - { - reference: './XFree86-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/XFree86-1.1.json', - referenceNumber: '155', - name: 'XFree86 License 1.1', - licenseId: 'XFree86-1.1', - seeAlso: ['http://www.xfree86.org/current/LICENSE4.html'], - isOsiApproved: false, - }, - { - reference: './XSkat.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/XSkat.json', - referenceNumber: '81', - name: 'XSkat License', - licenseId: 'XSkat', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/XSkat_License'], - isOsiApproved: false, - }, - { - reference: './Xerox.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Xerox.json', - referenceNumber: '224', - name: 'Xerox License', - licenseId: 'Xerox', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Xerox'], - isOsiApproved: false, - }, - { - reference: './Xnet.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Xnet.json', - referenceNumber: '313', - name: 'X.Net License', - licenseId: 'Xnet', - seeAlso: ['https://opensource.org/licenses/Xnet'], - isOsiApproved: true, - }, - { - reference: './YPL-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/YPL-1.0.json', - referenceNumber: '292', - name: 'Yahoo! Public License v1.0', - licenseId: 'YPL-1.0', - seeAlso: ['http://www.zimbra.com/license/yahoo_public_license_1.0.html'], - isOsiApproved: false, - }, - { - reference: './YPL-1.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/YPL-1.1.json', - referenceNumber: '40', - name: 'Yahoo! Public License v1.1', - licenseId: 'YPL-1.1', - seeAlso: ['http://www.zimbra.com/license/yahoo_public_license_1.1.html'], - isOsiApproved: false, - }, - { - reference: './ZPL-1.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/ZPL-1.1.json', - referenceNumber: '85', - name: 'Zope Public License 1.1', - licenseId: 'ZPL-1.1', - seeAlso: ['http://old.zope.org/Resources/License/ZPL-1.1'], - isOsiApproved: false, - }, - { - reference: './ZPL-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/ZPL-2.0.json', - referenceNumber: '112', - name: 'Zope Public License 2.0', - licenseId: 'ZPL-2.0', - seeAlso: [ - 'http://old.zope.org/Resources/License/ZPL-2.0', - 'https://opensource.org/licenses/ZPL-2.0', - ], - isOsiApproved: true, - }, - { - reference: './ZPL-2.1.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/ZPL-2.1.json', - referenceNumber: '368', - name: 'Zope Public License 2.1', - licenseId: 'ZPL-2.1', - seeAlso: ['http://old.zope.org/Resources/ZPL/'], - isOsiApproved: false, - }, - { - reference: './Zed.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Zed.json', - referenceNumber: '114', - name: 'Zed License', - licenseId: 'Zed', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Zed'], - isOsiApproved: false, - }, - { - reference: './Zend-2.0.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Zend-2.0.json', - referenceNumber: '373', - name: 'Zend License v2.0', - licenseId: 'Zend-2.0', - seeAlso: [ - 'https://web.archive.org/web/20130517195954/http://www.zend.com/license/2_00.txt', - ], - isOsiApproved: false, - }, - { - reference: './Zimbra-1.3.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Zimbra-1.3.json', - referenceNumber: '175', - name: 'Zimbra Public License v1.3', - licenseId: 'Zimbra-1.3', - seeAlso: [ - 'http://web.archive.org/web/20100302225219/http://www.zimbra.com/license/zimbra-public-license-1-3.html', - ], - isOsiApproved: false, - }, - { - reference: './Zimbra-1.4.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/Zimbra-1.4.json', - referenceNumber: '385', - name: 'Zimbra Public License v1.4', - licenseId: 'Zimbra-1.4', - seeAlso: ['http://www.zimbra.com/legal/zimbra-public-license-1-4'], - isOsiApproved: false, - }, - { - reference: './Zlib.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/Zlib.json', - referenceNumber: '45', - name: 'zlib License', - licenseId: 'Zlib', - seeAlso: [ - 'http://www.zlib.net/zlib_license.html', - 'https://opensource.org/licenses/Zlib', - ], - isOsiApproved: true, - }, - { - reference: './blessing.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/blessing.json', - referenceNumber: '301', - name: 'SQLite Blessing', - licenseId: 'blessing', - seeAlso: [ - 'https://www.sqlite.org/src/artifact/e33a4df7e32d742a?ln=4-9', - 'https://sqlite.org/src/artifact/df5091916dbb40e6', - ], - isOsiApproved: false, - }, - { - reference: './bzip2-1.0.5.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/bzip2-1.0.5.json', - referenceNumber: '186', - name: 'bzip2 and libbzip2 License v1.0.5', - licenseId: 'bzip2-1.0.5', - seeAlso: ['http://bzip.org/1.0.5/bzip2-manual-1.0.5.html'], - isOsiApproved: false, - }, - { - reference: './bzip2-1.0.6.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/bzip2-1.0.6.json', - referenceNumber: '69', - name: 'bzip2 and libbzip2 License v1.0.6', - licenseId: 'bzip2-1.0.6', - seeAlso: ['https://github.com/asimonov-im/bzip2/blob/master/LICENSE'], - isOsiApproved: false, - }, - { - reference: './copyleft-next-0.3.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/copyleft-next-0.3.0.json', - referenceNumber: '312', - name: 'copyleft-next 0.3.0', - licenseId: 'copyleft-next-0.3.0', - seeAlso: [ - 'https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.0', - ], - isOsiApproved: false, - }, - { - reference: './copyleft-next-0.3.1.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/copyleft-next-0.3.1.json', - referenceNumber: '378', - name: 'copyleft-next 0.3.1', - licenseId: 'copyleft-next-0.3.1', - seeAlso: [ - 'https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.1', - ], - isOsiApproved: false, - }, - { - reference: './curl.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/curl.json', - referenceNumber: '314', - name: 'curl License', - licenseId: 'curl', - seeAlso: ['https://github.com/bagder/curl/blob/master/COPYING'], - isOsiApproved: false, - }, - { - reference: './diffmark.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/diffmark.json', - referenceNumber: '397', - name: 'diffmark license', - licenseId: 'diffmark', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/diffmark'], - isOsiApproved: false, - }, - { - reference: './dvipdfm.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/dvipdfm.json', - referenceNumber: '19', - name: 'dvipdfm License', - licenseId: 'dvipdfm', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/dvipdfm'], - isOsiApproved: false, - }, - { - reference: './eCos-2.0.html', - isDeprecatedLicenseId: true, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/eCos-2.0.json', - referenceNumber: '272', - name: 'eCos license version 2.0', - licenseId: 'eCos-2.0', - seeAlso: ['https://www.gnu.org/licenses/ecos-license.html'], - isOsiApproved: false, - }, - { - reference: './eGenix.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/eGenix.json', - referenceNumber: '214', - name: 'eGenix.com Public License 1.1.0', - licenseId: 'eGenix', - seeAlso: [ - 'http://www.egenix.com/products/eGenix.com-Public-License-1.1.0.pdf', - 'https://fedoraproject.org/wiki/Licensing/eGenix.com_Public_License_1.1.0', - ], - isOsiApproved: false, - }, - { - reference: './etalab-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/etalab-2.0.json', - referenceNumber: '259', - name: 'Etalab Open License 2.0', - licenseId: 'etalab-2.0', - seeAlso: [ - 'https://github.com/DISIC/politique-de-contribution-open-source/blob/master/LICENSE.pdf', - 'https://raw.githubusercontent.com/DISIC/politique-de-contribution-open-source/master/LICENSE', - ], - isOsiApproved: false, - }, - { - reference: './gSOAP-1.3b.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/gSOAP-1.3b.json', - referenceNumber: '167', - name: 'gSOAP Public License v1.3b', - licenseId: 'gSOAP-1.3b', - seeAlso: ['http://www.cs.fsu.edu/~engelen/license.html'], - isOsiApproved: false, - }, - { - reference: './gnuplot.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/gnuplot.json', - referenceNumber: '383', - name: 'gnuplot License', - licenseId: 'gnuplot', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Gnuplot'], - isOsiApproved: false, - }, - { - reference: './iMatix.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/iMatix.json', - referenceNumber: '177', - name: 'iMatix Standard Function Library Agreement', - licenseId: 'iMatix', - seeAlso: ['http://legacy.imatix.com/html/sfl/sfl4.htm#license'], - isOsiApproved: false, - }, - { - reference: './libpng-2.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/libpng-2.0.json', - referenceNumber: '103', - name: 'PNG Reference Library version 2', - licenseId: 'libpng-2.0', - seeAlso: ['http://www.libpng.org/pub/png/src/libpng-LICENSE.txt'], - isOsiApproved: false, - }, - { - reference: './libselinux-1.0.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/libselinux-1.0.json', - referenceNumber: '18', - name: 'libselinux public domain notice', - licenseId: 'libselinux-1.0', - seeAlso: [ - 'https://github.com/SELinuxProject/selinux/blob/master/libselinux/LICENSE', - ], - isOsiApproved: false, - }, - { - reference: './libtiff.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/libtiff.json', - referenceNumber: '407', - name: 'libtiff License', - licenseId: 'libtiff', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/libtiff'], - isOsiApproved: false, - }, - { - reference: './mpich2.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/mpich2.json', - referenceNumber: '60', - name: 'mpich2 License', - licenseId: 'mpich2', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/MIT'], - isOsiApproved: false, - }, - { - reference: './psfrag.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/psfrag.json', - referenceNumber: '408', - name: 'psfrag License', - licenseId: 'psfrag', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/psfrag'], - isOsiApproved: false, - }, - { - reference: './psutils.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/psutils.json', - referenceNumber: '277', - name: 'psutils License', - licenseId: 'psutils', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/psutils'], - isOsiApproved: false, - }, - { - reference: './wxWindows.html', - isDeprecatedLicenseId: true, - detailsUrl: 'http://spdx.org/licenses/wxWindows.json', - referenceNumber: '244', - name: 'wxWindows Library License', - licenseId: 'wxWindows', - seeAlso: ['https://opensource.org/licenses/WXwindows'], - isOsiApproved: false, - }, - { - reference: './xinetd.html', - isDeprecatedLicenseId: false, - isFsfLibre: true, - detailsUrl: 'http://spdx.org/licenses/xinetd.json', - referenceNumber: '399', - name: 'xinetd License', - licenseId: 'xinetd', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/Xinetd_License'], - isOsiApproved: false, - }, - { - reference: './xpp.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/xpp.json', - referenceNumber: '96', - name: 'XPP License', - licenseId: 'xpp', - seeAlso: ['https://fedoraproject.org/wiki/Licensing/xpp'], - isOsiApproved: false, - }, - { - reference: './zlib-acknowledgement.html', - isDeprecatedLicenseId: false, - detailsUrl: 'http://spdx.org/licenses/zlib-acknowledgement.json', - referenceNumber: '246', - name: 'zlib/libpng License with Acknowledgement', - licenseId: 'zlib-acknowledgement', - seeAlso: [ - 'https://fedoraproject.org/wiki/Licensing/ZlibWithAcknowledgement', - ], - isOsiApproved: false, - }, - ], - releaseDate: '2020-02-13', + licenseListVersion: '3.8-4-gd79c632', + licenses: [ + { + reference: './0BSD.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/0BSD.json', + referenceNumber: '231', + name: 'BSD Zero Clause License', + licenseId: '0BSD', + seeAlso: ['http://landley.net/toybox/license.html'], + isOsiApproved: true, + }, + { + reference: './AAL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/AAL.json', + referenceNumber: '57', + name: 'Attribution Assurance License', + licenseId: 'AAL', + seeAlso: ['https://opensource.org/licenses/attribution'], + isOsiApproved: true, + }, + { + reference: './ADSL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/ADSL.json', + referenceNumber: '210', + name: 'Amazon Digital Services License', + licenseId: 'ADSL', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing/AmazonDigitalServicesLicense', + ], + isOsiApproved: false, + }, + { + reference: './AFL-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/AFL-1.1.json', + referenceNumber: '28', + name: 'Academic Free License v1.1', + licenseId: 'AFL-1.1', + seeAlso: [ + 'http://opensource.linux-mirror.org/licenses/afl-1.1.txt', + 'http://wayback.archive.org/web/20021004124254/http://www.opensource.org/licenses/academic.php', + ], + isOsiApproved: true, + }, + { + reference: './AFL-1.2.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/AFL-1.2.json', + referenceNumber: '215', + name: 'Academic Free License v1.2', + licenseId: 'AFL-1.2', + seeAlso: [ + 'http://opensource.linux-mirror.org/licenses/afl-1.2.txt', + 'http://wayback.archive.org/web/20021204204652/http://www.opensource.org/licenses/academic.php', + ], + isOsiApproved: true, + }, + { + reference: './AFL-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/AFL-2.0.json', + referenceNumber: '330', + name: 'Academic Free License v2.0', + licenseId: 'AFL-2.0', + seeAlso: [ + 'http://wayback.archive.org/web/20060924134533/http://www.opensource.org/licenses/afl-2.0.txt', + ], + isOsiApproved: true, + }, + { + reference: './AFL-2.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/AFL-2.1.json', + referenceNumber: '241', + name: 'Academic Free License v2.1', + licenseId: 'AFL-2.1', + seeAlso: ['http://opensource.linux-mirror.org/licenses/afl-2.1.txt'], + isOsiApproved: true, + }, + { + reference: './AFL-3.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/AFL-3.0.json', + referenceNumber: '350', + name: 'Academic Free License v3.0', + licenseId: 'AFL-3.0', + seeAlso: [ + 'http://www.rosenlaw.com/AFL3.0.htm', + 'https://opensource.org/licenses/afl-3.0', + ], + isOsiApproved: true, + }, + { + reference: './AGPL-1.0.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/AGPL-1.0.json', + referenceNumber: '169', + name: 'Affero General Public License v1.0', + licenseId: 'AGPL-1.0', + seeAlso: ['http://www.affero.org/oagpl.html'], + isOsiApproved: false, + }, + { + reference: './AGPL-1.0-only.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/AGPL-1.0-only.json', + referenceNumber: '68', + name: 'Affero General Public License v1.0 only', + licenseId: 'AGPL-1.0-only', + seeAlso: ['http://www.affero.org/oagpl.html'], + isOsiApproved: false, + }, + { + reference: './AGPL-1.0-or-later.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/AGPL-1.0-or-later.json', + referenceNumber: '162', + name: 'Affero General Public License v1.0 or later', + licenseId: 'AGPL-1.0-or-later', + seeAlso: ['http://www.affero.org/oagpl.html'], + isOsiApproved: false, + }, + { + reference: './AGPL-3.0.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/AGPL-3.0.json', + referenceNumber: '143', + name: 'GNU Affero General Public License v3.0', + licenseId: 'AGPL-3.0', + seeAlso: [ + 'https://www.gnu.org/licenses/agpl.txt', + 'https://opensource.org/licenses/AGPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './AGPL-3.0-only.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/AGPL-3.0-only.json', + referenceNumber: '287', + name: 'GNU Affero General Public License v3.0 only', + licenseId: 'AGPL-3.0-only', + seeAlso: [ + 'https://www.gnu.org/licenses/agpl.txt', + 'https://opensource.org/licenses/AGPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './AGPL-3.0-or-later.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/AGPL-3.0-or-later.json', + referenceNumber: '154', + name: 'GNU Affero General Public License v3.0 or later', + licenseId: 'AGPL-3.0-or-later', + seeAlso: [ + 'https://www.gnu.org/licenses/agpl.txt', + 'https://opensource.org/licenses/AGPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './AMDPLPA.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/AMDPLPA.json', + referenceNumber: '128', + name: "AMD's plpa_map.c License", + licenseId: 'AMDPLPA', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/AMD_plpa_map_License'], + isOsiApproved: false, + }, + { + reference: './AML.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/AML.json', + referenceNumber: '151', + name: 'Apple MIT License', + licenseId: 'AML', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Apple_MIT_License'], + isOsiApproved: false, + }, + { + reference: './AMPAS.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/AMPAS.json', + referenceNumber: '131', + name: 'Academy of Motion Picture Arts and Sciences BSD', + licenseId: 'AMPAS', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/BSD#AMPASBSD'], + isOsiApproved: false, + }, + { + reference: './ANTLR-PD.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/ANTLR-PD.json', + referenceNumber: '44', + name: 'ANTLR Software Rights Notice', + licenseId: 'ANTLR-PD', + seeAlso: ['http://www.antlr2.org/license.html'], + isOsiApproved: false, + }, + { + reference: './APAFML.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/APAFML.json', + referenceNumber: '240', + name: 'Adobe Postscript AFM License', + licenseId: 'APAFML', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/AdobePostscriptAFM'], + isOsiApproved: false, + }, + { + reference: './APL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/APL-1.0.json', + referenceNumber: '268', + name: 'Adaptive Public License 1.0', + licenseId: 'APL-1.0', + seeAlso: ['https://opensource.org/licenses/APL-1.0'], + isOsiApproved: true, + }, + { + reference: './APSL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/APSL-1.0.json', + referenceNumber: '375', + name: 'Apple Public Source License 1.0', + licenseId: 'APSL-1.0', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing/Apple_Public_Source_License_1.0', + ], + isOsiApproved: true, + }, + { + reference: './APSL-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/APSL-1.1.json', + referenceNumber: '327', + name: 'Apple Public Source License 1.1', + licenseId: 'APSL-1.1', + seeAlso: [ + 'http://www.opensource.apple.com/source/IOSerialFamily/IOSerialFamily-7/APPLE_LICENSE', + ], + isOsiApproved: true, + }, + { + reference: './APSL-1.2.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/APSL-1.2.json', + referenceNumber: '194', + name: 'Apple Public Source License 1.2', + licenseId: 'APSL-1.2', + seeAlso: ['http://www.samurajdata.se/opensource/mirror/licenses/apsl.php'], + isOsiApproved: true, + }, + { + reference: './APSL-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/APSL-2.0.json', + referenceNumber: '136', + name: 'Apple Public Source License 2.0', + licenseId: 'APSL-2.0', + seeAlso: ['http://www.opensource.apple.com/license/apsl/'], + isOsiApproved: true, + }, + { + reference: './Abstyles.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Abstyles.json', + referenceNumber: '72', + name: 'Abstyles License', + licenseId: 'Abstyles', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Abstyles'], + isOsiApproved: false, + }, + { + reference: './Adobe-2006.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Adobe-2006.json', + referenceNumber: '299', + name: 'Adobe Systems Incorporated Source Code License Agreement', + licenseId: 'Adobe-2006', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/AdobeLicense'], + isOsiApproved: false, + }, + { + reference: './Adobe-Glyph.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Adobe-Glyph.json', + referenceNumber: '332', + name: 'Adobe Glyph List License', + licenseId: 'Adobe-Glyph', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/MIT#AdobeGlyph'], + isOsiApproved: false, + }, + { + reference: './Afmparse.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Afmparse.json', + referenceNumber: '321', + name: 'Afmparse License', + licenseId: 'Afmparse', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Afmparse'], + isOsiApproved: false, + }, + { + reference: './Aladdin.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Aladdin.json', + referenceNumber: '304', + name: 'Aladdin Free Public License', + licenseId: 'Aladdin', + seeAlso: ['http://pages.cs.wisc.edu/~ghost/doc/AFPL/6.01/Public.htm'], + isOsiApproved: false, + }, + { + reference: './Apache-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Apache-1.0.json', + referenceNumber: '32', + name: 'Apache License 1.0', + licenseId: 'Apache-1.0', + seeAlso: ['http://www.apache.org/licenses/LICENSE-1.0'], + isOsiApproved: false, + }, + { + reference: './Apache-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Apache-1.1.json', + referenceNumber: '264', + name: 'Apache License 1.1', + licenseId: 'Apache-1.1', + seeAlso: [ + 'http://apache.org/licenses/LICENSE-1.1', + 'https://opensource.org/licenses/Apache-1.1', + ], + isOsiApproved: true, + }, + { + reference: './Apache-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Apache-2.0.json', + referenceNumber: '355', + name: 'Apache License 2.0', + licenseId: 'Apache-2.0', + seeAlso: [ + 'http://www.apache.org/licenses/LICENSE-2.0', + 'https://opensource.org/licenses/Apache-2.0', + ], + isOsiApproved: true, + }, + { + reference: './Artistic-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Artistic-1.0.json', + referenceNumber: '263', + name: 'Artistic License 1.0', + licenseId: 'Artistic-1.0', + seeAlso: ['https://opensource.org/licenses/Artistic-1.0'], + isOsiApproved: true, + }, + { + reference: './Artistic-1.0-Perl.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Artistic-1.0-Perl.json', + referenceNumber: '295', + name: 'Artistic License 1.0 (Perl)', + licenseId: 'Artistic-1.0-Perl', + seeAlso: ['http://dev.perl.org/licenses/artistic.html'], + isOsiApproved: true, + }, + { + reference: './Artistic-1.0-cl8.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Artistic-1.0-cl8.json', + referenceNumber: '221', + name: 'Artistic License 1.0 w/clause 8', + licenseId: 'Artistic-1.0-cl8', + seeAlso: ['https://opensource.org/licenses/Artistic-1.0'], + isOsiApproved: true, + }, + { + reference: './Artistic-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Artistic-2.0.json', + referenceNumber: '75', + name: 'Artistic License 2.0', + licenseId: 'Artistic-2.0', + seeAlso: [ + 'http://www.perlfoundation.org/artistic_license_2_0', + 'https://opensource.org/licenses/artistic-license-2.0', + ], + isOsiApproved: true, + }, + { + reference: './BSD-1-Clause.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-1-Clause.json', + referenceNumber: '377', + name: 'BSD 1-Clause License', + licenseId: 'BSD-1-Clause', + seeAlso: [ + 'https://svnweb.freebsd.org/base/head/include/ifaddrs.h?revision=326823', + ], + isOsiApproved: false, + }, + { + reference: './BSD-2-Clause.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-2-Clause.json', + referenceNumber: '296', + name: 'BSD 2-Clause "Simplified" License', + licenseId: 'BSD-2-Clause', + seeAlso: ['https://opensource.org/licenses/BSD-2-Clause'], + isOsiApproved: true, + }, + { + reference: './BSD-2-Clause-FreeBSD.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/BSD-2-Clause-FreeBSD.json', + referenceNumber: '271', + name: 'BSD 2-Clause FreeBSD License', + licenseId: 'BSD-2-Clause-FreeBSD', + seeAlso: ['http://www.freebsd.org/copyright/freebsd-license.html'], + isOsiApproved: false, + }, + { + reference: './BSD-2-Clause-NetBSD.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-2-Clause-NetBSD.json', + referenceNumber: '179', + name: 'BSD 2-Clause NetBSD License', + licenseId: 'BSD-2-Clause-NetBSD', + seeAlso: ['http://www.netbsd.org/about/redistribution.html#default'], + isOsiApproved: false, + }, + { + reference: './BSD-2-Clause-Patent.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-2-Clause-Patent.json', + referenceNumber: '351', + name: 'BSD-2-Clause Plus Patent License', + licenseId: 'BSD-2-Clause-Patent', + seeAlso: ['https://opensource.org/licenses/BSDplusPatent'], + isOsiApproved: true, + }, + { + reference: './BSD-3-Clause.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause.json', + referenceNumber: '195', + name: 'BSD 3-Clause "New" or "Revised" License', + licenseId: 'BSD-3-Clause', + seeAlso: ['https://opensource.org/licenses/BSD-3-Clause'], + isOsiApproved: true, + }, + { + reference: './BSD-3-Clause-Attribution.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-Attribution.json', + referenceNumber: '39', + name: 'BSD with attribution', + licenseId: 'BSD-3-Clause-Attribution', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/BSD_with_Attribution'], + isOsiApproved: false, + }, + { + reference: './BSD-3-Clause-Clear.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-Clear.json', + referenceNumber: '84', + name: 'BSD 3-Clause Clear License', + licenseId: 'BSD-3-Clause-Clear', + seeAlso: ['http://labs.metacarta.com/license-explanation.html#license'], + isOsiApproved: false, + }, + { + reference: './BSD-3-Clause-LBNL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-LBNL.json', + referenceNumber: '142', + name: 'Lawrence Berkeley National Labs BSD variant license', + licenseId: 'BSD-3-Clause-LBNL', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/LBNLBSD'], + isOsiApproved: true, + }, + { + reference: './BSD-3-Clause-No-Nuclear-License.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.json', + referenceNumber: '59', + name: 'BSD 3-Clause No Nuclear License', + licenseId: 'BSD-3-Clause-No-Nuclear-License', + seeAlso: [ + 'http://download.oracle.com/otn-pub/java/licenses/bsd.txt?AuthParam=1467140197_43d516ce1776bd08a58235a7785be1cc', + ], + isOsiApproved: false, + }, + { + reference: './BSD-3-Clause-No-Nuclear-License-2014.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.json', + referenceNumber: '331', + name: 'BSD 3-Clause No Nuclear License 2014', + licenseId: 'BSD-3-Clause-No-Nuclear-License-2014', + seeAlso: ['https://java.net/projects/javaeetutorial/pages/BerkeleyLicense'], + isOsiApproved: false, + }, + { + reference: './BSD-3-Clause-No-Nuclear-Warranty.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.json', + referenceNumber: '113', + name: 'BSD 3-Clause No Nuclear Warranty', + licenseId: 'BSD-3-Clause-No-Nuclear-Warranty', + seeAlso: ['https://jogamp.org/git/?p=gluegen.git;a=blob_plain;f=LICENSE.txt'], + isOsiApproved: false, + }, + { + reference: './BSD-3-Clause-Open-MPI.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-3-Clause-Open-MPI.json', + referenceNumber: '207', + name: 'BSD 3-Clause Open MPI variant', + licenseId: 'BSD-3-Clause-Open-MPI', + seeAlso: [ + 'https://www.open-mpi.org/community/license.php', + 'http://www.netlib.org/lapack/LICENSE.txt', + ], + isOsiApproved: false, + }, + { + reference: './BSD-4-Clause.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/BSD-4-Clause.json', + referenceNumber: '66', + name: 'BSD 4-Clause "Original" or "Old" License', + licenseId: 'BSD-4-Clause', + seeAlso: ['http://directory.fsf.org/wiki/License:BSD_4Clause'], + isOsiApproved: false, + }, + { + reference: './BSD-4-Clause-UC.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-4-Clause-UC.json', + referenceNumber: '361', + name: 'BSD-4-Clause (University of California-Specific)', + licenseId: 'BSD-4-Clause-UC', + seeAlso: ['http://www.freebsd.org/copyright/license.html'], + isOsiApproved: false, + }, + { + reference: './BSD-Protection.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-Protection.json', + referenceNumber: '388', + name: 'BSD Protection License', + licenseId: 'BSD-Protection', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/BSD_Protection_License'], + isOsiApproved: false, + }, + { + reference: './BSD-Source-Code.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BSD-Source-Code.json', + referenceNumber: '163', + name: 'BSD Source Code Attribution', + licenseId: 'BSD-Source-Code', + seeAlso: [ + 'https://github.com/robbiehanson/CocoaHTTPServer/blob/master/LICENSE.txt', + ], + isOsiApproved: false, + }, + { + reference: './BSL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/BSL-1.0.json', + referenceNumber: '284', + name: 'Boost Software License 1.0', + licenseId: 'BSL-1.0', + seeAlso: [ + 'http://www.boost.org/LICENSE_1_0.txt', + 'https://opensource.org/licenses/BSL-1.0', + ], + isOsiApproved: true, + }, + { + reference: './Bahyph.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Bahyph.json', + referenceNumber: '146', + name: 'Bahyph License', + licenseId: 'Bahyph', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Bahyph'], + isOsiApproved: false, + }, + { + reference: './Barr.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Barr.json', + referenceNumber: '123', + name: 'Barr License', + licenseId: 'Barr', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Barr'], + isOsiApproved: false, + }, + { + reference: './Beerware.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Beerware.json', + referenceNumber: '243', + name: 'Beerware License', + licenseId: 'Beerware', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing/Beerware', + 'https://people.freebsd.org/~phk/', + ], + isOsiApproved: false, + }, + { + reference: './BitTorrent-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BitTorrent-1.0.json', + referenceNumber: '200', + name: 'BitTorrent Open Source License v1.0', + licenseId: 'BitTorrent-1.0', + seeAlso: [ + 'http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1=1.1&r2=1.1.1.1&diff_format=s', + ], + isOsiApproved: false, + }, + { + reference: './BitTorrent-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/BitTorrent-1.1.json', + referenceNumber: '181', + name: 'BitTorrent Open Source License v1.1', + licenseId: 'BitTorrent-1.1', + seeAlso: ['http://directory.fsf.org/wiki/License:BitTorrentOSL1.1'], + isOsiApproved: false, + }, + { + reference: './BlueOak-1.0.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/BlueOak-1.0.0.json', + referenceNumber: '204', + name: 'Blue Oak Model License 1.0.0', + licenseId: 'BlueOak-1.0.0', + seeAlso: ['https://blueoakcouncil.org/license/1.0.0'], + isOsiApproved: false, + }, + { + reference: './Borceux.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Borceux.json', + referenceNumber: '294', + name: 'Borceux license', + licenseId: 'Borceux', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Borceux'], + isOsiApproved: false, + }, + { + reference: './CATOSL-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CATOSL-1.1.json', + referenceNumber: '226', + name: 'Computer Associates Trusted Open Source License 1.1', + licenseId: 'CATOSL-1.1', + seeAlso: ['https://opensource.org/licenses/CATOSL-1.1'], + isOsiApproved: true, + }, + { + reference: './CC-BY-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-1.0.json', + referenceNumber: '23', + name: 'Creative Commons Attribution 1.0 Generic', + licenseId: 'CC-BY-1.0', + seeAlso: ['https://creativecommons.org/licenses/by/1.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-2.0.json', + referenceNumber: '58', + name: 'Creative Commons Attribution 2.0 Generic', + licenseId: 'CC-BY-2.0', + seeAlso: ['https://creativecommons.org/licenses/by/2.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-2.5.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-2.5.json', + referenceNumber: '180', + name: 'Creative Commons Attribution 2.5 Generic', + licenseId: 'CC-BY-2.5', + seeAlso: ['https://creativecommons.org/licenses/by/2.5/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-3.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-3.0.json', + referenceNumber: '333', + name: 'Creative Commons Attribution 3.0 Unported', + licenseId: 'CC-BY-3.0', + seeAlso: ['https://creativecommons.org/licenses/by/3.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-4.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/CC-BY-4.0.json', + referenceNumber: '211', + name: 'Creative Commons Attribution 4.0 International', + licenseId: 'CC-BY-4.0', + seeAlso: ['https://creativecommons.org/licenses/by/4.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-1.0.json', + referenceNumber: '223', + name: 'Creative Commons Attribution Non Commercial 1.0 Generic', + licenseId: 'CC-BY-NC-1.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc/1.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-2.0.json', + referenceNumber: '315', + name: 'Creative Commons Attribution Non Commercial 2.0 Generic', + licenseId: 'CC-BY-NC-2.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc/2.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-2.5.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-2.5.json', + referenceNumber: '386', + name: 'Creative Commons Attribution Non Commercial 2.5 Generic', + licenseId: 'CC-BY-NC-2.5', + seeAlso: ['https://creativecommons.org/licenses/by-nc/2.5/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-3.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-3.0.json', + referenceNumber: '325', + name: 'Creative Commons Attribution Non Commercial 3.0 Unported', + licenseId: 'CC-BY-NC-3.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc/3.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-4.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-4.0.json', + referenceNumber: '262', + name: 'Creative Commons Attribution Non Commercial 4.0 International', + licenseId: 'CC-BY-NC-4.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc/4.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-ND-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-ND-1.0.json', + referenceNumber: '98', + name: 'Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic', + licenseId: 'CC-BY-NC-ND-1.0', + seeAlso: ['https://creativecommons.org/licenses/by-nd-nc/1.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-ND-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-ND-2.0.json', + referenceNumber: '133', + name: 'Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic', + licenseId: 'CC-BY-NC-ND-2.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc-nd/2.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-ND-2.5.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-ND-2.5.json', + referenceNumber: '30', + name: 'Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic', + licenseId: 'CC-BY-NC-ND-2.5', + seeAlso: ['https://creativecommons.org/licenses/by-nc-nd/2.5/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-ND-3.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-ND-3.0.json', + referenceNumber: '41', + name: 'Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported', + licenseId: 'CC-BY-NC-ND-3.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc-nd/3.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-ND-4.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-ND-4.0.json', + referenceNumber: '166', + name: 'Creative Commons Attribution Non Commercial No Derivatives 4.0 International', + licenseId: 'CC-BY-NC-ND-4.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-SA-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-SA-1.0.json', + referenceNumber: '116', + name: 'Creative Commons Attribution Non Commercial Share Alike 1.0 Generic', + licenseId: 'CC-BY-NC-SA-1.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc-sa/1.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-SA-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-SA-2.0.json', + referenceNumber: '398', + name: 'Creative Commons Attribution Non Commercial Share Alike 2.0 Generic', + licenseId: 'CC-BY-NC-SA-2.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc-sa/2.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-SA-2.5.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-SA-2.5.json', + referenceNumber: '208', + name: 'Creative Commons Attribution Non Commercial Share Alike 2.5 Generic', + licenseId: 'CC-BY-NC-SA-2.5', + seeAlso: ['https://creativecommons.org/licenses/by-nc-sa/2.5/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-SA-3.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-SA-3.0.json', + referenceNumber: '349', + name: 'Creative Commons Attribution Non Commercial Share Alike 3.0 Unported', + licenseId: 'CC-BY-NC-SA-3.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-NC-SA-4.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-NC-SA-4.0.json', + referenceNumber: '320', + name: 'Creative Commons Attribution Non Commercial Share Alike 4.0 International', + licenseId: 'CC-BY-NC-SA-4.0', + seeAlso: ['https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-ND-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-ND-1.0.json', + referenceNumber: '90', + name: 'Creative Commons Attribution No Derivatives 1.0 Generic', + licenseId: 'CC-BY-ND-1.0', + seeAlso: ['https://creativecommons.org/licenses/by-nd/1.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-ND-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-ND-2.0.json', + referenceNumber: '46', + name: 'Creative Commons Attribution No Derivatives 2.0 Generic', + licenseId: 'CC-BY-ND-2.0', + seeAlso: ['https://creativecommons.org/licenses/by-nd/2.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-ND-2.5.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-ND-2.5.json', + referenceNumber: '27', + name: 'Creative Commons Attribution No Derivatives 2.5 Generic', + licenseId: 'CC-BY-ND-2.5', + seeAlso: ['https://creativecommons.org/licenses/by-nd/2.5/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-ND-3.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-ND-3.0.json', + referenceNumber: '274', + name: 'Creative Commons Attribution No Derivatives 3.0 Unported', + licenseId: 'CC-BY-ND-3.0', + seeAlso: ['https://creativecommons.org/licenses/by-nd/3.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-ND-4.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-ND-4.0.json', + referenceNumber: '307', + name: 'Creative Commons Attribution No Derivatives 4.0 International', + licenseId: 'CC-BY-ND-4.0', + seeAlso: ['https://creativecommons.org/licenses/by-nd/4.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-SA-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-SA-1.0.json', + referenceNumber: '391', + name: 'Creative Commons Attribution Share Alike 1.0 Generic', + licenseId: 'CC-BY-SA-1.0', + seeAlso: ['https://creativecommons.org/licenses/by-sa/1.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-SA-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-SA-2.0.json', + referenceNumber: '356', + name: 'Creative Commons Attribution Share Alike 2.0 Generic', + licenseId: 'CC-BY-SA-2.0', + seeAlso: ['https://creativecommons.org/licenses/by-sa/2.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-SA-2.5.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-SA-2.5.json', + referenceNumber: '196', + name: 'Creative Commons Attribution Share Alike 2.5 Generic', + licenseId: 'CC-BY-SA-2.5', + seeAlso: ['https://creativecommons.org/licenses/by-sa/2.5/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-SA-3.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-BY-SA-3.0.json', + referenceNumber: '225', + name: 'Creative Commons Attribution Share Alike 3.0 Unported', + licenseId: 'CC-BY-SA-3.0', + seeAlso: ['https://creativecommons.org/licenses/by-sa/3.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-BY-SA-4.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/CC-BY-SA-4.0.json', + referenceNumber: '286', + name: 'Creative Commons Attribution Share Alike 4.0 International', + licenseId: 'CC-BY-SA-4.0', + seeAlso: ['https://creativecommons.org/licenses/by-sa/4.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CC-PDDC.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CC-PDDC.json', + referenceNumber: '88', + name: 'Creative Commons Public Domain Dedication and Certification', + licenseId: 'CC-PDDC', + seeAlso: ['https://creativecommons.org/licenses/publicdomain/'], + isOsiApproved: false, + }, + { + reference: './CC0-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/CC0-1.0.json', + referenceNumber: '63', + name: 'Creative Commons Zero v1.0 Universal', + licenseId: 'CC0-1.0', + seeAlso: ['https://creativecommons.org/publicdomain/zero/1.0/legalcode'], + isOsiApproved: false, + }, + { + reference: './CDDL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/CDDL-1.0.json', + referenceNumber: '324', + name: 'Common Development and Distribution License 1.0', + licenseId: 'CDDL-1.0', + seeAlso: ['https://opensource.org/licenses/cddl1'], + isOsiApproved: true, + }, + { + reference: './CDDL-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CDDL-1.1.json', + referenceNumber: '273', + name: 'Common Development and Distribution License 1.1', + licenseId: 'CDDL-1.1', + seeAlso: [ + 'http://glassfish.java.net/public/CDDL+GPL_1_1.html', + 'https://javaee.github.io/glassfish/LICENSE', + ], + isOsiApproved: false, + }, + { + reference: './CDLA-Permissive-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CDLA-Permissive-1.0.json', + referenceNumber: '104', + name: 'Community Data License Agreement Permissive 1.0', + licenseId: 'CDLA-Permissive-1.0', + seeAlso: ['https://cdla.io/permissive-1-0'], + isOsiApproved: false, + }, + { + reference: './CDLA-Sharing-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CDLA-Sharing-1.0.json', + referenceNumber: '172', + name: 'Community Data License Agreement Sharing 1.0', + licenseId: 'CDLA-Sharing-1.0', + seeAlso: ['https://cdla.io/sharing-1-0'], + isOsiApproved: false, + }, + { + reference: './CECILL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CECILL-1.0.json', + referenceNumber: '10', + name: 'CeCILL Free Software License Agreement v1.0', + licenseId: 'CECILL-1.0', + seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL_V1-fr.html'], + isOsiApproved: false, + }, + { + reference: './CECILL-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CECILL-1.1.json', + referenceNumber: '130', + name: 'CeCILL Free Software License Agreement v1.1', + licenseId: 'CECILL-1.1', + seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL_V1.1-US.html'], + isOsiApproved: false, + }, + { + reference: './CECILL-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/CECILL-2.0.json', + referenceNumber: '5', + name: 'CeCILL Free Software License Agreement v2.0', + licenseId: 'CECILL-2.0', + seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL_V2-en.html'], + isOsiApproved: false, + }, + { + reference: './CECILL-2.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CECILL-2.1.json', + referenceNumber: '140', + name: 'CeCILL Free Software License Agreement v2.1', + licenseId: 'CECILL-2.1', + seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html'], + isOsiApproved: true, + }, + { + reference: './CECILL-B.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/CECILL-B.json', + referenceNumber: '89', + name: 'CeCILL-B Free Software License Agreement', + licenseId: 'CECILL-B', + seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html'], + isOsiApproved: false, + }, + { + reference: './CECILL-C.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/CECILL-C.json', + referenceNumber: '232', + name: 'CeCILL-C Free Software License Agreement', + licenseId: 'CECILL-C', + seeAlso: ['http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html'], + isOsiApproved: false, + }, + { + reference: './CERN-OHL-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CERN-OHL-1.1.json', + referenceNumber: '118', + name: 'CERN Open Hardware Licence v1.1', + licenseId: 'CERN-OHL-1.1', + seeAlso: ['https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1'], + isOsiApproved: false, + }, + { + reference: './CERN-OHL-1.2.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CERN-OHL-1.2.json', + referenceNumber: '161', + name: 'CERN Open Hardware Licence v1.2', + licenseId: 'CERN-OHL-1.2', + seeAlso: ['https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2'], + isOsiApproved: false, + }, + { + reference: './CNRI-Jython.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CNRI-Jython.json', + referenceNumber: '74', + name: 'CNRI Jython License', + licenseId: 'CNRI-Jython', + seeAlso: ['http://www.jython.org/license.html'], + isOsiApproved: false, + }, + { + reference: './CNRI-Python.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CNRI-Python.json', + referenceNumber: '92', + name: 'CNRI Python License', + licenseId: 'CNRI-Python', + seeAlso: ['https://opensource.org/licenses/CNRI-Python'], + isOsiApproved: true, + }, + { + reference: './CNRI-Python-GPL-Compatible.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CNRI-Python-GPL-Compatible.json', + referenceNumber: '337', + name: 'CNRI Python Open Source GPL Compatible License Agreement', + licenseId: 'CNRI-Python-GPL-Compatible', + seeAlso: ['http://www.python.org/download/releases/1.6.1/download_win/'], + isOsiApproved: false, + }, + { + reference: './CPAL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/CPAL-1.0.json', + referenceNumber: '275', + name: 'Common Public Attribution License 1.0', + licenseId: 'CPAL-1.0', + seeAlso: ['https://opensource.org/licenses/CPAL-1.0'], + isOsiApproved: true, + }, + { + reference: './CPL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/CPL-1.0.json', + referenceNumber: '228', + name: 'Common Public License 1.0', + licenseId: 'CPL-1.0', + seeAlso: ['https://opensource.org/licenses/CPL-1.0'], + isOsiApproved: true, + }, + { + reference: './CPOL-1.02.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CPOL-1.02.json', + referenceNumber: '222', + name: 'Code Project Open License 1.02', + licenseId: 'CPOL-1.02', + seeAlso: ['http://www.codeproject.com/info/cpol10.aspx'], + isOsiApproved: false, + }, + { + reference: './CUA-OPL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CUA-OPL-1.0.json', + referenceNumber: '159', + name: 'CUA Office Public License v1.0', + licenseId: 'CUA-OPL-1.0', + seeAlso: ['https://opensource.org/licenses/CUA-OPL-1.0'], + isOsiApproved: true, + }, + { + reference: './Caldera.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Caldera.json', + referenceNumber: '242', + name: 'Caldera License', + licenseId: 'Caldera', + seeAlso: ['http://www.lemis.com/grog/UNIX/ancient-source-all.pdf'], + isOsiApproved: false, + }, + { + reference: './ClArtistic.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/ClArtistic.json', + referenceNumber: '237', + name: 'Clarified Artistic License', + licenseId: 'ClArtistic', + seeAlso: [ + 'http://gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/', + 'http://www.ncftp.com/ncftp/doc/LICENSE.txt', + ], + isOsiApproved: false, + }, + { + reference: './Condor-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Condor-1.1.json', + referenceNumber: '145', + name: 'Condor Public License v1.1', + licenseId: 'Condor-1.1', + seeAlso: [ + 'http://research.cs.wisc.edu/condor/license.html#condor', + 'http://web.archive.org/web/20111123062036/http://research.cs.wisc.edu/condor/license.html#condor', + ], + isOsiApproved: false, + }, + { + reference: './Crossword.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Crossword.json', + referenceNumber: '97', + name: 'Crossword License', + licenseId: 'Crossword', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Crossword'], + isOsiApproved: false, + }, + { + reference: './CrystalStacker.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/CrystalStacker.json', + referenceNumber: '38', + name: 'CrystalStacker License', + licenseId: 'CrystalStacker', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing:CrystalStacker?rd=Licensing/CrystalStacker', + ], + isOsiApproved: false, + }, + { + reference: './Cube.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Cube.json', + referenceNumber: '379', + name: 'Cube License', + licenseId: 'Cube', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Cube'], + isOsiApproved: false, + }, + { + reference: './D-FSL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/D-FSL-1.0.json', + referenceNumber: '347', + name: 'Deutsche Freie Software Lizenz', + licenseId: 'D-FSL-1.0', + seeAlso: [ + 'http://www.dipp.nrw.de/d-fsl/lizenzen/', + 'http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/de/D-FSL-1_0_de.txt', + 'http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/en/D-FSL-1_0_en.txt', + 'https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl', + 'https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/deutsche-freie-software-lizenz', + 'https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/german-free-software-license', + 'https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_de.txt/at_download/file', + 'https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_en.txt/at_download/file', + ], + isOsiApproved: false, + }, + { + reference: './DOC.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/DOC.json', + referenceNumber: '265', + name: 'DOC License', + licenseId: 'DOC', + seeAlso: ['http://www.cs.wustl.edu/~schmidt/ACE-copying.html'], + isOsiApproved: false, + }, + { + reference: './DSDP.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/DSDP.json', + referenceNumber: '257', + name: 'DSDP License', + licenseId: 'DSDP', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/DSDP'], + isOsiApproved: false, + }, + { + reference: './Dotseqn.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Dotseqn.json', + referenceNumber: '33', + name: 'Dotseqn License', + licenseId: 'Dotseqn', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Dotseqn'], + isOsiApproved: false, + }, + { + reference: './ECL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/ECL-1.0.json', + referenceNumber: '405', + name: 'Educational Community License v1.0', + licenseId: 'ECL-1.0', + seeAlso: ['https://opensource.org/licenses/ECL-1.0'], + isOsiApproved: true, + }, + { + reference: './ECL-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/ECL-2.0.json', + referenceNumber: '1', + name: 'Educational Community License v2.0', + licenseId: 'ECL-2.0', + seeAlso: ['https://opensource.org/licenses/ECL-2.0'], + isOsiApproved: true, + }, + { + reference: './EFL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/EFL-1.0.json', + referenceNumber: '73', + name: 'Eiffel Forum License v1.0', + licenseId: 'EFL-1.0', + seeAlso: [ + 'http://www.eiffel-nice.org/license/forum.txt', + 'https://opensource.org/licenses/EFL-1.0', + ], + isOsiApproved: true, + }, + { + reference: './EFL-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/EFL-2.0.json', + referenceNumber: '7', + name: 'Eiffel Forum License v2.0', + licenseId: 'EFL-2.0', + seeAlso: [ + 'http://www.eiffel-nice.org/license/eiffel-forum-license-2.html', + 'https://opensource.org/licenses/EFL-2.0', + ], + isOsiApproved: true, + }, + { + reference: './EPL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/EPL-1.0.json', + referenceNumber: '267', + name: 'Eclipse Public License 1.0', + licenseId: 'EPL-1.0', + seeAlso: [ + 'http://www.eclipse.org/legal/epl-v10.html', + 'https://opensource.org/licenses/EPL-1.0', + ], + isOsiApproved: true, + }, + { + reference: './EPL-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/EPL-2.0.json', + referenceNumber: '403', + name: 'Eclipse Public License 2.0', + licenseId: 'EPL-2.0', + seeAlso: [ + 'https://www.eclipse.org/legal/epl-2.0', + 'https://www.opensource.org/licenses/EPL-2.0', + ], + isOsiApproved: true, + }, + { + reference: './EUDatagrid.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/EUDatagrid.json', + referenceNumber: '254', + name: 'EU DataGrid Software License', + licenseId: 'EUDatagrid', + seeAlso: [ + 'http://eu-datagrid.web.cern.ch/eu-datagrid/license.html', + 'https://opensource.org/licenses/EUDatagrid', + ], + isOsiApproved: true, + }, + { + reference: './EUPL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/EUPL-1.0.json', + referenceNumber: '94', + name: 'European Union Public License 1.0', + licenseId: 'EUPL-1.0', + seeAlso: [ + 'http://ec.europa.eu/idabc/en/document/7330.html', + 'http://ec.europa.eu/idabc/servlets/Doc027f.pdf?id=31096', + ], + isOsiApproved: false, + }, + { + reference: './EUPL-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/EUPL-1.1.json', + referenceNumber: '396', + name: 'European Union Public License 1.1', + licenseId: 'EUPL-1.1', + seeAlso: [ + 'https://joinup.ec.europa.eu/software/page/eupl/licence-eupl', + 'https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl1.1.-licence-en_0.pdf', + 'https://opensource.org/licenses/EUPL-1.1', + ], + isOsiApproved: true, + }, + { + reference: './EUPL-1.2.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/EUPL-1.2.json', + referenceNumber: '261', + name: 'European Union Public License 1.2', + licenseId: 'EUPL-1.2', + seeAlso: [ + 'https://joinup.ec.europa.eu/page/eupl-text-11-12', + 'https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf', + 'https://joinup.ec.europa.eu/sites/default/files/inline-files/EUPL%20v1_2%20EN(1).txt', + 'http://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=CELEX:32017D0863', + 'https://opensource.org/licenses/EUPL-1.1', + ], + isOsiApproved: true, + }, + { + reference: './Entessa.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Entessa.json', + referenceNumber: '374', + name: 'Entessa Public License v1.0', + licenseId: 'Entessa', + seeAlso: ['https://opensource.org/licenses/Entessa'], + isOsiApproved: true, + }, + { + reference: './ErlPL-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/ErlPL-1.1.json', + referenceNumber: '395', + name: 'Erlang Public License v1.1', + licenseId: 'ErlPL-1.1', + seeAlso: ['http://www.erlang.org/EPLICENSE'], + isOsiApproved: false, + }, + { + reference: './Eurosym.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Eurosym.json', + referenceNumber: '165', + name: 'Eurosym License', + licenseId: 'Eurosym', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Eurosym'], + isOsiApproved: false, + }, + { + reference: './FSFAP.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/FSFAP.json', + referenceNumber: '382', + name: 'FSF All Permissive License', + licenseId: 'FSFAP', + seeAlso: [ + 'https://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html', + ], + isOsiApproved: false, + }, + { + reference: './FSFUL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/FSFUL.json', + referenceNumber: '2', + name: 'FSF Unlimited License', + licenseId: 'FSFUL', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License'], + isOsiApproved: false, + }, + { + reference: './FSFULLR.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/FSFULLR.json', + referenceNumber: '297', + name: 'FSF Unlimited License (with License Retention)', + licenseId: 'FSFULLR', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License#License_Retention_Variant', + ], + isOsiApproved: false, + }, + { + reference: './FTL.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/FTL.json', + referenceNumber: '363', + name: 'Freetype Project License', + licenseId: 'FTL', + seeAlso: [ + 'http://freetype.fis.uniroma2.it/FTL.TXT', + 'http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT', + ], + isOsiApproved: false, + }, + { + reference: './Fair.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Fair.json', + referenceNumber: '253', + name: 'Fair License', + licenseId: 'Fair', + seeAlso: ['http://fairlicense.org/', 'https://opensource.org/licenses/Fair'], + isOsiApproved: true, + }, + { + reference: './Frameworx-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Frameworx-1.0.json', + referenceNumber: '362', + name: 'Frameworx Open License 1.0', + licenseId: 'Frameworx-1.0', + seeAlso: ['https://opensource.org/licenses/Frameworx-1.0'], + isOsiApproved: true, + }, + { + reference: './FreeImage.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/FreeImage.json', + referenceNumber: '359', + name: 'FreeImage Public License v1.0', + licenseId: 'FreeImage', + seeAlso: ['http://freeimage.sourceforge.net/freeimage-license.txt'], + isOsiApproved: false, + }, + { + reference: './GFDL-1.1.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GFDL-1.1.json', + referenceNumber: '248', + name: 'GNU Free Documentation License v1.1', + licenseId: 'GFDL-1.1', + seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt'], + isOsiApproved: false, + }, + { + reference: './GFDL-1.1-only.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GFDL-1.1-only.json', + referenceNumber: '100', + name: 'GNU Free Documentation License v1.1 only', + licenseId: 'GFDL-1.1-only', + seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt'], + isOsiApproved: false, + }, + { + reference: './GFDL-1.1-or-later.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GFDL-1.1-or-later.json', + referenceNumber: '119', + name: 'GNU Free Documentation License v1.1 or later', + licenseId: 'GFDL-1.1-or-later', + seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt'], + isOsiApproved: false, + }, + { + reference: './GFDL-1.2.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GFDL-1.2.json', + referenceNumber: '190', + name: 'GNU Free Documentation License v1.2', + licenseId: 'GFDL-1.2', + seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt'], + isOsiApproved: false, + }, + { + reference: './GFDL-1.2-only.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GFDL-1.2-only.json', + referenceNumber: '86', + name: 'GNU Free Documentation License v1.2 only', + licenseId: 'GFDL-1.2-only', + seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt'], + isOsiApproved: false, + }, + { + reference: './GFDL-1.2-or-later.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GFDL-1.2-or-later.json', + referenceNumber: '127', + name: 'GNU Free Documentation License v1.2 or later', + licenseId: 'GFDL-1.2-or-later', + seeAlso: ['https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt'], + isOsiApproved: false, + }, + { + reference: './GFDL-1.3.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GFDL-1.3.json', + referenceNumber: '354', + name: 'GNU Free Documentation License v1.3', + licenseId: 'GFDL-1.3', + seeAlso: ['https://www.gnu.org/licenses/fdl-1.3.txt'], + isOsiApproved: false, + }, + { + reference: './GFDL-1.3-only.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GFDL-1.3-only.json', + referenceNumber: '193', + name: 'GNU Free Documentation License v1.3 only', + licenseId: 'GFDL-1.3-only', + seeAlso: ['https://www.gnu.org/licenses/fdl-1.3.txt'], + isOsiApproved: false, + }, + { + reference: './GFDL-1.3-or-later.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GFDL-1.3-or-later.json', + referenceNumber: '51', + name: 'GNU Free Documentation License v1.3 or later', + licenseId: 'GFDL-1.3-or-later', + seeAlso: ['https://www.gnu.org/licenses/fdl-1.3.txt'], + isOsiApproved: false, + }, + { + reference: './GL2PS.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/GL2PS.json', + referenceNumber: '303', + name: 'GL2PS License', + licenseId: 'GL2PS', + seeAlso: ['http://www.geuz.org/gl2ps/COPYING.GL2PS'], + isOsiApproved: false, + }, + { + reference: './GPL-1.0.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/GPL-1.0.json', + referenceNumber: '319', + name: 'GNU General Public License v1.0 only', + licenseId: 'GPL-1.0', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html', + ], + isOsiApproved: false, + }, + { + reference: './GPL-1.0+.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/GPL-1.0+.json', + referenceNumber: '198', + name: 'GNU General Public License v1.0 or later', + licenseId: 'GPL-1.0+', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html', + ], + isOsiApproved: false, + }, + { + reference: './GPL-1.0-only.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/GPL-1.0-only.json', + referenceNumber: '15', + name: 'GNU General Public License v1.0 only', + licenseId: 'GPL-1.0-only', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html', + ], + isOsiApproved: false, + }, + { + reference: './GPL-1.0-or-later.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/GPL-1.0-or-later.json', + referenceNumber: '129', + name: 'GNU General Public License v1.0 or later', + licenseId: 'GPL-1.0-or-later', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html', + ], + isOsiApproved: false, + }, + { + reference: './GPL-2.0.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GPL-2.0.json', + referenceNumber: '345', + name: 'GNU General Public License v2.0 only', + licenseId: 'GPL-2.0', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html', + 'https://opensource.org/licenses/GPL-2.0', + ], + isOsiApproved: true, + }, + { + reference: './GPL-2.0+.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GPL-2.0+.json', + referenceNumber: '389', + name: 'GNU General Public License v2.0 or later', + licenseId: 'GPL-2.0+', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html', + 'https://opensource.org/licenses/GPL-2.0', + ], + isOsiApproved: true, + }, + { + reference: './GPL-2.0-only.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GPL-2.0-only.json', + referenceNumber: '227', + name: 'GNU General Public License v2.0 only', + licenseId: 'GPL-2.0-only', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html', + 'https://opensource.org/licenses/GPL-2.0', + ], + isOsiApproved: true, + }, + { + reference: './GPL-2.0-or-later.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GPL-2.0-or-later.json', + referenceNumber: '249', + name: 'GNU General Public License v2.0 or later', + licenseId: 'GPL-2.0-or-later', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html', + 'https://opensource.org/licenses/GPL-2.0', + ], + isOsiApproved: true, + }, + { + reference: './GPL-2.0-with-GCC-exception.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/GPL-2.0-with-GCC-exception.json', + referenceNumber: '329', + name: 'GNU General Public License v2.0 w/GCC Runtime Library exception', + licenseId: 'GPL-2.0-with-GCC-exception', + seeAlso: [ + 'https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/libgcc1.c;h=762f5143fc6eed57b6797c82710f3538aa52b40b;hb=cb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10', + ], + isOsiApproved: false, + }, + { + reference: './GPL-2.0-with-autoconf-exception.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/GPL-2.0-with-autoconf-exception.json', + referenceNumber: '36', + name: 'GNU General Public License v2.0 w/Autoconf exception', + licenseId: 'GPL-2.0-with-autoconf-exception', + seeAlso: ['http://ac-archive.sourceforge.net/doc/copyright.html'], + isOsiApproved: false, + }, + { + reference: './GPL-2.0-with-bison-exception.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/GPL-2.0-with-bison-exception.json', + referenceNumber: '360', + name: 'GNU General Public License v2.0 w/Bison exception', + licenseId: 'GPL-2.0-with-bison-exception', + seeAlso: [ + 'http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id=193d7c7054ba7197b0789e14965b739162319b5e#n141', + ], + isOsiApproved: false, + }, + { + reference: './GPL-2.0-with-classpath-exception.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/GPL-2.0-with-classpath-exception.json', + referenceNumber: '219', + name: 'GNU General Public License v2.0 w/Classpath exception', + licenseId: 'GPL-2.0-with-classpath-exception', + seeAlso: ['https://www.gnu.org/software/classpath/license.html'], + isOsiApproved: false, + }, + { + reference: './GPL-2.0-with-font-exception.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/GPL-2.0-with-font-exception.json', + referenceNumber: '24', + name: 'GNU General Public License v2.0 w/Font exception', + licenseId: 'GPL-2.0-with-font-exception', + seeAlso: ['https://www.gnu.org/licenses/gpl-faq.html#FontException'], + isOsiApproved: false, + }, + { + reference: './GPL-3.0.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GPL-3.0.json', + referenceNumber: '401', + name: 'GNU General Public License v3.0 only', + licenseId: 'GPL-3.0', + seeAlso: [ + 'https://www.gnu.org/licenses/gpl-3.0-standalone.html', + 'https://opensource.org/licenses/GPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './GPL-3.0+.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GPL-3.0+.json', + referenceNumber: '147', + name: 'GNU General Public License v3.0 or later', + licenseId: 'GPL-3.0+', + seeAlso: [ + 'https://www.gnu.org/licenses/gpl-3.0-standalone.html', + 'https://opensource.org/licenses/GPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './GPL-3.0-only.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GPL-3.0-only.json', + referenceNumber: '122', + name: 'GNU General Public License v3.0 only', + licenseId: 'GPL-3.0-only', + seeAlso: [ + 'https://www.gnu.org/licenses/gpl-3.0-standalone.html', + 'https://opensource.org/licenses/GPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './GPL-3.0-or-later.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/GPL-3.0-or-later.json', + referenceNumber: '387', + name: 'GNU General Public License v3.0 or later', + licenseId: 'GPL-3.0-or-later', + seeAlso: [ + 'https://www.gnu.org/licenses/gpl-3.0-standalone.html', + 'https://opensource.org/licenses/GPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './GPL-3.0-with-GCC-exception.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/GPL-3.0-with-GCC-exception.json', + referenceNumber: '6', + name: 'GNU General Public License v3.0 w/GCC Runtime Library exception', + licenseId: 'GPL-3.0-with-GCC-exception', + seeAlso: ['https://www.gnu.org/licenses/gcc-exception-3.1.html'], + isOsiApproved: true, + }, + { + reference: './GPL-3.0-with-autoconf-exception.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/GPL-3.0-with-autoconf-exception.json', + referenceNumber: '8', + name: 'GNU General Public License v3.0 w/Autoconf exception', + licenseId: 'GPL-3.0-with-autoconf-exception', + seeAlso: ['https://www.gnu.org/licenses/autoconf-exception-3.0.html'], + isOsiApproved: false, + }, + { + reference: './Giftware.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Giftware.json', + referenceNumber: '367', + name: 'Giftware License', + licenseId: 'Giftware', + seeAlso: ['http://liballeg.org/license.html#allegro-4-the-giftware-license'], + isOsiApproved: false, + }, + { + reference: './Glide.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Glide.json', + referenceNumber: '117', + name: '3dfx Glide License', + licenseId: 'Glide', + seeAlso: ['http://www.users.on.net/~triforce/glidexp/COPYING.txt'], + isOsiApproved: false, + }, + { + reference: './Glulxe.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Glulxe.json', + referenceNumber: '199', + name: 'Glulxe License', + licenseId: 'Glulxe', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Glulxe'], + isOsiApproved: false, + }, + { + reference: './HPND.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/HPND.json', + referenceNumber: '148', + name: 'Historical Permission Notice and Disclaimer', + licenseId: 'HPND', + seeAlso: ['https://opensource.org/licenses/HPND'], + isOsiApproved: true, + }, + { + reference: './HPND-sell-variant.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/HPND-sell-variant.json', + referenceNumber: '158', + name: 'Historical Permission Notice and Disclaimer - sell variant', + licenseId: 'HPND-sell-variant', + seeAlso: [ + 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h=v4.19', + ], + isOsiApproved: false, + }, + { + reference: './HaskellReport.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/HaskellReport.json', + referenceNumber: '205', + name: 'Haskell Language Report License', + licenseId: 'HaskellReport', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing/Haskell_Language_Report_License', + ], + isOsiApproved: false, + }, + { + reference: './IBM-pibs.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/IBM-pibs.json', + referenceNumber: '233', + name: 'IBM PowerPC Initialization and Boot Software', + licenseId: 'IBM-pibs', + seeAlso: [ + 'http://git.denx.de/?p=u-boot.git;a=blob;f=arch/powerpc/cpu/ppc4xx/miiphy.c;h=297155fdafa064b955e53e9832de93bfb0cfb85b;hb=9fab4bf4cc077c21e43941866f3f2c196f28670d', + ], + isOsiApproved: false, + }, + { + reference: './ICU.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/ICU.json', + referenceNumber: '174', + name: 'ICU License', + licenseId: 'ICU', + seeAlso: ['http://source.icu-project.org/repos/icu/icu/trunk/license.html'], + isOsiApproved: false, + }, + { + reference: './IJG.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/IJG.json', + referenceNumber: '236', + name: 'Independent JPEG Group License', + licenseId: 'IJG', + seeAlso: ['http://dev.w3.org/cvsweb/Amaya/libjpeg/Attic/README?rev=1.2'], + isOsiApproved: false, + }, + { + reference: './IPA.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/IPA.json', + referenceNumber: '310', + name: 'IPA Font License', + licenseId: 'IPA', + seeAlso: ['https://opensource.org/licenses/IPA'], + isOsiApproved: true, + }, + { + reference: './IPL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/IPL-1.0.json', + referenceNumber: '309', + name: 'IBM Public License v1.0', + licenseId: 'IPL-1.0', + seeAlso: ['https://opensource.org/licenses/IPL-1.0'], + isOsiApproved: true, + }, + { + reference: './ISC.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/ISC.json', + referenceNumber: '353', + name: 'ISC License', + licenseId: 'ISC', + seeAlso: [ + 'https://www.isc.org/downloads/software-support-policy/isc-license/', + 'https://opensource.org/licenses/ISC', + ], + isOsiApproved: true, + }, + { + reference: './ImageMagick.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/ImageMagick.json', + referenceNumber: '326', + name: 'ImageMagick License', + licenseId: 'ImageMagick', + seeAlso: ['http://www.imagemagick.org/script/license.php'], + isOsiApproved: false, + }, + { + reference: './Imlib2.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Imlib2.json', + referenceNumber: '135', + name: 'Imlib2 License', + licenseId: 'Imlib2', + seeAlso: [ + 'http://trac.enlightenment.org/e/browser/trunk/imlib2/COPYING', + 'https://git.enlightenment.org/legacy/imlib2.git/tree/COPYING', + ], + isOsiApproved: false, + }, + { + reference: './Info-ZIP.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Info-ZIP.json', + referenceNumber: '283', + name: 'Info-ZIP License', + licenseId: 'Info-ZIP', + seeAlso: ['http://www.info-zip.org/license.html'], + isOsiApproved: false, + }, + { + reference: './Intel.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Intel.json', + referenceNumber: '29', + name: 'Intel Open Source License', + licenseId: 'Intel', + seeAlso: ['https://opensource.org/licenses/Intel'], + isOsiApproved: true, + }, + { + reference: './Intel-ACPI.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Intel-ACPI.json', + referenceNumber: '235', + name: 'Intel ACPI Software License Agreement', + licenseId: 'Intel-ACPI', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing/Intel_ACPI_Software_License_Agreement', + ], + isOsiApproved: false, + }, + { + reference: './Interbase-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Interbase-1.0.json', + referenceNumber: '323', + name: 'Interbase Public License v1.0', + licenseId: 'Interbase-1.0', + seeAlso: [ + 'https://web.archive.org/web/20060319014854/http://info.borland.com/devsupport/interbase/opensource/IPL.html', + ], + isOsiApproved: false, + }, + { + reference: './JPNIC.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/JPNIC.json', + referenceNumber: '316', + name: 'Japan Network Information Center License', + licenseId: 'JPNIC', + seeAlso: [ + 'https://gitlab.isc.org/isc-projects/bind9/blob/master/COPYRIGHT#L366', + ], + isOsiApproved: false, + }, + { + reference: './JSON.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/JSON.json', + referenceNumber: '197', + name: 'JSON License', + licenseId: 'JSON', + seeAlso: ['http://www.json.org/license.html'], + isOsiApproved: false, + }, + { + reference: './JasPer-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/JasPer-2.0.json', + referenceNumber: '77', + name: 'JasPer License', + licenseId: 'JasPer-2.0', + seeAlso: ['http://www.ece.uvic.ca/~mdadams/jasper/LICENSE'], + isOsiApproved: false, + }, + { + reference: './LAL-1.2.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LAL-1.2.json', + referenceNumber: '153', + name: 'Licence Art Libre 1.2', + licenseId: 'LAL-1.2', + seeAlso: ['http://artlibre.org/licence/lal/licence-art-libre-12/'], + isOsiApproved: false, + }, + { + reference: './LAL-1.3.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LAL-1.3.json', + referenceNumber: '357', + name: 'Licence Art Libre 1.3', + licenseId: 'LAL-1.3', + seeAlso: ['https://artlibre.org/'], + isOsiApproved: false, + }, + { + reference: './LGPL-2.0.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/LGPL-2.0.json', + referenceNumber: '276', + name: 'GNU Library General Public License v2 only', + licenseId: 'LGPL-2.0', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-2.0+.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/LGPL-2.0+.json', + referenceNumber: '139', + name: 'GNU Library General Public License v2 or later', + licenseId: 'LGPL-2.0+', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-2.0-only.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LGPL-2.0-only.json', + referenceNumber: '328', + name: 'GNU Library General Public License v2 only', + licenseId: 'LGPL-2.0-only', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-2.0-or-later.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LGPL-2.0-or-later.json', + referenceNumber: '35', + name: 'GNU Library General Public License v2 or later', + licenseId: 'LGPL-2.0-or-later', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-2.1.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LGPL-2.1.json', + referenceNumber: '182', + name: 'GNU Lesser General Public License v2.1 only', + licenseId: 'LGPL-2.1', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html', + 'https://opensource.org/licenses/LGPL-2.1', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-2.1+.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LGPL-2.1+.json', + referenceNumber: '202', + name: 'GNU Library General Public License v2.1 or later', + licenseId: 'LGPL-2.1+', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html', + 'https://opensource.org/licenses/LGPL-2.1', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-2.1-only.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LGPL-2.1-only.json', + referenceNumber: '138', + name: 'GNU Lesser General Public License v2.1 only', + licenseId: 'LGPL-2.1-only', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html', + 'https://opensource.org/licenses/LGPL-2.1', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-2.1-or-later.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LGPL-2.1-or-later.json', + referenceNumber: '269', + name: 'GNU Lesser General Public License v2.1 or later', + licenseId: 'LGPL-2.1-or-later', + seeAlso: [ + 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html', + 'https://opensource.org/licenses/LGPL-2.1', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-3.0.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LGPL-3.0.json', + referenceNumber: '209', + name: 'GNU Lesser General Public License v3.0 only', + licenseId: 'LGPL-3.0', + seeAlso: [ + 'https://www.gnu.org/licenses/lgpl-3.0-standalone.html', + 'https://opensource.org/licenses/LGPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-3.0+.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LGPL-3.0+.json', + referenceNumber: '220', + name: 'GNU Lesser General Public License v3.0 or later', + licenseId: 'LGPL-3.0+', + seeAlso: [ + 'https://www.gnu.org/licenses/lgpl-3.0-standalone.html', + 'https://opensource.org/licenses/LGPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-3.0-only.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LGPL-3.0-only.json', + referenceNumber: '47', + name: 'GNU Lesser General Public License v3.0 only', + licenseId: 'LGPL-3.0-only', + seeAlso: [ + 'https://www.gnu.org/licenses/lgpl-3.0-standalone.html', + 'https://opensource.org/licenses/LGPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './LGPL-3.0-or-later.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LGPL-3.0-or-later.json', + referenceNumber: '317', + name: 'GNU Lesser General Public License v3.0 or later', + licenseId: 'LGPL-3.0-or-later', + seeAlso: [ + 'https://www.gnu.org/licenses/lgpl-3.0-standalone.html', + 'https://opensource.org/licenses/LGPL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './LGPLLR.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LGPLLR.json', + referenceNumber: '404', + name: 'Lesser General Public License For Linguistic Resources', + licenseId: 'LGPLLR', + seeAlso: ['http://www-igm.univ-mlv.fr/~unitex/lgpllr.html'], + isOsiApproved: false, + }, + { + reference: './LPL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LPL-1.0.json', + referenceNumber: '371', + name: 'Lucent Public License Version 1.0', + licenseId: 'LPL-1.0', + seeAlso: ['https://opensource.org/licenses/LPL-1.0'], + isOsiApproved: true, + }, + { + reference: './LPL-1.02.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LPL-1.02.json', + referenceNumber: '120', + name: 'Lucent Public License v1.02', + licenseId: 'LPL-1.02', + seeAlso: [ + 'http://plan9.bell-labs.com/plan9/license.html', + 'https://opensource.org/licenses/LPL-1.02', + ], + isOsiApproved: true, + }, + { + reference: './LPPL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LPPL-1.0.json', + referenceNumber: '82', + name: 'LaTeX Project Public License v1.0', + licenseId: 'LPPL-1.0', + seeAlso: ['http://www.latex-project.org/lppl/lppl-1-0.txt'], + isOsiApproved: false, + }, + { + reference: './LPPL-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LPPL-1.1.json', + referenceNumber: '168', + name: 'LaTeX Project Public License v1.1', + licenseId: 'LPPL-1.1', + seeAlso: ['http://www.latex-project.org/lppl/lppl-1-1.txt'], + isOsiApproved: false, + }, + { + reference: './LPPL-1.2.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LPPL-1.2.json', + referenceNumber: '160', + name: 'LaTeX Project Public License v1.2', + licenseId: 'LPPL-1.2', + seeAlso: ['http://www.latex-project.org/lppl/lppl-1-2.txt'], + isOsiApproved: false, + }, + { + reference: './LPPL-1.3a.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/LPPL-1.3a.json', + referenceNumber: '270', + name: 'LaTeX Project Public License v1.3a', + licenseId: 'LPPL-1.3a', + seeAlso: ['http://www.latex-project.org/lppl/lppl-1-3a.txt'], + isOsiApproved: false, + }, + { + reference: './LPPL-1.3c.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LPPL-1.3c.json', + referenceNumber: '126', + name: 'LaTeX Project Public License v1.3c', + licenseId: 'LPPL-1.3c', + seeAlso: [ + 'http://www.latex-project.org/lppl/lppl-1-3c.txt', + 'https://opensource.org/licenses/LPPL-1.3c', + ], + isOsiApproved: true, + }, + { + reference: './Latex2e.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Latex2e.json', + referenceNumber: '37', + name: 'Latex2e License', + licenseId: 'Latex2e', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Latex2e'], + isOsiApproved: false, + }, + { + reference: './Leptonica.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Leptonica.json', + referenceNumber: '300', + name: 'Leptonica License', + licenseId: 'Leptonica', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Leptonica'], + isOsiApproved: false, + }, + { + reference: './LiLiQ-P-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LiLiQ-P-1.1.json', + referenceNumber: '79', + name: 'Licence Libre du Qu\xe9bec \u2013 Permissive version 1.1', + licenseId: 'LiLiQ-P-1.1', + seeAlso: [ + 'https://forge.gouv.qc.ca/licence/fr/liliq-v1-1/', + 'http://opensource.org/licenses/LiLiQ-P-1.1', + ], + isOsiApproved: true, + }, + { + reference: './LiLiQ-R-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LiLiQ-R-1.1.json', + referenceNumber: '290', + name: 'Licence Libre du Qu\xe9bec \u2013 R\xe9ciprocit\xe9 version 1.1', + licenseId: 'LiLiQ-R-1.1', + seeAlso: [ + 'https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-liliq-r-v1-1/', + 'http://opensource.org/licenses/LiLiQ-R-1.1', + ], + isOsiApproved: true, + }, + { + reference: './LiLiQ-Rplus-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/LiLiQ-Rplus-1.1.json', + referenceNumber: '335', + name: 'Licence Libre du Qu\xe9bec \u2013 R\xe9ciprocit\xe9 forte version 1.1', + licenseId: 'LiLiQ-Rplus-1.1', + seeAlso: [ + 'https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-forte-liliq-r-v1-1/', + 'http://opensource.org/licenses/LiLiQ-Rplus-1.1', + ], + isOsiApproved: true, + }, + { + reference: './Libpng.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Libpng.json', + referenceNumber: '376', + name: 'libpng License', + licenseId: 'Libpng', + seeAlso: ['http://www.libpng.org/pub/png/src/libpng-LICENSE.txt'], + isOsiApproved: false, + }, + { + reference: './Linux-OpenIB.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Linux-OpenIB.json', + referenceNumber: '217', + name: 'Linux Kernel Variant of OpenIB.org license', + licenseId: 'Linux-OpenIB', + seeAlso: [ + 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h', + ], + isOsiApproved: false, + }, + { + reference: './MIT.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/MIT.json', + referenceNumber: '256', + name: 'MIT License', + licenseId: 'MIT', + seeAlso: ['https://opensource.org/licenses/MIT'], + isOsiApproved: true, + }, + { + reference: './MIT-0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MIT-0.json', + referenceNumber: '76', + name: 'MIT No Attribution', + licenseId: 'MIT-0', + seeAlso: [ + 'https://github.com/aws/mit-0', + 'https://romanrm.net/mit-zero', + 'https://github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE', + ], + isOsiApproved: false, + }, + { + reference: './MIT-CMU.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MIT-CMU.json', + referenceNumber: '344', + name: 'CMU License', + licenseId: 'MIT-CMU', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing:MIT?rd=Licensing/MIT#CMU_Style', + 'https://github.com/python-pillow/Pillow/blob/fffb426092c8db24a5f4b6df243a8a3c01fb63cd/LICENSE', + ], + isOsiApproved: false, + }, + { + reference: './MIT-advertising.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MIT-advertising.json', + referenceNumber: '192', + name: 'Enlightenment License (e16)', + licenseId: 'MIT-advertising', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/MIT_With_Advertising'], + isOsiApproved: false, + }, + { + reference: './MIT-enna.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MIT-enna.json', + referenceNumber: '52', + name: 'enna License', + licenseId: 'MIT-enna', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/MIT#enna'], + isOsiApproved: false, + }, + { + reference: './MIT-feh.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MIT-feh.json', + referenceNumber: '365', + name: 'feh License', + licenseId: 'MIT-feh', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/MIT#feh'], + isOsiApproved: false, + }, + { + reference: './MITNFA.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MITNFA.json', + referenceNumber: '336', + name: 'MIT +no-false-attribs license', + licenseId: 'MITNFA', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/MITNFA'], + isOsiApproved: false, + }, + { + reference: './MPL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MPL-1.0.json', + referenceNumber: '239', + name: 'Mozilla Public License 1.0', + licenseId: 'MPL-1.0', + seeAlso: [ + 'http://www.mozilla.org/MPL/MPL-1.0.html', + 'https://opensource.org/licenses/MPL-1.0', + ], + isOsiApproved: true, + }, + { + reference: './MPL-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/MPL-1.1.json', + referenceNumber: '394', + name: 'Mozilla Public License 1.1', + licenseId: 'MPL-1.1', + seeAlso: [ + 'http://www.mozilla.org/MPL/MPL-1.1.html', + 'https://opensource.org/licenses/MPL-1.1', + ], + isOsiApproved: true, + }, + { + reference: './MPL-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/MPL-2.0.json', + referenceNumber: '121', + name: 'Mozilla Public License 2.0', + licenseId: 'MPL-2.0', + seeAlso: [ + 'http://www.mozilla.org/MPL/2.0/', + 'https://opensource.org/licenses/MPL-2.0', + ], + isOsiApproved: true, + }, + { + reference: './MPL-2.0-no-copyleft-exception.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MPL-2.0-no-copyleft-exception.json', + referenceNumber: '183', + name: 'Mozilla Public License 2.0 (no copyleft exception)', + licenseId: 'MPL-2.0-no-copyleft-exception', + seeAlso: [ + 'http://www.mozilla.org/MPL/2.0/', + 'https://opensource.org/licenses/MPL-2.0', + ], + isOsiApproved: true, + }, + { + reference: './MS-PL.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/MS-PL.json', + referenceNumber: '366', + name: 'Microsoft Public License', + licenseId: 'MS-PL', + seeAlso: [ + 'http://www.microsoft.com/opensource/licenses.mspx', + 'https://opensource.org/licenses/MS-PL', + ], + isOsiApproved: true, + }, + { + reference: './MS-RL.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/MS-RL.json', + referenceNumber: '4', + name: 'Microsoft Reciprocal License', + licenseId: 'MS-RL', + seeAlso: [ + 'http://www.microsoft.com/opensource/licenses.mspx', + 'https://opensource.org/licenses/MS-RL', + ], + isOsiApproved: true, + }, + { + reference: './MTLL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MTLL.json', + referenceNumber: '102', + name: 'Matrix Template Library License', + licenseId: 'MTLL', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing/Matrix_Template_Library_License', + ], + isOsiApproved: false, + }, + { + reference: './MakeIndex.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MakeIndex.json', + referenceNumber: '343', + name: 'MakeIndex License', + licenseId: 'MakeIndex', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/MakeIndex'], + isOsiApproved: false, + }, + { + reference: './MirOS.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MirOS.json', + referenceNumber: '369', + name: 'The MirOS Licence', + licenseId: 'MirOS', + seeAlso: ['https://opensource.org/licenses/MirOS'], + isOsiApproved: true, + }, + { + reference: './Motosoto.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Motosoto.json', + referenceNumber: '12', + name: 'Motosoto License', + licenseId: 'Motosoto', + seeAlso: ['https://opensource.org/licenses/Motosoto'], + isOsiApproved: true, + }, + { + reference: './MulanPSL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/MulanPSL-1.0.json', + referenceNumber: '201', + name: 'Mulan Permissive Software License, Version 1', + licenseId: 'MulanPSL-1.0', + seeAlso: [ + 'https://license.coscl.org.cn/MulanPSL/', + 'https://github.com/yuwenlong/longphp/blob/25dfb70cc2a466dc4bb55ba30901cbce08d164b5/LICENSE', + ], + isOsiApproved: false, + }, + { + reference: './Multics.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Multics.json', + referenceNumber: '164', + name: 'Multics License', + licenseId: 'Multics', + seeAlso: ['https://opensource.org/licenses/Multics'], + isOsiApproved: true, + }, + { + reference: './Mup.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Mup.json', + referenceNumber: '305', + name: 'Mup License', + licenseId: 'Mup', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Mup'], + isOsiApproved: false, + }, + { + reference: './NASA-1.3.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/NASA-1.3.json', + referenceNumber: '110', + name: 'NASA Open Source Agreement 1.3', + licenseId: 'NASA-1.3', + seeAlso: [ + 'http://ti.arc.nasa.gov/opensource/nosa/', + 'https://opensource.org/licenses/NASA-1.3', + ], + isOsiApproved: true, + }, + { + reference: './NBPL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/NBPL-1.0.json', + referenceNumber: '17', + name: 'Net Boolean Public License v1', + licenseId: 'NBPL-1.0', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=37b4b3f6cc4bf34e1d3dec61e69914b9819d8894', + ], + isOsiApproved: false, + }, + { + reference: './NCSA.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/NCSA.json', + referenceNumber: '187', + name: 'University of Illinois/NCSA Open Source License', + licenseId: 'NCSA', + seeAlso: [ + 'http://otm.illinois.edu/uiuc_openSource', + 'https://opensource.org/licenses/NCSA', + ], + isOsiApproved: true, + }, + { + reference: './NGPL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/NGPL.json', + referenceNumber: '308', + name: 'Nethack General Public License', + licenseId: 'NGPL', + seeAlso: ['https://opensource.org/licenses/NGPL'], + isOsiApproved: true, + }, + { + reference: './NLOD-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/NLOD-1.0.json', + referenceNumber: '134', + name: 'Norwegian Licence for Open Government Data', + licenseId: 'NLOD-1.0', + seeAlso: ['http://data.norge.no/nlod/en/1.0'], + isOsiApproved: false, + }, + { + reference: './NLPL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/NLPL.json', + referenceNumber: '306', + name: 'No Limit Public License', + licenseId: 'NLPL', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/NLPL'], + isOsiApproved: false, + }, + { + reference: './NOSL.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/NOSL.json', + referenceNumber: '380', + name: 'Netizen Open Source License', + licenseId: 'NOSL', + seeAlso: ['http://bits.netizen.com.au/licenses/NOSL/nosl.txt'], + isOsiApproved: false, + }, + { + reference: './NPL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/NPL-1.0.json', + referenceNumber: '245', + name: 'Netscape Public License v1.0', + licenseId: 'NPL-1.0', + seeAlso: ['http://www.mozilla.org/MPL/NPL/1.0/'], + isOsiApproved: false, + }, + { + reference: './NPL-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/NPL-1.1.json', + referenceNumber: '409', + name: 'Netscape Public License v1.1', + licenseId: 'NPL-1.1', + seeAlso: ['http://www.mozilla.org/MPL/NPL/1.1/'], + isOsiApproved: false, + }, + { + reference: './NPOSL-3.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/NPOSL-3.0.json', + referenceNumber: '150', + name: 'Non-Profit Open Software License 3.0', + licenseId: 'NPOSL-3.0', + seeAlso: ['https://opensource.org/licenses/NOSL3.0'], + isOsiApproved: true, + }, + { + reference: './NRL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/NRL.json', + referenceNumber: '101', + name: 'NRL License', + licenseId: 'NRL', + seeAlso: ['http://web.mit.edu/network/isakmp/nrllicense.html'], + isOsiApproved: false, + }, + { + reference: './NTP.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/NTP.json', + referenceNumber: '258', + name: 'NTP License', + licenseId: 'NTP', + seeAlso: ['https://opensource.org/licenses/NTP'], + isOsiApproved: true, + }, + { + reference: './NTP-0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/NTP-0.json', + referenceNumber: '185', + name: 'NTP No Attribution', + licenseId: 'NTP-0', + seeAlso: ['https://github.com/tytso/e2fsprogs/blob/master/lib/et/et_name.c'], + isOsiApproved: false, + }, + { + reference: './Naumen.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Naumen.json', + referenceNumber: '282', + name: 'Naumen Public License', + licenseId: 'Naumen', + seeAlso: ['https://opensource.org/licenses/Naumen'], + isOsiApproved: true, + }, + { + reference: './Net-SNMP.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Net-SNMP.json', + referenceNumber: '278', + name: 'Net-SNMP License', + licenseId: 'Net-SNMP', + seeAlso: ['http://net-snmp.sourceforge.net/about/license.html'], + isOsiApproved: false, + }, + { + reference: './NetCDF.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/NetCDF.json', + referenceNumber: '212', + name: 'NetCDF license', + licenseId: 'NetCDF', + seeAlso: ['http://www.unidata.ucar.edu/software/netcdf/copyright.html'], + isOsiApproved: false, + }, + { + reference: './Newsletr.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Newsletr.json', + referenceNumber: '358', + name: 'Newsletr License', + licenseId: 'Newsletr', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Newsletr'], + isOsiApproved: false, + }, + { + reference: './Nokia.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Nokia.json', + referenceNumber: '125', + name: 'Nokia Open Source License', + licenseId: 'Nokia', + seeAlso: ['https://opensource.org/licenses/nokia'], + isOsiApproved: true, + }, + { + reference: './Noweb.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Noweb.json', + referenceNumber: '70', + name: 'Noweb License', + licenseId: 'Noweb', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Noweb'], + isOsiApproved: false, + }, + { + reference: './Nunit.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Nunit.json', + referenceNumber: '87', + name: 'Nunit License', + licenseId: 'Nunit', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Nunit'], + isOsiApproved: false, + }, + { + reference: './OCCT-PL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OCCT-PL.json', + referenceNumber: '65', + name: 'Open CASCADE Technology Public License', + licenseId: 'OCCT-PL', + seeAlso: ['http://www.opencascade.com/content/occt-public-license'], + isOsiApproved: false, + }, + { + reference: './OCLC-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OCLC-2.0.json', + referenceNumber: '341', + name: 'OCLC Research Public License 2.0', + licenseId: 'OCLC-2.0', + seeAlso: [ + 'http://www.oclc.org/research/activities/software/license/v2final.htm', + 'https://opensource.org/licenses/OCLC-2.0', + ], + isOsiApproved: true, + }, + { + reference: './ODC-By-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/ODC-By-1.0.json', + referenceNumber: '381', + name: 'Open Data Commons Attribution License v1.0', + licenseId: 'ODC-By-1.0', + seeAlso: ['https://opendatacommons.org/licenses/by/1.0/'], + isOsiApproved: false, + }, + { + reference: './ODbL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/ODbL-1.0.json', + referenceNumber: '340', + name: 'ODC Open Database License v1.0', + licenseId: 'ODbL-1.0', + seeAlso: ['http://www.opendatacommons.org/licenses/odbl/1.0/'], + isOsiApproved: false, + }, + { + reference: './OFL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/OFL-1.0.json', + referenceNumber: '80', + name: 'SIL Open Font License 1.0', + licenseId: 'OFL-1.0', + seeAlso: ['http://scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web'], + isOsiApproved: false, + }, + { + reference: './OFL-1.0-RFN.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OFL-1.0-RFN.json', + referenceNumber: '298', + name: 'SIL Open Font License 1.0 with Reserved Font Name', + licenseId: 'OFL-1.0-RFN', + seeAlso: ['http://scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web'], + isOsiApproved: false, + }, + { + reference: './OFL-1.0-no-RFN.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OFL-1.0-no-RFN.json', + referenceNumber: '71', + name: 'SIL Open Font License 1.0 with no Reserved Font Name', + licenseId: 'OFL-1.0-no-RFN', + seeAlso: ['http://scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web'], + isOsiApproved: false, + }, + { + reference: './OFL-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/OFL-1.1.json', + referenceNumber: '311', + name: 'SIL Open Font License 1.1', + licenseId: 'OFL-1.1', + seeAlso: [ + 'http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web', + 'https://opensource.org/licenses/OFL-1.1', + ], + isOsiApproved: true, + }, + { + reference: './OFL-1.1-RFN.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OFL-1.1-RFN.json', + referenceNumber: '43', + name: 'SIL Open Font License 1.1 with Reserved Font Name', + licenseId: 'OFL-1.1-RFN', + seeAlso: [ + 'http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web', + 'https://opensource.org/licenses/OFL-1.1', + ], + isOsiApproved: true, + }, + { + reference: './OFL-1.1-no-RFN.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OFL-1.1-no-RFN.json', + referenceNumber: '238', + name: 'SIL Open Font License 1.1 with no Reserved Font Name', + licenseId: 'OFL-1.1-no-RFN', + seeAlso: [ + 'http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web', + 'https://opensource.org/licenses/OFL-1.1', + ], + isOsiApproved: true, + }, + { + reference: './OGL-Canada-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OGL-Canada-2.0.json', + referenceNumber: '346', + name: 'Open Government Licence - Canada', + licenseId: 'OGL-Canada-2.0', + seeAlso: ['https://open.canada.ca/en/open-government-licence-canada'], + isOsiApproved: false, + }, + { + reference: './OGL-UK-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OGL-UK-1.0.json', + referenceNumber: '348', + name: 'Open Government Licence v1.0', + licenseId: 'OGL-UK-1.0', + seeAlso: [ + 'http://www.nationalarchives.gov.uk/doc/open-government-licence/version/1/', + ], + isOsiApproved: false, + }, + { + reference: './OGL-UK-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OGL-UK-2.0.json', + referenceNumber: '13', + name: 'Open Government Licence v2.0', + licenseId: 'OGL-UK-2.0', + seeAlso: [ + 'http://www.nationalarchives.gov.uk/doc/open-government-licence/version/2/', + ], + isOsiApproved: false, + }, + { + reference: './OGL-UK-3.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OGL-UK-3.0.json', + referenceNumber: '21', + name: 'Open Government Licence v3.0', + licenseId: 'OGL-UK-3.0', + seeAlso: [ + 'http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/', + ], + isOsiApproved: false, + }, + { + reference: './OGTSL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OGTSL.json', + referenceNumber: '26', + name: 'Open Group Test Suite License', + licenseId: 'OGTSL', + seeAlso: [ + 'http://www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt', + 'https://opensource.org/licenses/OGTSL', + ], + isOsiApproved: true, + }, + { + reference: './OLDAP-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-1.1.json', + referenceNumber: '55', + name: 'Open LDAP Public License v1.1', + licenseId: 'OLDAP-1.1', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=806557a5ad59804ef3a44d5abfbe91d706b0791f', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-1.2.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-1.2.json', + referenceNumber: '48', + name: 'Open LDAP Public License v1.2', + licenseId: 'OLDAP-1.2', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=42b0383c50c299977b5893ee695cf4e486fb0dc7', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-1.3.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-1.3.json', + referenceNumber: '42', + name: 'Open LDAP Public License v1.3', + licenseId: 'OLDAP-1.3', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=e5f8117f0ce088d0bd7a8e18ddf37eaa40eb09b1', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-1.4.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-1.4.json', + referenceNumber: '50', + name: 'Open LDAP Public License v1.4', + licenseId: 'OLDAP-1.4', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=c9f95c2f3f2ffb5e0ae55fe7388af75547660941', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.0.json', + referenceNumber: '25', + name: 'Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)', + licenseId: 'OLDAP-2.0', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=cbf50f4e1185a21abd4c0a54d3f4341fe28f36ea', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.0.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.0.1.json', + referenceNumber: '280', + name: 'Open LDAP Public License v2.0.1', + licenseId: 'OLDAP-2.0.1', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=b6d68acd14e51ca3aab4428bf26522aa74873f0e', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.1.json', + referenceNumber: '400', + name: 'Open LDAP Public License v2.1', + licenseId: 'OLDAP-2.1', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=b0d176738e96a0d3b9f85cb51e140a86f21be715', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.2.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.2.json', + referenceNumber: '318', + name: 'Open LDAP Public License v2.2', + licenseId: 'OLDAP-2.2', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=470b0c18ec67621c85881b2733057fecf4a1acc3', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.2.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.2.1.json', + referenceNumber: '384', + name: 'Open LDAP Public License v2.2.1', + licenseId: 'OLDAP-2.2.1', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=4bc786f34b50aa301be6f5600f58a980070f481e', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.2.2.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.2.2.json', + referenceNumber: '170', + name: 'Open LDAP Public License 2.2.2', + licenseId: 'OLDAP-2.2.2', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=df2cc1e21eb7c160695f5b7cffd6296c151ba188', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.3.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.3.json', + referenceNumber: '230', + name: 'Open LDAP Public License v2.3', + licenseId: 'OLDAP-2.3', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=d32cf54a32d581ab475d23c810b0a7fbaf8d63c3', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.4.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.4.json', + referenceNumber: '115', + name: 'Open LDAP Public License v2.4', + licenseId: 'OLDAP-2.4', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=cd1284c4a91a8a380d904eee68d1583f989ed386', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.5.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.5.json', + referenceNumber: '108', + name: 'Open LDAP Public License v2.5', + licenseId: 'OLDAP-2.5', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=6852b9d90022e8593c98205413380536b1b5a7cf', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.6.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.6.json', + referenceNumber: '109', + name: 'Open LDAP Public License v2.6', + licenseId: 'OLDAP-2.6', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=1cae062821881f41b73012ba816434897abf4205', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.7.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.7.json', + referenceNumber: '229', + name: 'Open LDAP Public License v2.7', + licenseId: 'OLDAP-2.7', + seeAlso: [ + 'http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=47c2415c1df81556eeb39be6cad458ef87c534a2', + ], + isOsiApproved: false, + }, + { + reference: './OLDAP-2.8.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OLDAP-2.8.json', + referenceNumber: '252', + name: 'Open LDAP Public License v2.8', + licenseId: 'OLDAP-2.8', + seeAlso: ['http://www.openldap.org/software/release/license.html'], + isOsiApproved: false, + }, + { + reference: './OML.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OML.json', + referenceNumber: '171', + name: 'Open Market License', + licenseId: 'OML', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Open_Market_License'], + isOsiApproved: false, + }, + { + reference: './OPL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OPL-1.0.json', + referenceNumber: '338', + name: 'Open Public License v1.0', + licenseId: 'OPL-1.0', + seeAlso: [ + 'http://old.koalateam.com/jackaroo/OPL_1_0.TXT', + 'https://fedoraproject.org/wiki/Licensing/Open_Public_License', + ], + isOsiApproved: false, + }, + { + reference: './OSET-PL-2.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/OSET-PL-2.1.json', + referenceNumber: '203', + name: 'OSET Public License version 2.1', + licenseId: 'OSET-PL-2.1', + seeAlso: [ + 'http://www.osetfoundation.org/public-license', + 'https://opensource.org/licenses/OPL-2.1', + ], + isOsiApproved: true, + }, + { + reference: './OSL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/OSL-1.0.json', + referenceNumber: '95', + name: 'Open Software License 1.0', + licenseId: 'OSL-1.0', + seeAlso: ['https://opensource.org/licenses/OSL-1.0'], + isOsiApproved: true, + }, + { + reference: './OSL-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/OSL-1.1.json', + referenceNumber: '178', + name: 'Open Software License 1.1', + licenseId: 'OSL-1.1', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/OSL1.1'], + isOsiApproved: false, + }, + { + reference: './OSL-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/OSL-2.0.json', + referenceNumber: '364', + name: 'Open Software License 2.0', + licenseId: 'OSL-2.0', + seeAlso: [ + 'http://web.archive.org/web/20041020171434/http://www.rosenlaw.com/osl2.0.html', + ], + isOsiApproved: true, + }, + { + reference: './OSL-2.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/OSL-2.1.json', + referenceNumber: '157', + name: 'Open Software License 2.1', + licenseId: 'OSL-2.1', + seeAlso: [ + 'http://web.archive.org/web/20050212003940/http://www.rosenlaw.com/osl21.htm', + 'https://opensource.org/licenses/OSL-2.1', + ], + isOsiApproved: true, + }, + { + reference: './OSL-3.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/OSL-3.0.json', + referenceNumber: '149', + name: 'Open Software License 3.0', + licenseId: 'OSL-3.0', + seeAlso: [ + 'https://web.archive.org/web/20120101081418/http://rosenlaw.com:80/OSL3.0.htm', + 'https://opensource.org/licenses/OSL-3.0', + ], + isOsiApproved: true, + }, + { + reference: './OpenSSL.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/OpenSSL.json', + referenceNumber: '83', + name: 'OpenSSL License', + licenseId: 'OpenSSL', + seeAlso: ['http://www.openssl.org/source/license.html'], + isOsiApproved: false, + }, + { + reference: './PDDL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/PDDL-1.0.json', + referenceNumber: '132', + name: 'ODC Public Domain Dedication & License 1.0', + licenseId: 'PDDL-1.0', + seeAlso: ['http://opendatacommons.org/licenses/pddl/1.0/'], + isOsiApproved: false, + }, + { + reference: './PHP-3.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/PHP-3.0.json', + referenceNumber: '191', + name: 'PHP License v3.0', + licenseId: 'PHP-3.0', + seeAlso: [ + 'http://www.php.net/license/3_0.txt', + 'https://opensource.org/licenses/PHP-3.0', + ], + isOsiApproved: true, + }, + { + reference: './PHP-3.01.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/PHP-3.01.json', + referenceNumber: '3', + name: 'PHP License v3.01', + licenseId: 'PHP-3.01', + seeAlso: ['http://www.php.net/license/3_01.txt'], + isOsiApproved: false, + }, + { + reference: './PSF-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/PSF-2.0.json', + referenceNumber: '91', + name: 'Python Software Foundation License 2.0', + licenseId: 'PSF-2.0', + seeAlso: ['https://opensource.org/licenses/Python-2.0'], + isOsiApproved: false, + }, + { + reference: './Parity-6.0.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Parity-6.0.0.json', + referenceNumber: '406', + name: 'The Parity Public License 6.0.0', + licenseId: 'Parity-6.0.0', + seeAlso: ['https://paritylicense.com/versions/6.0.0.html'], + isOsiApproved: false, + }, + { + reference: './Plexus.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Plexus.json', + referenceNumber: '156', + name: 'Plexus Classworlds License', + licenseId: 'Plexus', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing/Plexus_Classworlds_License', + ], + isOsiApproved: false, + }, + { + reference: './PostgreSQL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/PostgreSQL.json', + referenceNumber: '11', + name: 'PostgreSQL License', + licenseId: 'PostgreSQL', + seeAlso: [ + 'http://www.postgresql.org/about/licence', + 'https://opensource.org/licenses/PostgreSQL', + ], + isOsiApproved: true, + }, + { + reference: './Python-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Python-2.0.json', + referenceNumber: '393', + name: 'Python License 2.0', + licenseId: 'Python-2.0', + seeAlso: ['https://opensource.org/licenses/Python-2.0'], + isOsiApproved: true, + }, + { + reference: './QPL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/QPL-1.0.json', + referenceNumber: '289', + name: 'Q Public License 1.0', + licenseId: 'QPL-1.0', + seeAlso: [ + 'http://doc.qt.nokia.com/3.3/license.html', + 'https://opensource.org/licenses/QPL-1.0', + ], + isOsiApproved: true, + }, + { + reference: './Qhull.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Qhull.json', + referenceNumber: '124', + name: 'Qhull License', + licenseId: 'Qhull', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Qhull'], + isOsiApproved: false, + }, + { + reference: './RHeCos-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/RHeCos-1.1.json', + referenceNumber: '61', + name: 'Red Hat eCos Public License v1.1', + licenseId: 'RHeCos-1.1', + seeAlso: ['http://ecos.sourceware.org/old-license.html'], + isOsiApproved: false, + }, + { + reference: './RPL-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/RPL-1.1.json', + referenceNumber: '216', + name: 'Reciprocal Public License 1.1', + licenseId: 'RPL-1.1', + seeAlso: ['https://opensource.org/licenses/RPL-1.1'], + isOsiApproved: true, + }, + { + reference: './RPL-1.5.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/RPL-1.5.json', + referenceNumber: '105', + name: 'Reciprocal Public License 1.5', + licenseId: 'RPL-1.5', + seeAlso: ['https://opensource.org/licenses/RPL-1.5'], + isOsiApproved: true, + }, + { + reference: './RPSL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/RPSL-1.0.json', + referenceNumber: '53', + name: 'RealNetworks Public Source License v1.0', + licenseId: 'RPSL-1.0', + seeAlso: [ + 'https://helixcommunity.org/content/rpsl', + 'https://opensource.org/licenses/RPSL-1.0', + ], + isOsiApproved: true, + }, + { + reference: './RSA-MD.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/RSA-MD.json', + referenceNumber: '279', + name: 'RSA Message-Digest License ', + licenseId: 'RSA-MD', + seeAlso: ['http://www.faqs.org/rfcs/rfc1321.html'], + isOsiApproved: false, + }, + { + reference: './RSCPL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/RSCPL.json', + referenceNumber: '339', + name: 'Ricoh Source Code Public License', + licenseId: 'RSCPL', + seeAlso: [ + 'http://wayback.archive.org/web/20060715140826/http://www.risource.org/RPL/RPL-1.0A.shtml', + 'https://opensource.org/licenses/RSCPL', + ], + isOsiApproved: true, + }, + { + reference: './Rdisc.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Rdisc.json', + referenceNumber: '322', + name: 'Rdisc License', + licenseId: 'Rdisc', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Rdisc_License'], + isOsiApproved: false, + }, + { + reference: './Ruby.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Ruby.json', + referenceNumber: '14', + name: 'Ruby License', + licenseId: 'Ruby', + seeAlso: ['http://www.ruby-lang.org/en/LICENSE.txt'], + isOsiApproved: false, + }, + { + reference: './SAX-PD.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SAX-PD.json', + referenceNumber: '152', + name: 'Sax Public Domain Notice', + licenseId: 'SAX-PD', + seeAlso: ['http://www.saxproject.org/copying.html'], + isOsiApproved: false, + }, + { + reference: './SCEA.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SCEA.json', + referenceNumber: '137', + name: 'SCEA Shared Source License', + licenseId: 'SCEA', + seeAlso: ['http://research.scea.com/scea_shared_source_license.html'], + isOsiApproved: false, + }, + { + reference: './SGI-B-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SGI-B-1.0.json', + referenceNumber: '189', + name: 'SGI Free Software License B v1.0', + licenseId: 'SGI-B-1.0', + seeAlso: ['http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.1.0.html'], + isOsiApproved: false, + }, + { + reference: './SGI-B-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SGI-B-1.1.json', + referenceNumber: '288', + name: 'SGI Free Software License B v1.1', + licenseId: 'SGI-B-1.1', + seeAlso: ['http://oss.sgi.com/projects/FreeB/'], + isOsiApproved: false, + }, + { + reference: './SGI-B-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/SGI-B-2.0.json', + referenceNumber: '31', + name: 'SGI Free Software License B v2.0', + licenseId: 'SGI-B-2.0', + seeAlso: ['http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf'], + isOsiApproved: false, + }, + { + reference: './SHL-0.5.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SHL-0.5.json', + referenceNumber: '49', + name: 'Solderpad Hardware License v0.5', + licenseId: 'SHL-0.5', + seeAlso: ['https://solderpad.org/licenses/SHL-0.5/'], + isOsiApproved: false, + }, + { + reference: './SHL-0.51.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SHL-0.51.json', + referenceNumber: '281', + name: 'Solderpad Hardware License, Version 0.51', + licenseId: 'SHL-0.51', + seeAlso: ['https://solderpad.org/licenses/SHL-0.51/'], + isOsiApproved: false, + }, + { + reference: './SISSL.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/SISSL.json', + referenceNumber: '78', + name: 'Sun Industry Standards Source License v1.1', + licenseId: 'SISSL', + seeAlso: [ + 'http://www.openoffice.org/licenses/sissl_license.html', + 'https://opensource.org/licenses/SISSL', + ], + isOsiApproved: true, + }, + { + reference: './SISSL-1.2.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SISSL-1.2.json', + referenceNumber: '62', + name: 'Sun Industry Standards Source License v1.2', + licenseId: 'SISSL-1.2', + seeAlso: [ + 'http://gridscheduler.sourceforge.net/Gridengine_SISSL_license.html', + ], + isOsiApproved: false, + }, + { + reference: './SMLNJ.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/SMLNJ.json', + referenceNumber: '218', + name: 'Standard ML of New Jersey License', + licenseId: 'SMLNJ', + seeAlso: ['https://www.smlnj.org/license.html'], + isOsiApproved: false, + }, + { + reference: './SMPPL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SMPPL.json', + referenceNumber: '106', + name: 'Secure Messaging Protocol Public License', + licenseId: 'SMPPL', + seeAlso: [ + 'https://github.com/dcblake/SMP/blob/master/Documentation/License.txt', + ], + isOsiApproved: false, + }, + { + reference: './SNIA.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SNIA.json', + referenceNumber: '302', + name: 'SNIA Public License 1.1', + licenseId: 'SNIA', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/SNIA_Public_License'], + isOsiApproved: false, + }, + { + reference: './SPL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/SPL-1.0.json', + referenceNumber: '247', + name: 'Sun Public License v1.0', + licenseId: 'SPL-1.0', + seeAlso: ['https://opensource.org/licenses/SPL-1.0'], + isOsiApproved: true, + }, + { + reference: './SSH-OpenSSH.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SSH-OpenSSH.json', + referenceNumber: '22', + name: 'SSH OpenSSH license', + licenseId: 'SSH-OpenSSH', + seeAlso: [ + 'https://github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/LICENCE#L10', + ], + isOsiApproved: false, + }, + { + reference: './SSH-short.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SSH-short.json', + referenceNumber: '67', + name: 'SSH short notice', + licenseId: 'SSH-short', + seeAlso: [ + 'https://github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/pathnames.h', + 'http://web.mit.edu/kolya/.f/root/athena.mit.edu/sipb.mit.edu/project/openssh/OldFiles/src/openssh-2.9.9p2/ssh-add.1', + 'https://joinup.ec.europa.eu/svn/lesoll/trunk/italc/lib/src/dsa_key.cpp', + ], + isOsiApproved: false, + }, + { + reference: './SSPL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SSPL-1.0.json', + referenceNumber: '334', + name: 'Server Side Public License, v 1', + licenseId: 'SSPL-1.0', + seeAlso: ['https://www.mongodb.com/licensing/server-side-public-license'], + isOsiApproved: false, + }, + { + reference: './SWL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SWL.json', + referenceNumber: '93', + name: 'Scheme Widget Library (SWL) Software License Agreement', + licenseId: 'SWL', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/SWL'], + isOsiApproved: false, + }, + { + reference: './Saxpath.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Saxpath.json', + referenceNumber: '34', + name: 'Saxpath License', + licenseId: 'Saxpath', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Saxpath_License'], + isOsiApproved: false, + }, + { + reference: './Sendmail.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Sendmail.json', + referenceNumber: '293', + name: 'Sendmail License', + licenseId: 'Sendmail', + seeAlso: [ + 'http://www.sendmail.com/pdfs/open_source/sendmail_license.pdf', + 'https://web.archive.org/web/20160322142305/https://www.sendmail.com/pdfs/open_source/sendmail_license.pdf', + ], + isOsiApproved: false, + }, + { + reference: './Sendmail-8.23.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Sendmail-8.23.json', + referenceNumber: '176', + name: 'Sendmail License 8.23', + licenseId: 'Sendmail-8.23', + seeAlso: [ + 'https://www.proofpoint.com/sites/default/files/sendmail-license.pdf', + 'https://web.archive.org/web/20181003101040/https://www.proofpoint.com/sites/default/files/sendmail-license.pdf', + ], + isOsiApproved: false, + }, + { + reference: './SimPL-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SimPL-2.0.json', + referenceNumber: '250', + name: 'Simple Public License 2.0', + licenseId: 'SimPL-2.0', + seeAlso: ['https://opensource.org/licenses/SimPL-2.0'], + isOsiApproved: true, + }, + { + reference: './Sleepycat.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Sleepycat.json', + referenceNumber: '56', + name: 'Sleepycat License', + licenseId: 'Sleepycat', + seeAlso: ['https://opensource.org/licenses/Sleepycat'], + isOsiApproved: true, + }, + { + reference: './Spencer-86.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Spencer-86.json', + referenceNumber: '184', + name: 'Spencer License 86', + licenseId: 'Spencer-86', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License', + ], + isOsiApproved: false, + }, + { + reference: './Spencer-94.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Spencer-94.json', + referenceNumber: '213', + name: 'Spencer License 94', + licenseId: 'Spencer-94', + seeAlso: [ + 'https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License', + ], + isOsiApproved: false, + }, + { + reference: './Spencer-99.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Spencer-99.json', + referenceNumber: '64', + name: 'Spencer License 99', + licenseId: 'Spencer-99', + seeAlso: [ + 'http://www.opensource.apple.com/source/tcl/tcl-5/tcl/generic/regfronts.c', + ], + isOsiApproved: false, + }, + { + reference: './StandardML-NJ.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/StandardML-NJ.json', + referenceNumber: '285', + name: 'Standard ML of New Jersey License', + licenseId: 'StandardML-NJ', + seeAlso: ['http://www.smlnj.org//license.html'], + isOsiApproved: false, + }, + { + reference: './SugarCRM-1.1.3.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/SugarCRM-1.1.3.json', + referenceNumber: '342', + name: 'SugarCRM Public License v1.1.3', + licenseId: 'SugarCRM-1.1.3', + seeAlso: ['http://www.sugarcrm.com/crm/SPL'], + isOsiApproved: false, + }, + { + reference: './TAPR-OHL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/TAPR-OHL-1.0.json', + referenceNumber: '9', + name: 'TAPR Open Hardware License v1.0', + licenseId: 'TAPR-OHL-1.0', + seeAlso: ['https://www.tapr.org/OHL'], + isOsiApproved: false, + }, + { + reference: './TCL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/TCL.json', + referenceNumber: '54', + name: 'TCL/TK License', + licenseId: 'TCL', + seeAlso: [ + 'http://www.tcl.tk/software/tcltk/license.html', + 'https://fedoraproject.org/wiki/Licensing/TCL', + ], + isOsiApproved: false, + }, + { + reference: './TCP-wrappers.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/TCP-wrappers.json', + referenceNumber: '234', + name: 'TCP Wrappers License', + licenseId: 'TCP-wrappers', + seeAlso: ['http://rc.quest.com/topics/openssh/license.php#tcpwrappers'], + isOsiApproved: false, + }, + { + reference: './TMate.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/TMate.json', + referenceNumber: '402', + name: 'TMate Open Source License', + licenseId: 'TMate', + seeAlso: ['http://svnkit.com/license.html'], + isOsiApproved: false, + }, + { + reference: './TORQUE-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/TORQUE-1.1.json', + referenceNumber: '188', + name: 'TORQUE v2.5+ Software License v1.1', + licenseId: 'TORQUE-1.1', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/TORQUEv1.1'], + isOsiApproved: false, + }, + { + reference: './TOSL.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/TOSL.json', + referenceNumber: '251', + name: 'Trusster Open Source License', + licenseId: 'TOSL', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/TOSL'], + isOsiApproved: false, + }, + { + reference: './TU-Berlin-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/TU-Berlin-1.0.json', + referenceNumber: '372', + name: 'Technische Universitaet Berlin License 1.0', + licenseId: 'TU-Berlin-1.0', + seeAlso: [ + 'https://github.com/swh/ladspa/blob/7bf6f3799fdba70fda297c2d8fd9f526803d9680/gsm/COPYRIGHT', + ], + isOsiApproved: false, + }, + { + reference: './TU-Berlin-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/TU-Berlin-2.0.json', + referenceNumber: '392', + name: 'Technische Universitaet Berlin License 2.0', + licenseId: 'TU-Berlin-2.0', + seeAlso: [ + 'https://github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt', + ], + isOsiApproved: false, + }, + { + reference: './UCL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/UCL-1.0.json', + referenceNumber: '291', + name: 'Upstream Compatibility License v1.0', + licenseId: 'UCL-1.0', + seeAlso: ['https://opensource.org/licenses/UCL-1.0'], + isOsiApproved: true, + }, + { + reference: './UPL-1.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/UPL-1.0.json', + referenceNumber: '144', + name: 'Universal Permissive License v1.0', + licenseId: 'UPL-1.0', + seeAlso: ['https://opensource.org/licenses/UPL'], + isOsiApproved: true, + }, + { + reference: './Unicode-DFS-2015.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Unicode-DFS-2015.json', + referenceNumber: '260', + name: 'Unicode License Agreement - Data Files and Software (2015)', + licenseId: 'Unicode-DFS-2015', + seeAlso: [ + 'https://web.archive.org/web/20151224134844/http://unicode.org/copyright.html', + ], + isOsiApproved: false, + }, + { + reference: './Unicode-DFS-2016.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Unicode-DFS-2016.json', + referenceNumber: '370', + name: 'Unicode License Agreement - Data Files and Software (2016)', + licenseId: 'Unicode-DFS-2016', + seeAlso: ['http://www.unicode.org/copyright.html'], + isOsiApproved: false, + }, + { + reference: './Unicode-TOU.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Unicode-TOU.json', + referenceNumber: '16', + name: 'Unicode Terms of Use', + licenseId: 'Unicode-TOU', + seeAlso: ['http://www.unicode.org/copyright.html'], + isOsiApproved: false, + }, + { + reference: './Unlicense.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Unlicense.json', + referenceNumber: '173', + name: 'The Unlicense', + licenseId: 'Unlicense', + seeAlso: ['https://unlicense.org/'], + isOsiApproved: false, + }, + { + reference: './VOSTROM.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/VOSTROM.json', + referenceNumber: '352', + name: 'VOSTROM Public License for Open Source', + licenseId: 'VOSTROM', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/VOSTROM'], + isOsiApproved: false, + }, + { + reference: './VSL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/VSL-1.0.json', + referenceNumber: '390', + name: 'Vovida Software License v1.0', + licenseId: 'VSL-1.0', + seeAlso: ['https://opensource.org/licenses/VSL-1.0'], + isOsiApproved: true, + }, + { + reference: './Vim.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Vim.json', + referenceNumber: '206', + name: 'Vim License', + licenseId: 'Vim', + seeAlso: ['http://vimdoc.sourceforge.net/htmldoc/uganda.html'], + isOsiApproved: false, + }, + { + reference: './W3C.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/W3C.json', + referenceNumber: '107', + name: 'W3C Software Notice and License (2002-12-31)', + licenseId: 'W3C', + seeAlso: [ + 'http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231.html', + 'https://opensource.org/licenses/W3C', + ], + isOsiApproved: true, + }, + { + reference: './W3C-19980720.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/W3C-19980720.json', + referenceNumber: '266', + name: 'W3C Software Notice and License (1998-07-20)', + licenseId: 'W3C-19980720', + seeAlso: [ + 'http://www.w3.org/Consortium/Legal/copyright-software-19980720.html', + ], + isOsiApproved: false, + }, + { + reference: './W3C-20150513.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/W3C-20150513.json', + referenceNumber: '111', + name: 'W3C Software Notice and Document License (2015-05-13)', + licenseId: 'W3C-20150513', + seeAlso: [ + 'https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document', + ], + isOsiApproved: false, + }, + { + reference: './WTFPL.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/WTFPL.json', + referenceNumber: '20', + name: 'Do What The F*ck You Want To Public License', + licenseId: 'WTFPL', + seeAlso: ['http://sam.zoy.org/wtfpl/COPYING'], + isOsiApproved: false, + }, + { + reference: './Watcom-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Watcom-1.0.json', + referenceNumber: '141', + name: 'Sybase Open Watcom Public License 1.0', + licenseId: 'Watcom-1.0', + seeAlso: ['https://opensource.org/licenses/Watcom-1.0'], + isOsiApproved: true, + }, + { + reference: './Wsuipa.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Wsuipa.json', + referenceNumber: '255', + name: 'Wsuipa License', + licenseId: 'Wsuipa', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Wsuipa'], + isOsiApproved: false, + }, + { + reference: './X11.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/X11.json', + referenceNumber: '99', + name: 'X11 License', + licenseId: 'X11', + seeAlso: ['http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3'], + isOsiApproved: false, + }, + { + reference: './XFree86-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/XFree86-1.1.json', + referenceNumber: '155', + name: 'XFree86 License 1.1', + licenseId: 'XFree86-1.1', + seeAlso: ['http://www.xfree86.org/current/LICENSE4.html'], + isOsiApproved: false, + }, + { + reference: './XSkat.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/XSkat.json', + referenceNumber: '81', + name: 'XSkat License', + licenseId: 'XSkat', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/XSkat_License'], + isOsiApproved: false, + }, + { + reference: './Xerox.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Xerox.json', + referenceNumber: '224', + name: 'Xerox License', + licenseId: 'Xerox', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Xerox'], + isOsiApproved: false, + }, + { + reference: './Xnet.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Xnet.json', + referenceNumber: '313', + name: 'X.Net License', + licenseId: 'Xnet', + seeAlso: ['https://opensource.org/licenses/Xnet'], + isOsiApproved: true, + }, + { + reference: './YPL-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/YPL-1.0.json', + referenceNumber: '292', + name: 'Yahoo! Public License v1.0', + licenseId: 'YPL-1.0', + seeAlso: ['http://www.zimbra.com/license/yahoo_public_license_1.0.html'], + isOsiApproved: false, + }, + { + reference: './YPL-1.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/YPL-1.1.json', + referenceNumber: '40', + name: 'Yahoo! Public License v1.1', + licenseId: 'YPL-1.1', + seeAlso: ['http://www.zimbra.com/license/yahoo_public_license_1.1.html'], + isOsiApproved: false, + }, + { + reference: './ZPL-1.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/ZPL-1.1.json', + referenceNumber: '85', + name: 'Zope Public License 1.1', + licenseId: 'ZPL-1.1', + seeAlso: ['http://old.zope.org/Resources/License/ZPL-1.1'], + isOsiApproved: false, + }, + { + reference: './ZPL-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/ZPL-2.0.json', + referenceNumber: '112', + name: 'Zope Public License 2.0', + licenseId: 'ZPL-2.0', + seeAlso: [ + 'http://old.zope.org/Resources/License/ZPL-2.0', + 'https://opensource.org/licenses/ZPL-2.0', + ], + isOsiApproved: true, + }, + { + reference: './ZPL-2.1.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/ZPL-2.1.json', + referenceNumber: '368', + name: 'Zope Public License 2.1', + licenseId: 'ZPL-2.1', + seeAlso: ['http://old.zope.org/Resources/ZPL/'], + isOsiApproved: false, + }, + { + reference: './Zed.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Zed.json', + referenceNumber: '114', + name: 'Zed License', + licenseId: 'Zed', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Zed'], + isOsiApproved: false, + }, + { + reference: './Zend-2.0.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Zend-2.0.json', + referenceNumber: '373', + name: 'Zend License v2.0', + licenseId: 'Zend-2.0', + seeAlso: [ + 'https://web.archive.org/web/20130517195954/http://www.zend.com/license/2_00.txt', + ], + isOsiApproved: false, + }, + { + reference: './Zimbra-1.3.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Zimbra-1.3.json', + referenceNumber: '175', + name: 'Zimbra Public License v1.3', + licenseId: 'Zimbra-1.3', + seeAlso: [ + 'http://web.archive.org/web/20100302225219/http://www.zimbra.com/license/zimbra-public-license-1-3.html', + ], + isOsiApproved: false, + }, + { + reference: './Zimbra-1.4.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/Zimbra-1.4.json', + referenceNumber: '385', + name: 'Zimbra Public License v1.4', + licenseId: 'Zimbra-1.4', + seeAlso: ['http://www.zimbra.com/legal/zimbra-public-license-1-4'], + isOsiApproved: false, + }, + { + reference: './Zlib.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/Zlib.json', + referenceNumber: '45', + name: 'zlib License', + licenseId: 'Zlib', + seeAlso: [ + 'http://www.zlib.net/zlib_license.html', + 'https://opensource.org/licenses/Zlib', + ], + isOsiApproved: true, + }, + { + reference: './blessing.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/blessing.json', + referenceNumber: '301', + name: 'SQLite Blessing', + licenseId: 'blessing', + seeAlso: [ + 'https://www.sqlite.org/src/artifact/e33a4df7e32d742a?ln=4-9', + 'https://sqlite.org/src/artifact/df5091916dbb40e6', + ], + isOsiApproved: false, + }, + { + reference: './bzip2-1.0.5.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/bzip2-1.0.5.json', + referenceNumber: '186', + name: 'bzip2 and libbzip2 License v1.0.5', + licenseId: 'bzip2-1.0.5', + seeAlso: ['http://bzip.org/1.0.5/bzip2-manual-1.0.5.html'], + isOsiApproved: false, + }, + { + reference: './bzip2-1.0.6.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/bzip2-1.0.6.json', + referenceNumber: '69', + name: 'bzip2 and libbzip2 License v1.0.6', + licenseId: 'bzip2-1.0.6', + seeAlso: ['https://github.com/asimonov-im/bzip2/blob/master/LICENSE'], + isOsiApproved: false, + }, + { + reference: './copyleft-next-0.3.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/copyleft-next-0.3.0.json', + referenceNumber: '312', + name: 'copyleft-next 0.3.0', + licenseId: 'copyleft-next-0.3.0', + seeAlso: [ + 'https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.0', + ], + isOsiApproved: false, + }, + { + reference: './copyleft-next-0.3.1.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/copyleft-next-0.3.1.json', + referenceNumber: '378', + name: 'copyleft-next 0.3.1', + licenseId: 'copyleft-next-0.3.1', + seeAlso: [ + 'https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.1', + ], + isOsiApproved: false, + }, + { + reference: './curl.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/curl.json', + referenceNumber: '314', + name: 'curl License', + licenseId: 'curl', + seeAlso: ['https://github.com/bagder/curl/blob/master/COPYING'], + isOsiApproved: false, + }, + { + reference: './diffmark.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/diffmark.json', + referenceNumber: '397', + name: 'diffmark license', + licenseId: 'diffmark', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/diffmark'], + isOsiApproved: false, + }, + { + reference: './dvipdfm.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/dvipdfm.json', + referenceNumber: '19', + name: 'dvipdfm License', + licenseId: 'dvipdfm', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/dvipdfm'], + isOsiApproved: false, + }, + { + reference: './eCos-2.0.html', + isDeprecatedLicenseId: true, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/eCos-2.0.json', + referenceNumber: '272', + name: 'eCos license version 2.0', + licenseId: 'eCos-2.0', + seeAlso: ['https://www.gnu.org/licenses/ecos-license.html'], + isOsiApproved: false, + }, + { + reference: './eGenix.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/eGenix.json', + referenceNumber: '214', + name: 'eGenix.com Public License 1.1.0', + licenseId: 'eGenix', + seeAlso: [ + 'http://www.egenix.com/products/eGenix.com-Public-License-1.1.0.pdf', + 'https://fedoraproject.org/wiki/Licensing/eGenix.com_Public_License_1.1.0', + ], + isOsiApproved: false, + }, + { + reference: './etalab-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/etalab-2.0.json', + referenceNumber: '259', + name: 'Etalab Open License 2.0', + licenseId: 'etalab-2.0', + seeAlso: [ + 'https://github.com/DISIC/politique-de-contribution-open-source/blob/master/LICENSE.pdf', + 'https://raw.githubusercontent.com/DISIC/politique-de-contribution-open-source/master/LICENSE', + ], + isOsiApproved: false, + }, + { + reference: './gSOAP-1.3b.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/gSOAP-1.3b.json', + referenceNumber: '167', + name: 'gSOAP Public License v1.3b', + licenseId: 'gSOAP-1.3b', + seeAlso: ['http://www.cs.fsu.edu/~engelen/license.html'], + isOsiApproved: false, + }, + { + reference: './gnuplot.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/gnuplot.json', + referenceNumber: '383', + name: 'gnuplot License', + licenseId: 'gnuplot', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Gnuplot'], + isOsiApproved: false, + }, + { + reference: './iMatix.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/iMatix.json', + referenceNumber: '177', + name: 'iMatix Standard Function Library Agreement', + licenseId: 'iMatix', + seeAlso: ['http://legacy.imatix.com/html/sfl/sfl4.htm#license'], + isOsiApproved: false, + }, + { + reference: './libpng-2.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/libpng-2.0.json', + referenceNumber: '103', + name: 'PNG Reference Library version 2', + licenseId: 'libpng-2.0', + seeAlso: ['http://www.libpng.org/pub/png/src/libpng-LICENSE.txt'], + isOsiApproved: false, + }, + { + reference: './libselinux-1.0.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/libselinux-1.0.json', + referenceNumber: '18', + name: 'libselinux public domain notice', + licenseId: 'libselinux-1.0', + seeAlso: [ + 'https://github.com/SELinuxProject/selinux/blob/master/libselinux/LICENSE', + ], + isOsiApproved: false, + }, + { + reference: './libtiff.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/libtiff.json', + referenceNumber: '407', + name: 'libtiff License', + licenseId: 'libtiff', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/libtiff'], + isOsiApproved: false, + }, + { + reference: './mpich2.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/mpich2.json', + referenceNumber: '60', + name: 'mpich2 License', + licenseId: 'mpich2', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/MIT'], + isOsiApproved: false, + }, + { + reference: './psfrag.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/psfrag.json', + referenceNumber: '408', + name: 'psfrag License', + licenseId: 'psfrag', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/psfrag'], + isOsiApproved: false, + }, + { + reference: './psutils.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/psutils.json', + referenceNumber: '277', + name: 'psutils License', + licenseId: 'psutils', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/psutils'], + isOsiApproved: false, + }, + { + reference: './wxWindows.html', + isDeprecatedLicenseId: true, + detailsUrl: 'http://spdx.org/licenses/wxWindows.json', + referenceNumber: '244', + name: 'wxWindows Library License', + licenseId: 'wxWindows', + seeAlso: ['https://opensource.org/licenses/WXwindows'], + isOsiApproved: false, + }, + { + reference: './xinetd.html', + isDeprecatedLicenseId: false, + isFsfLibre: true, + detailsUrl: 'http://spdx.org/licenses/xinetd.json', + referenceNumber: '399', + name: 'xinetd License', + licenseId: 'xinetd', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/Xinetd_License'], + isOsiApproved: false, + }, + { + reference: './xpp.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/xpp.json', + referenceNumber: '96', + name: 'XPP License', + licenseId: 'xpp', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/xpp'], + isOsiApproved: false, + }, + { + reference: './zlib-acknowledgement.html', + isDeprecatedLicenseId: false, + detailsUrl: 'http://spdx.org/licenses/zlib-acknowledgement.json', + referenceNumber: '246', + name: 'zlib/libpng License with Acknowledgement', + licenseId: 'zlib-acknowledgement', + seeAlso: ['https://fedoraproject.org/wiki/Licensing/ZlibWithAcknowledgement'], + isOsiApproved: false, + }, + ], + releaseDate: '2020-02-13', }; diff --git a/packages/@romejs/codec-spdx-license/index.ts b/packages/@romejs/codec-spdx-license/index.ts index 621c3901408..b6493cad39e 100644 --- a/packages/@romejs/codec-spdx-license/index.ts +++ b/packages/@romejs/codec-spdx-license/index.ts @@ -8,22 +8,22 @@ import data from './data'; type License = { - reference: string; - isDeprecatedLicenseId: boolean; - isFsfLibre?: boolean; - detailsUrl: string; - referenceNumber: string; - name: string; - licenseId: string; - seeAlso: Array; - isOsiApproved: boolean; + reference: string; + isDeprecatedLicenseId: boolean; + isFsfLibre?: boolean; + detailsUrl: string; + referenceNumber: string; + name: string; + licenseId: string; + seeAlso: Array; + isOsiApproved: boolean; }; const idToLicense: Map = new Map(); const licenseNames: Array = []; for (const license of data.licenses) { - licenseNames.push(license.licenseId); - idToLicense.set(license.licenseId, license); + licenseNames.push(license.licenseId); + idToLicense.set(license.licenseId, license); } export {licenseNames}; @@ -31,7 +31,7 @@ export {licenseNames}; export {ExpressionNode as SPDXExpressionNode} from './parse'; export function getSPDXLicense(licenseId: string): undefined | License { - return idToLicense.get(licenseId); + return idToLicense.get(licenseId); } export {default as parseSPDXLicense} from './parse'; diff --git a/packages/@romejs/codec-spdx-license/parse.ts b/packages/@romejs/codec-spdx-license/parse.ts index 2c0609e0198..564cff017b5 100644 --- a/packages/@romejs/codec-spdx-license/parse.ts +++ b/packages/@romejs/codec-spdx-license/parse.ts @@ -6,14 +6,14 @@ */ import { - BaseTokens, - ComplexNode, - ParserOptions, - SimpleToken, - ValueToken, - createParser, - isAlpha, - isDigit, + BaseTokens, + ComplexNode, + ParserOptions, + SimpleToken, + ValueToken, + createParser, + isAlpha, + isDigit, } from '@romejs/parser-core'; import {getSPDXLicense, licenseNames} from './index'; import {descriptions} from '@romejs/diagnostics'; @@ -21,240 +21,240 @@ import {Number0, ob1Get0, ob1Inc} from '@romejs/ob1'; //# Tokens type Tokens = BaseTokens & { - ParenOpen: SimpleToken<'ParenOpen'>; - ParenClose: SimpleToken<'ParenClose'>; - Plus: SimpleToken<'Plus'>; - And: SimpleToken<'And'>; - With: SimpleToken<'With'>; - Or: SimpleToken<'Or'>; - Word: ValueToken<'Word', string>; + ParenOpen: SimpleToken<'ParenOpen'>; + ParenClose: SimpleToken<'ParenClose'>; + Plus: SimpleToken<'Plus'>; + And: SimpleToken<'And'>; + With: SimpleToken<'With'>; + Or: SimpleToken<'Or'>; + Word: ValueToken<'Word', string>; }; //# Nodes export type ExpressionNode = LicenseNode | AndNode | OrNode; type AndNode = ComplexNode< - 'And', - { - left: ExpressionNode; - right: ExpressionNode; - } + 'And', + { + left: ExpressionNode; + right: ExpressionNode; + } >; type OrNode = ComplexNode< - 'Or', - { - left: ExpressionNode; - right: ExpressionNode; - } + 'Or', + { + left: ExpressionNode; + right: ExpressionNode; + } >; type LicenseNode = ComplexNode< - 'License', - { - plus: boolean; - id: string; - exception: undefined | string; - } + 'License', + { + plus: boolean; + id: string; + exception: undefined | string; + } >; function isWordChar(char: string) { - return isAlpha(char) || isDigit(char) || char === '-' || char === '.'; + return isAlpha(char) || isDigit(char) || char === '-' || char === '.'; } type SPDXLicenseParserOptions = ParserOptions & { - loose?: boolean; + loose?: boolean; }; const createSPDXLicenseParser = createParser((ParserCore) => - class SPDXLicenseParser extends ParserCore { - constructor(opts: SPDXLicenseParserOptions) { - super(opts, 'parse/spdxLicense'); - this.loose = opts.loose === true; - } - - loose: boolean; - - // For some reason Flow will throw an error without the type casts... - tokenize(index: Number0, input: string) { - const char = input[ob1Get0(index)]; - - if (char === '+') { - return this.finishToken('Plus'); - } - - if (char === '(') { - return this.finishToken('ParenOpen'); - } - - if (char === ')') { - return this.finishToken('ParenClose'); - } - - // Skip spaces - if (char === ' ') { - return this.lookaheadToken(ob1Inc(index)); - } - - if (isWordChar(char)) { - const [value, end] = this.readInputFrom(index, isWordChar); - - if (value === 'AND') { - return this.finishToken('And', end); - } else if (value === 'OR') { - return this.finishToken('Or', end); - } else if (value === 'WITH') { - return this.finishToken('With', end); - } else { - return this.finishValueToken('Word', value, end); - } - } - - return undefined; - } - - parseLicense(token: Tokens['Word']): LicenseNode { - const startPos = this.getPosition(); - this.nextToken(); - - // Validate id - const id = token.value; - let licenseInfo = getSPDXLicense(id); - const nextToken = this.getToken(); - - // Sometimes licenses will be specified as "Apache 2.0" but what they actually meant was "Apache-2.0" - - // In loose mode, just make it equivalent, otherwise, complain - if (licenseInfo === undefined && nextToken.type === 'Word') { - const possibleCorrectLicense = `${id}-${nextToken.value}`; - const possibleLicenseInfo = getSPDXLicense(possibleCorrectLicense); - - if (possibleLicenseInfo !== undefined) { - if (this.loose) { - // Just allow it... - licenseInfo = possibleLicenseInfo; - this.nextToken(); - } else { - throw this.unexpected({ - description: descriptions.SPDX.VALID_LICENSE_WITH_MISSING_DASH( - possibleCorrectLicense, - ), - start: this.getPositionFromIndex(token.start), - end: this.getPositionFromIndex(nextToken.end), - }); - } - } - } - - if (licenseInfo === undefined) { - throw this.unexpected({ - description: descriptions.SPDX.UNKNOWN_LICENSE(id, licenseNames), - start: this.getPositionFromIndex(token.start), - end: this.getPositionFromIndex(token.end), - }); - } - - // Is this a plus? (wtf is this) - const plus = this.eatToken('Plus') !== undefined; - - // Get exception - let exception; - if (this.eatToken('With')) { - const token = this.getToken(); - if (token.type === 'Word') { - exception = token.value; - this.nextToken(); - } else { - throw this.unexpected({ - description: descriptions.SPDX.WITH_RIGHT_LICENSE_ONLY, - }); - } - } - - return { - type: 'License', - loc: this.finishLoc(startPos), - id, - exception, - plus, - }; - } - - parseExpression(): ExpressionNode { - const startPos = this.getPosition(); - const startToken = this.getToken(); - - let value; - - switch (startToken.type) { - case 'ParenOpen': { - this.nextToken(); - value = this.parseExpression(); - this.expectToken('ParenClose'); - break; - } - - case 'Word': { - value = this.parseLicense(startToken); - break; - } - - case 'Or': - case 'And': - throw this.unexpected({ - description: descriptions.SPDX.OPERATOR_NOT_BETWEEN_EXPRESSION, - }); - - case 'Plus': - throw this.unexpected({ - description: descriptions.SPDX.PLUS_NOT_AFTER_LICENSE, - }); - - case 'ParenClose': - throw this.unexpected({ - description: descriptions.SPDX.UNOPENED_PAREN, - }); - - default: - throw this.unexpected(); - } - - // Parse and/or - const nextToken = this.getToken(); - switch (nextToken.type) { - case 'Or': { - this.nextToken(); - return { - type: 'Or', - loc: this.finishLoc(startPos), - left: value, - right: this.parseExpression(), - }; - } - - case 'And': { - this.nextToken(); - return { - type: 'And', - loc: this.finishLoc(startPos), - left: value, - right: this.parseExpression(), - }; - } - - default: - return value; - } - } - - parse(): ExpressionNode { - const expr = this.parseExpression(); - this.finalize(); - return expr; - } - } + class SPDXLicenseParser extends ParserCore { + constructor(opts: SPDXLicenseParserOptions) { + super(opts, 'parse/spdxLicense'); + this.loose = opts.loose === true; + } + + loose: boolean; + + // For some reason Flow will throw an error without the type casts... + tokenize(index: Number0, input: string) { + const char = input[ob1Get0(index)]; + + if (char === '+') { + return this.finishToken('Plus'); + } + + if (char === '(') { + return this.finishToken('ParenOpen'); + } + + if (char === ')') { + return this.finishToken('ParenClose'); + } + + // Skip spaces + if (char === ' ') { + return this.lookaheadToken(ob1Inc(index)); + } + + if (isWordChar(char)) { + const [value, end] = this.readInputFrom(index, isWordChar); + + if (value === 'AND') { + return this.finishToken('And', end); + } else if (value === 'OR') { + return this.finishToken('Or', end); + } else if (value === 'WITH') { + return this.finishToken('With', end); + } else { + return this.finishValueToken('Word', value, end); + } + } + + return undefined; + } + + parseLicense(token: Tokens['Word']): LicenseNode { + const startPos = this.getPosition(); + this.nextToken(); + + // Validate id + const id = token.value; + let licenseInfo = getSPDXLicense(id); + const nextToken = this.getToken(); + + // Sometimes licenses will be specified as "Apache 2.0" but what they actually meant was "Apache-2.0" + + // In loose mode, just make it equivalent, otherwise, complain + if (licenseInfo === undefined && nextToken.type === 'Word') { + const possibleCorrectLicense = `${id}-${nextToken.value}`; + const possibleLicenseInfo = getSPDXLicense(possibleCorrectLicense); + + if (possibleLicenseInfo !== undefined) { + if (this.loose) { + // Just allow it... + licenseInfo = possibleLicenseInfo; + this.nextToken(); + } else { + throw this.unexpected({ + description: descriptions.SPDX.VALID_LICENSE_WITH_MISSING_DASH( + possibleCorrectLicense, + ), + start: this.getPositionFromIndex(token.start), + end: this.getPositionFromIndex(nextToken.end), + }); + } + } + } + + if (licenseInfo === undefined) { + throw this.unexpected({ + description: descriptions.SPDX.UNKNOWN_LICENSE(id, licenseNames), + start: this.getPositionFromIndex(token.start), + end: this.getPositionFromIndex(token.end), + }); + } + + // Is this a plus? (wtf is this) + const plus = this.eatToken('Plus') !== undefined; + + // Get exception + let exception; + if (this.eatToken('With')) { + const token = this.getToken(); + if (token.type === 'Word') { + exception = token.value; + this.nextToken(); + } else { + throw this.unexpected({ + description: descriptions.SPDX.WITH_RIGHT_LICENSE_ONLY, + }); + } + } + + return { + type: 'License', + loc: this.finishLoc(startPos), + id, + exception, + plus, + }; + } + + parseExpression(): ExpressionNode { + const startPos = this.getPosition(); + const startToken = this.getToken(); + + let value; + + switch (startToken.type) { + case 'ParenOpen': { + this.nextToken(); + value = this.parseExpression(); + this.expectToken('ParenClose'); + break; + } + + case 'Word': { + value = this.parseLicense(startToken); + break; + } + + case 'Or': + case 'And': + throw this.unexpected({ + description: descriptions.SPDX.OPERATOR_NOT_BETWEEN_EXPRESSION, + }); + + case 'Plus': + throw this.unexpected({ + description: descriptions.SPDX.PLUS_NOT_AFTER_LICENSE, + }); + + case 'ParenClose': + throw this.unexpected({ + description: descriptions.SPDX.UNOPENED_PAREN, + }); + + default: + throw this.unexpected(); + } + + // Parse and/or + const nextToken = this.getToken(); + switch (nextToken.type) { + case 'Or': { + this.nextToken(); + return { + type: 'Or', + loc: this.finishLoc(startPos), + left: value, + right: this.parseExpression(), + }; + } + + case 'And': { + this.nextToken(); + return { + type: 'And', + loc: this.finishLoc(startPos), + left: value, + right: this.parseExpression(), + }; + } + + default: + return value; + } + } + + parse(): ExpressionNode { + const expr = this.parseExpression(); + this.finalize(); + return expr; + } + } ); export default function parse(opts: SPDXLicenseParserOptions): ExpressionNode { - return createSPDXLicenseParser(opts).parse(); + return createSPDXLicenseParser(opts).parse(); } diff --git a/packages/@romejs/codec-spdx-license/stringify.ts b/packages/@romejs/codec-spdx-license/stringify.ts index d7b19061af1..41227e4176c 100644 --- a/packages/@romejs/codec-spdx-license/stringify.ts +++ b/packages/@romejs/codec-spdx-license/stringify.ts @@ -8,23 +8,23 @@ import {ExpressionNode} from './parse'; export default function stringify(node: ExpressionNode): string { - // TODO parens - switch (node.type) { - case 'Or': - return `${stringify(node.left)} OR ${stringify(node.right)}`; + // TODO parens + switch (node.type) { + case 'Or': + return `${stringify(node.left)} OR ${stringify(node.right)}`; - case 'And': - return `${stringify(node.left)} AND ${stringify(node.right)}`; + case 'And': + return `${stringify(node.left)} AND ${stringify(node.right)}`; - case 'License': { - let str = node.id; - if (node.plus) { - str += '+'; - } - if (node.exception !== undefined) { - str += ` WITH ${node.exception}`; - } - return str; - } - } + case 'License': { + let str = node.id; + if (node.plus) { + str += '+'; + } + if (node.exception !== undefined) { + str += ` WITH ${node.exception}`; + } + return str; + } + } } diff --git a/packages/@romejs/codec-tar/index.ts b/packages/@romejs/codec-tar/index.ts index 201dca68269..0e64eb668e4 100644 --- a/packages/@romejs/codec-tar/index.ts +++ b/packages/@romejs/codec-tar/index.ts @@ -8,42 +8,42 @@ import stream = require('stream'); type HeaderType = - | 'file' - | 'link' - | 'symlink' - | 'directory' - | 'block-device' - | 'character-device' - | 'fifo' - | 'contiguous-file'; + | 'file' + | 'link' + | 'symlink' + | 'directory' + | 'block-device' + | 'character-device' + | 'fifo' + | 'contiguous-file'; type Header = { - name: string; - size: number; - mode: number; - mtime: Date; - type: HeaderType; - linkname: undefined | string; - uid: number; - gid: number; - uname: undefined | string; - gname: undefined | string; - devmajor: number; - devminor: number; + name: string; + size: number; + mode: number; + mtime: Date; + type: HeaderType; + linkname: undefined | string; + uid: number; + gid: number; + uname: undefined | string; + gname: undefined | string; + devmajor: number; + devminor: number; }; type PartialHeader = { - name: string; - mode?: number; - mtime?: Date; - type?: HeaderType; - linkname?: string; - uid?: number; - gid?: number; - uname?: string; - gname?: string; - devmajor?: number; - devminor?: number; + name: string; + mode?: number; + mtime?: Date; + type?: HeaderType; + linkname?: string; + uid?: number; + gid?: number; + uname?: string; + gname?: string; + devmajor?: number; + devminor?: number; }; const END_OF_TAR = Buffer.alloc(1_024); @@ -56,195 +56,193 @@ const DMODE = 493; const FMODE = 420; function encodeOct(num: number, n: number): string { - const oct = num.toString(8); - if (oct.length > n) { - return `${SEVENS.slice(0, n)} `; - } else { - return `${ZEROS.slice(0, n - oct.length)}${oct} `; - } + const oct = num.toString(8); + if (oct.length > n) { + return `${SEVENS.slice(0, n)} `; + } else { + return `${ZEROS.slice(0, n - oct.length)}${oct} `; + } } function checksum(block: Buffer): number { - let sum = 8 * 32; - for (let i = 0; i < 148; i++) { - sum += block[i]; - } - for (let j = 156; j < 512; j++) { - sum += block[j]; - } - return sum; + let sum = 8 * 32; + for (let i = 0; i < 148; i++) { + sum += block[i]; + } + for (let j = 156; j < 512; j++) { + sum += block[j]; + } + return sum; } function toTypeflag(type: HeaderType): number { - switch (type) { - case 'file': - return 0; - case 'link': - return 1; - case 'symlink': - return 2; - case 'character-device': - return 3; - case 'block-device': - return 4; - case 'directory': - return 5; - case 'fifo': - return 6; - case 'contiguous-file': - return 7; - } - - return 0; + switch (type) { + case 'file': + return 0; + case 'link': + return 1; + case 'symlink': + return 2; + case 'character-device': + return 3; + case 'block-device': + return 4; + case 'directory': + return 5; + case 'fifo': + return 6; + case 'contiguous-file': + return 7; + } + + return 0; } function encodeHeader(header: Header): Buffer { - const buf = Buffer.alloc(512); - - let name = header.name; - let prefix = ''; - - if (Buffer.byteLength(name) !== name.length) { - throw new Error( - 'utf-8 filename is only supported in PAX, we only support USTAR', - ); - } - - // If a filename is over 100 characters then split it up if possible (requires a directory) - while (Buffer.byteLength(name) > 100) { - const i = name.indexOf('/'); - if (i === -1) { - throw new Error( - 'filename is too long for USTAR and it was in no directory', - ); - } - - prefix += prefix ? `/${name.slice(0, i)}` : name.slice(0, i); - name = name.slice(i + 1); - } - - if (Buffer.byteLength(name) > 100) { - throw new Error('filename is too long for USTAR'); - } - - if (Buffer.byteLength(prefix) > 155) { - throw new Error('prefix is too long for USTAR'); - } - - if (header.linkname !== undefined && Buffer.byteLength(header.linkname) > 100) { - throw new Error('linkname is too long for USTAR'); - } - - buf.write(name); - buf.write(encodeOct(header.mode & MASK, 6), 100); - buf.write(encodeOct(header.uid, 6), 108); - buf.write(encodeOct(header.gid, 6), 116); - buf.write(encodeOct(header.size, 11), 124); - buf.write(encodeOct(header.mtime.getTime() / 1_000 | 0, 11), 136); - - buf[156] = ZERO_OFFSET + toTypeflag(header.type); - - if (header.linkname !== undefined) { - buf.write(header.linkname, 157); - } - - buf.write(USTAR, 257); - if (header.uname !== undefined) { - buf.write(header.uname, 265); - } - if (header.gname !== undefined) { - buf.write(header.gname, 297); - } - buf.write(encodeOct(header.devmajor || 0, 6), 329); - buf.write(encodeOct(header.devminor || 0, 6), 337); - - if (prefix !== '') { - buf.write(prefix, 345); - } - - buf.write(encodeOct(checksum(buf), 6), 148); - - return buf; + const buf = Buffer.alloc(512); + + let name = header.name; + let prefix = ''; + + if (Buffer.byteLength(name) !== name.length) { + throw new Error( + 'utf-8 filename is only supported in PAX, we only support USTAR', + ); + } + + // If a filename is over 100 characters then split it up if possible (requires a directory) + while (Buffer.byteLength(name) > 100) { + const i = name.indexOf('/'); + if (i === -1) { + throw new Error('filename is too long for USTAR and it was in no directory'); + } + + prefix += prefix ? `/${name.slice(0, i)}` : name.slice(0, i); + name = name.slice(i + 1); + } + + if (Buffer.byteLength(name) > 100) { + throw new Error('filename is too long for USTAR'); + } + + if (Buffer.byteLength(prefix) > 155) { + throw new Error('prefix is too long for USTAR'); + } + + if (header.linkname !== undefined && Buffer.byteLength(header.linkname) > 100) { + throw new Error('linkname is too long for USTAR'); + } + + buf.write(name); + buf.write(encodeOct(header.mode & MASK, 6), 100); + buf.write(encodeOct(header.uid, 6), 108); + buf.write(encodeOct(header.gid, 6), 116); + buf.write(encodeOct(header.size, 11), 124); + buf.write(encodeOct(header.mtime.getTime() / 1_000 | 0, 11), 136); + + buf[156] = ZERO_OFFSET + toTypeflag(header.type); + + if (header.linkname !== undefined) { + buf.write(header.linkname, 157); + } + + buf.write(USTAR, 257); + if (header.uname !== undefined) { + buf.write(header.uname, 265); + } + if (header.gname !== undefined) { + buf.write(header.gname, 297); + } + buf.write(encodeOct(header.devmajor || 0, 6), 329); + buf.write(encodeOct(header.devminor || 0, 6), 337); + + if (prefix !== '') { + buf.write(prefix, 345); + } + + buf.write(encodeOct(checksum(buf), 6), 148); + + return buf; } export class TarWriter { - constructor(stream: stream.Writable) { - this.finalized = false; - this.stream = stream; - } - - stream: stream.Writable; - finalized: boolean; - - static normalizeHeader(partial: PartialHeader, size: number): Header { - let mode = partial.mode; - if (mode === undefined) { - if (partial.type === 'directory') { - mode = DMODE; - } else { - mode = FMODE; - } - } - - return { - name: partial.name, - size, - mode, - mtime: partial.mtime === undefined ? new Date() : partial.mtime, - type: partial.type === undefined ? 'file' : partial.type, - linkname: partial.linkname, - uid: partial.uid === undefined ? 0 : partial.uid, - gid: partial.gid === undefined ? 0 : partial.gid, - uname: partial.uname, - gname: partial.gname, - devmajor: partial.devmajor === undefined ? 0 : partial.devmajor, - devminor: partial.devminor === undefined ? 0 : partial.devminor, - }; - } - - overflow(size: number) { - size &= 511; - if (size > 0) { - this.stream.write(END_OF_TAR.slice(0, 512 - size)); - } - } - - append(rawHeader: PartialHeader, rawBuffer: string | Buffer) { - if (this.finalized) { - throw new Error('Already finalized file'); - } - - const buffer: Buffer = - rawBuffer instanceof Buffer ? rawBuffer : Buffer.from(rawBuffer); - const header = TarWriter.normalizeHeader(rawHeader, buffer.length); - - this.stream.write(encodeHeader(header)); - this.stream.write(buffer); - this.overflow(header.size); - } - - finalize(): Promise { - this.finalized = true; - - return new Promise((resolve, reject) => { - const {stream} = this; - - stream.on( - 'close', - () => { - resolve(); - }, - ); - - stream.on( - 'error', - (err) => { - reject(err); - }, - ); - - stream.write(END_OF_TAR); - stream.end(); - }); - } + constructor(stream: stream.Writable) { + this.finalized = false; + this.stream = stream; + } + + stream: stream.Writable; + finalized: boolean; + + static normalizeHeader(partial: PartialHeader, size: number): Header { + let mode = partial.mode; + if (mode === undefined) { + if (partial.type === 'directory') { + mode = DMODE; + } else { + mode = FMODE; + } + } + + return { + name: partial.name, + size, + mode, + mtime: partial.mtime === undefined ? new Date() : partial.mtime, + type: partial.type === undefined ? 'file' : partial.type, + linkname: partial.linkname, + uid: partial.uid === undefined ? 0 : partial.uid, + gid: partial.gid === undefined ? 0 : partial.gid, + uname: partial.uname, + gname: partial.gname, + devmajor: partial.devmajor === undefined ? 0 : partial.devmajor, + devminor: partial.devminor === undefined ? 0 : partial.devminor, + }; + } + + overflow(size: number) { + size &= 511; + if (size > 0) { + this.stream.write(END_OF_TAR.slice(0, 512 - size)); + } + } + + append(rawHeader: PartialHeader, rawBuffer: string | Buffer) { + if (this.finalized) { + throw new Error('Already finalized file'); + } + + const buffer: Buffer = + rawBuffer instanceof Buffer ? rawBuffer : Buffer.from(rawBuffer); + const header = TarWriter.normalizeHeader(rawHeader, buffer.length); + + this.stream.write(encodeHeader(header)); + this.stream.write(buffer); + this.overflow(header.size); + } + + finalize(): Promise { + this.finalized = true; + + return new Promise((resolve, reject) => { + const {stream} = this; + + stream.on( + 'close', + () => { + resolve(); + }, + ); + + stream.on( + 'error', + (err) => { + reject(err); + }, + ); + + stream.write(END_OF_TAR); + stream.end(); + }); + } } diff --git a/packages/@romejs/codec-url/index.ts b/packages/@romejs/codec-url/index.ts index cd7ee325879..64114ad7949 100644 --- a/packages/@romejs/codec-url/index.ts +++ b/packages/@romejs/codec-url/index.ts @@ -11,39 +11,39 @@ import url = require('url'); import {ob1Coerce0, ob1Number0, ob1Number1} from '@romejs/ob1'; export type ConsumableUrl = { - path: Consumer; - query: Consumer; + path: Consumer; + query: Consumer; }; export function consumeUrl(rawUrl: string): ConsumableUrl { - const parts = url.parse(rawUrl, true); + const parts = url.parse(rawUrl, true); - const query = consumeUnknown({...parts.query}, 'parse/url/query'); + const query = consumeUnknown({...parts.query}, 'parse/url/query'); - const path = consume({ - value: parts.pathname, - context: { - category: 'parse/url', - getDiagnosticPointer() { - return { - language: 'url', - mtime: undefined, - sourceText: rawUrl, - filename: 'url', - start: { - index: ob1Number0, - line: ob1Number1, - column: ob1Number0, - }, - end: { - index: ob1Coerce0(rawUrl.length - 1), - line: ob1Number1, - column: ob1Coerce0(rawUrl.length - 1), - }, - }; - }, - }, - }); + const path = consume({ + value: parts.pathname, + context: { + category: 'parse/url', + getDiagnosticPointer() { + return { + language: 'url', + mtime: undefined, + sourceText: rawUrl, + filename: 'url', + start: { + index: ob1Number0, + line: ob1Number1, + column: ob1Number0, + }, + end: { + index: ob1Coerce0(rawUrl.length - 1), + line: ob1Number1, + column: ob1Coerce0(rawUrl.length - 1), + }, + }; + }, + }, + }); - return {query, path}; + return {query, path}; } diff --git a/packages/@romejs/codec-websocket/frame.ts b/packages/@romejs/codec-websocket/frame.ts index 7031109598f..5e68c4dfc02 100644 --- a/packages/@romejs/codec-websocket/frame.ts +++ b/packages/@romejs/codec-websocket/frame.ts @@ -10,130 +10,128 @@ import {BuildFrameOpts, Frame} from './types'; import crypto = require('crypto'); export function isCompleteFrame(frame: Frame): boolean { - return Buffer.byteLength(frame.payload) >= frame.payloadLength; + return Buffer.byteLength(frame.payload) >= frame.payloadLength; } export function unmaskPayload( - payload: Buffer, - mask: undefined | Buffer, - offset: number, + payload: Buffer, + mask: undefined | Buffer, + offset: number, ) { - if (mask === undefined) { - return payload; - } + if (mask === undefined) { + return payload; + } - for (let i = 0; i < payload.length; i++) { - payload[i] ^= mask[offset + i & 3]; - } + for (let i = 0; i < payload.length; i++) { + payload[i] ^= mask[offset + i & 3]; + } - return payload; + return payload; } export function buildFrame(opts: BuildFrameOpts, shouldMask: boolean): Buffer { - const {opcode, fin, data} = opts; - - let offset = shouldMask ? 6 : 2; - let dataLength = data.length; - - if (dataLength >= 65_536) { - offset += 8; - dataLength = 127; - } else if (dataLength > 125) { - offset += 2; - dataLength = 126; - } - - const head = Buffer.allocUnsafe(offset); - - head[0] = fin ? opcode | 128 : opcode; - head[1] = dataLength; - - if (dataLength === 126) { - head.writeUInt16BE(data.length, 2); - } else if (dataLength === 127) { - head.writeUInt32BE(0, 2); - head.writeUInt32BE(data.length, 6); - } - - if (shouldMask) { - const mask = crypto.randomBytes(4); - head[1] |= 128; - head[offset - 4] = mask[0]; - head[offset - 3] = mask[1]; - head[offset - 2] = mask[2]; - head[offset - 1] = mask[3]; - - const masked = Buffer.alloc(dataLength); - for (let i = 0; i < dataLength; ++i) { - masked[i] = data[i] ^ mask[i & 3]; - } - - return Buffer.concat([head, masked]); - } else { - return Buffer.concat([head, data]); - } + const {opcode, fin, data} = opts; + + let offset = shouldMask ? 6 : 2; + let dataLength = data.length; + + if (dataLength >= 65_536) { + offset += 8; + dataLength = 127; + } else if (dataLength > 125) { + offset += 2; + dataLength = 126; + } + + const head = Buffer.allocUnsafe(offset); + + head[0] = fin ? opcode | 128 : opcode; + head[1] = dataLength; + + if (dataLength === 126) { + head.writeUInt16BE(data.length, 2); + } else if (dataLength === 127) { + head.writeUInt32BE(0, 2); + head.writeUInt32BE(data.length, 6); + } + + if (shouldMask) { + const mask = crypto.randomBytes(4); + head[1] |= 128; + head[offset - 4] = mask[0]; + head[offset - 3] = mask[1]; + head[offset - 2] = mask[2]; + head[offset - 1] = mask[3]; + + const masked = Buffer.alloc(dataLength); + for (let i = 0; i < dataLength; ++i) { + masked[i] = data[i] ^ mask[i & 3]; + } + + return Buffer.concat([head, masked]); + } else { + return Buffer.concat([head, data]); + } } export function parseFrame(buffer: Buffer): Frame { - const firstByte = buffer.readUInt8(0); - const isFinalFrame: boolean = Boolean(firstByte >>> 7 & 1); - const opcode: number = firstByte & 15; - - const [reserved1, reserved2, reserved3] = [ - (firstByte >>> 6 & 1) === 1, - (firstByte >>> 5 & 1) === 1, - (firstByte >>> 4 & 1) === 1, - ]; - reserved1; - reserved2; - reserved3; - - const secondByte: number = buffer.readUInt8(1); - const isMasked: boolean = Boolean(secondByte >>> 7 & 1); - - // Keep track of our current position as we advance through the buffer - let currentOffset = 2; - let payloadLength = secondByte & 127; - if (payloadLength > 125) { - if (payloadLength === 126) { - payloadLength = buffer.readUInt16BE(currentOffset); - currentOffset += 2; - } else if (payloadLength === 127) { - const leftPart = buffer.readUInt32BE(currentOffset); - currentOffset += 4; - - // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned - - // if payload length is greater than this number. - if (leftPart >= Number.MAX_SAFE_INTEGER) { - throw new Error( - 'Unsupported WebSocket frame: payload length > 2^53 - 1', - ); - } - - const rightPart = buffer.readUInt32BE(currentOffset); - currentOffset += 4; - - payloadLength = leftPart * Math.pow(2, 32) + rightPart; - } else { - throw new Error('Unknown payload length'); - } - } - - // Get the masking key if one exists - let mask; - if (isMasked) { - mask = buffer.slice(currentOffset, currentOffset + 4); - currentOffset += 4; - } - - let payload = unmaskPayload(buffer.slice(currentOffset), mask, 0); - - return { - fin: isFinalFrame, - opcode, - mask, - payload, - payloadLength, - }; + const firstByte = buffer.readUInt8(0); + const isFinalFrame: boolean = Boolean(firstByte >>> 7 & 1); + const opcode: number = firstByte & 15; + + const [reserved1, reserved2, reserved3] = [ + (firstByte >>> 6 & 1) === 1, + (firstByte >>> 5 & 1) === 1, + (firstByte >>> 4 & 1) === 1, + ]; + reserved1; + reserved2; + reserved3; + + const secondByte: number = buffer.readUInt8(1); + const isMasked: boolean = Boolean(secondByte >>> 7 & 1); + + // Keep track of our current position as we advance through the buffer + let currentOffset = 2; + let payloadLength = secondByte & 127; + if (payloadLength > 125) { + if (payloadLength === 126) { + payloadLength = buffer.readUInt16BE(currentOffset); + currentOffset += 2; + } else if (payloadLength === 127) { + const leftPart = buffer.readUInt32BE(currentOffset); + currentOffset += 4; + + // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned + + // if payload length is greater than this number. + if (leftPart >= Number.MAX_SAFE_INTEGER) { + throw new Error('Unsupported WebSocket frame: payload length > 2^53 - 1'); + } + + const rightPart = buffer.readUInt32BE(currentOffset); + currentOffset += 4; + + payloadLength = leftPart * Math.pow(2, 32) + rightPart; + } else { + throw new Error('Unknown payload length'); + } + } + + // Get the masking key if one exists + let mask; + if (isMasked) { + mask = buffer.slice(currentOffset, currentOffset + 4); + currentOffset += 4; + } + + let payload = unmaskPayload(buffer.slice(currentOffset), mask, 0); + + return { + fin: isFinalFrame, + opcode, + mask, + payload, + payloadLength, + }; } diff --git a/packages/@romejs/codec-websocket/index.ts b/packages/@romejs/codec-websocket/index.ts index 3e743a741ee..d4eb1673674 100644 --- a/packages/@romejs/codec-websocket/index.ts +++ b/packages/@romejs/codec-websocket/index.ts @@ -19,268 +19,268 @@ import net = require('net'); import {Reporter} from '@romejs/cli-reporter'; export function createKey(key: string): string { - return crypto.createHash('sha1').update(`${key}${GUID}`).digest('base64'); + return crypto.createHash('sha1').update(`${key}${GUID}`).digest('base64'); } type WebSocketType = 'client' | 'server'; export class WebSocketInterface { - constructor(type: WebSocketType, socket: net.Socket, reporter?: Reporter) { - // When a frame is set here then any additional continuation frames payloads will be appended - this.unfinishedFrame = undefined; - - // When a frame is set here, all additional chunks will be appended until we reach the correct payloadLength - this.incompleteFrame = undefined; - - this.reporter = reporter; - this.socket = socket; - this.alive = true; - this.type = type; - - this.completeFrameEvent = new Event({name: 'WebSocketInterface.message'}); - this.errorEvent = new Event({name: 'WebSocketInterface.error'}); - this.endEvent = new Event({name: 'WebSocketInterface.end', serial: true}); - - socket.on( - 'data', - (buff) => { - this.addBuffer(buff); - }, - ); - - socket.on( - 'error', - (err: NodeJS.ErrnoException) => { - if (err.code === 'ECONNRESET') { - this.endEvent.send(); - } else { - this.errorEvent.send(err); - } - }, - ); - - socket.on( - 'close', - () => { - this.end(); - }, - ); - } - - alive: boolean; - type: WebSocketType; - incompleteFrame: undefined | Frame; - unfinishedFrame: undefined | Frame; - socket: net.Socket; - reporter: undefined | Reporter; - - completeFrameEvent: Event; - errorEvent: Event; - endEvent: Event; - - end() { - if (!this.alive) { - return; - } - - this.alive = false; - this.endEvent.send(); - this.socket.end(); - } - - send(buff: string | Buffer) { - if (typeof buff === 'string') { - this.sendFrame({ - opcode: OPCODES.TEXT, - fin: true, - data: Buffer.from(buff), - }); - } else if (buff instanceof Buffer) { - this.sendFrame({ - opcode: OPCODES.BINARY, - fin: true, - data: buff, - }); - } else { - throw new Error("Don't know how to send this"); - } - } - - sendJSON(val: unknown) { - this.send(String(JSON.stringify(val))); - } - - sendFrame(frameOpts: BuildFrameOpts) { - if (this.reporter !== undefined) { - this.reporter.info( - 'Sending frame', - { - fin: frameOpts.fin, - opcode: frameOpts.opcode, - msg: frameOpts.data, - }, - ); - } - this.socket.write(buildFrame(frameOpts, this.type === 'client')); - } - - completeFrame(frame: Frame): void { - // If we have an unfinished frame then only allow continuations - const {unfinishedFrame} = this; - if (unfinishedFrame !== undefined) { - if (frame.opcode === OPCODES.CONTINUATION) { - unfinishedFrame.payload = Buffer.concat([ - unfinishedFrame.payload, - unmaskPayload( - frame.payload, - unfinishedFrame.mask, - unfinishedFrame.payload.length, - ), - ]); - - if (frame.fin) { - this.unfinishedFrame = undefined; - this.completeFrame(unfinishedFrame); - } - return; - } else { - // Silently ignore the previous frame... - this.unfinishedFrame = undefined; - /*throw new Error( + constructor(type: WebSocketType, socket: net.Socket, reporter?: Reporter) { + // When a frame is set here then any additional continuation frames payloads will be appended + this.unfinishedFrame = undefined; + + // When a frame is set here, all additional chunks will be appended until we reach the correct payloadLength + this.incompleteFrame = undefined; + + this.reporter = reporter; + this.socket = socket; + this.alive = true; + this.type = type; + + this.completeFrameEvent = new Event({name: 'WebSocketInterface.message'}); + this.errorEvent = new Event({name: 'WebSocketInterface.error'}); + this.endEvent = new Event({name: 'WebSocketInterface.end', serial: true}); + + socket.on( + 'data', + (buff) => { + this.addBuffer(buff); + }, + ); + + socket.on( + 'error', + (err: NodeJS.ErrnoException) => { + if (err.code === 'ECONNRESET') { + this.endEvent.send(); + } else { + this.errorEvent.send(err); + } + }, + ); + + socket.on( + 'close', + () => { + this.end(); + }, + ); + } + + alive: boolean; + type: WebSocketType; + incompleteFrame: undefined | Frame; + unfinishedFrame: undefined | Frame; + socket: net.Socket; + reporter: undefined | Reporter; + + completeFrameEvent: Event; + errorEvent: Event; + endEvent: Event; + + end() { + if (!this.alive) { + return; + } + + this.alive = false; + this.endEvent.send(); + this.socket.end(); + } + + send(buff: string | Buffer) { + if (typeof buff === 'string') { + this.sendFrame({ + opcode: OPCODES.TEXT, + fin: true, + data: Buffer.from(buff), + }); + } else if (buff instanceof Buffer) { + this.sendFrame({ + opcode: OPCODES.BINARY, + fin: true, + data: buff, + }); + } else { + throw new Error("Don't know how to send this"); + } + } + + sendJSON(val: unknown) { + this.send(String(JSON.stringify(val))); + } + + sendFrame(frameOpts: BuildFrameOpts) { + if (this.reporter !== undefined) { + this.reporter.info( + 'Sending frame', + { + fin: frameOpts.fin, + opcode: frameOpts.opcode, + msg: frameOpts.data, + }, + ); + } + this.socket.write(buildFrame(frameOpts, this.type === 'client')); + } + + completeFrame(frame: Frame): void { + // If we have an unfinished frame then only allow continuations + const {unfinishedFrame} = this; + if (unfinishedFrame !== undefined) { + if (frame.opcode === OPCODES.CONTINUATION) { + unfinishedFrame.payload = Buffer.concat([ + unfinishedFrame.payload, + unmaskPayload( + frame.payload, + unfinishedFrame.mask, + unfinishedFrame.payload.length, + ), + ]); + + if (frame.fin) { + this.unfinishedFrame = undefined; + this.completeFrame(unfinishedFrame); + } + return; + } else { + // Silently ignore the previous frame... + this.unfinishedFrame = undefined; + /*throw new Error( `We're waiting for a frame to finish so only allow continuation frames. Received frame: ${JSON.stringify( frame, )} Unfinished frame: ${JSON.stringify(unfinishedFrame)}`, );*/ - } - } - - if (frame.fin) { - if (frame.opcode === OPCODES.PING) { - this.sendFrame({ - opcode: OPCODES.PONG, - fin: true, - data: frame.payload, - }); - } else { - // Trim off any excess payload - let excess; - if (frame.payload.length > frame.payloadLength) { - excess = frame.payload.slice(frame.payloadLength); - frame.payload = frame.payload.slice(0, frame.payloadLength); - } - - if (this.reporter !== undefined) { - this.reporter.info( - 'Received complete frame', - { - opcode: frame.opcode, - length: frame.payloadLength, - msg: frame.payload, - }, - ); - } - - this.completeFrameEvent.send(frame); - - if (excess !== undefined) { - this.addBuffer(excess); - } - } - } else { - this.unfinishedFrame = frame; - } - } - - addBufferToIncompleteFrame(incompleteFrame: Frame, buff: Buffer) { - incompleteFrame.payload = Buffer.concat([ - incompleteFrame.payload, - unmaskPayload(buff, incompleteFrame.mask, incompleteFrame.payload.length), - ]); - - if (isCompleteFrame(incompleteFrame)) { - this.incompleteFrame = undefined; - this.completeFrame(incompleteFrame); - } - } - - addBuffer(buff: Buffer): void { - // Check if we're still waiting for the rest of a payload - const {incompleteFrame} = this; - if (incompleteFrame !== undefined) { - this.addBufferToIncompleteFrame(incompleteFrame, buff); - return; - } - - const frame = parseFrame(buff); - - if (isCompleteFrame(frame)) { - // Frame has been completed! - this.completeFrame(frame); - } else { - this.incompleteFrame = frame; - } - } + } + } + + if (frame.fin) { + if (frame.opcode === OPCODES.PING) { + this.sendFrame({ + opcode: OPCODES.PONG, + fin: true, + data: frame.payload, + }); + } else { + // Trim off any excess payload + let excess; + if (frame.payload.length > frame.payloadLength) { + excess = frame.payload.slice(frame.payloadLength); + frame.payload = frame.payload.slice(0, frame.payloadLength); + } + + if (this.reporter !== undefined) { + this.reporter.info( + 'Received complete frame', + { + opcode: frame.opcode, + length: frame.payloadLength, + msg: frame.payload, + }, + ); + } + + this.completeFrameEvent.send(frame); + + if (excess !== undefined) { + this.addBuffer(excess); + } + } + } else { + this.unfinishedFrame = frame; + } + } + + addBufferToIncompleteFrame(incompleteFrame: Frame, buff: Buffer) { + incompleteFrame.payload = Buffer.concat([ + incompleteFrame.payload, + unmaskPayload(buff, incompleteFrame.mask, incompleteFrame.payload.length), + ]); + + if (isCompleteFrame(incompleteFrame)) { + this.incompleteFrame = undefined; + this.completeFrame(incompleteFrame); + } + } + + addBuffer(buff: Buffer): void { + // Check if we're still waiting for the rest of a payload + const {incompleteFrame} = this; + if (incompleteFrame !== undefined) { + this.addBufferToIncompleteFrame(incompleteFrame, buff); + return; + } + + const frame = parseFrame(buff); + + if (isCompleteFrame(frame)) { + // Frame has been completed! + this.completeFrame(frame); + } else { + this.incompleteFrame = frame; + } + } } export async function createClient(rawUrl: string): Promise { - const parts = url.parse(rawUrl); - - return new Promise((resolve, reject) => { - const key = crypto.randomBytes(16).toString('base64'); - const digest = createKey(key); - - const req = http.request({ - hostname: parts.hostname, - port: parts.port, - path: parts.path, - method: 'GET', - headers: { - Connection: 'Upgrade', - Upgrade: 'websocket', - 'Sec-WebSocket-Key': key, - 'Sec-WebSocket-Version': '13', - }, - }); - - req.on( - 'response', - (res) => { - if (res.statusCode && res.statusCode >= 400) { - process.stderr.write(`Unexpected HTTP code: ${res.statusCode}\n`); - res.pipe(process.stderr); - } else { - res.pipe(process.stderr); - } - }, - ); - - req.on( - 'upgrade', - (res, socket, head) => { - if (res.headers['sec-websocket-accept'] !== digest) { - socket.end(); - reject( - new Error( - `Digest mismatch ${digest} !== ${res.headers['sec-websocket-accept']}`, - ), - ); - return; - } - - const client = new WebSocketInterface('client', socket); - //client.addBuffer(head); - head; - resolve(client); - }, - ); - - req.on( - 'error', - (err) => { - reject(err); - }, - ); - - req.end(); - }); + const parts = url.parse(rawUrl); + + return new Promise((resolve, reject) => { + const key = crypto.randomBytes(16).toString('base64'); + const digest = createKey(key); + + const req = http.request({ + hostname: parts.hostname, + port: parts.port, + path: parts.path, + method: 'GET', + headers: { + Connection: 'Upgrade', + Upgrade: 'websocket', + 'Sec-WebSocket-Key': key, + 'Sec-WebSocket-Version': '13', + }, + }); + + req.on( + 'response', + (res) => { + if (res.statusCode && res.statusCode >= 400) { + process.stderr.write(`Unexpected HTTP code: ${res.statusCode}\n`); + res.pipe(process.stderr); + } else { + res.pipe(process.stderr); + } + }, + ); + + req.on( + 'upgrade', + (res, socket, head) => { + if (res.headers['sec-websocket-accept'] !== digest) { + socket.end(); + reject( + new Error( + `Digest mismatch ${digest} !== ${res.headers['sec-websocket-accept']}`, + ), + ); + return; + } + + const client = new WebSocketInterface('client', socket); + //client.addBuffer(head); + head; + resolve(client); + }, + ); + + req.on( + 'error', + (err) => { + reject(err); + }, + ); + + req.end(); + }); } diff --git a/packages/@romejs/codec-websocket/types.ts b/packages/@romejs/codec-websocket/types.ts index f558a79d536..04aacf1ea3e 100644 --- a/packages/@romejs/codec-websocket/types.ts +++ b/packages/@romejs/codec-websocket/types.ts @@ -6,26 +6,26 @@ */ export type BuildFrameOpts = { - opcode: number; - fin: boolean; - data: Buffer; + opcode: number; + fin: boolean; + data: Buffer; }; export type Frame = { - fin: boolean; - opcode: number; - mask: undefined | Buffer; - payload: Buffer; - payloadLength: number; + fin: boolean; + opcode: number; + mask: undefined | Buffer; + payload: Buffer; + payloadLength: number; }; export const OPCODES = { - CONTINUATION: 0, - TEXT: 1, - BINARY: 2, - TERMINATE: 8, - PING: 9, - PONG: 10, + CONTINUATION: 0, + TEXT: 1, + BINARY: 2, + TERMINATE: 8, + PING: 9, + PONG: 10, }; export const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; diff --git a/packages/@romejs/consume/Consumer.ts b/packages/@romejs/consume/Consumer.ts index 0fb31d99e27..a1c7f8ceda8 100644 --- a/packages/@romejs/consume/Consumer.ts +++ b/packages/@romejs/consume/Consumer.ts @@ -6,1203 +6,1198 @@ */ import { - Diagnostic, - DiagnosticAdvice, - DiagnosticDescriptionOptionalCategory, - DiagnosticLocation, - Diagnostics, - DiagnosticsError, - catchDiagnosticsSync, - createBlessedDiagnosticMessage, - createSingleDiagnosticError, - descriptions, + Diagnostic, + DiagnosticAdvice, + DiagnosticDescriptionOptionalCategory, + DiagnosticLocation, + Diagnostics, + DiagnosticsError, + catchDiagnosticsSync, + createBlessedDiagnosticMessage, + createSingleDiagnosticError, + descriptions, } from '@romejs/diagnostics'; import {UnknownObject, isPlainObject} from '@romejs/typescript-helpers'; import { - JSONArray, - JSONObject, - JSONPropertyValue, - JSONValue, + JSONArray, + JSONObject, + JSONPropertyValue, + JSONValue, } from '@romejs/codec-json'; import { - ConsumeContext, - ConsumeKey, - ConsumePath, - ConsumePropertyDefinition, - ConsumePropertyMetadata, - ConsumePropertyNumberDefinition, - ConsumePropertyPrimitiveDefinition, - ConsumePropertyStringDefinition, - ConsumeSourceLocationRequestTarget, - ConsumerHandleUnexpected, - ConsumerOnDefinition, - ConsumerOptions, + ConsumeContext, + ConsumeKey, + ConsumePath, + ConsumePropertyDefinition, + ConsumePropertyMetadata, + ConsumePropertyNumberDefinition, + ConsumePropertyPrimitiveDefinition, + ConsumePropertyStringDefinition, + ConsumeSourceLocationRequestTarget, + ConsumerHandleUnexpected, + ConsumerOnDefinition, + ConsumerOptions, } from './types'; import {SourceLocation, UNKNOWN_POSITION} from '@romejs/parser-core'; import { - Number0, - Number1, - UnknownNumber, - ob1Add, - ob1Coerce0, - ob1Coerce1, - ob1Get, + Number0, + Number1, + UnknownNumber, + ob1Add, + ob1Coerce0, + ob1Coerce1, + ob1Get, } from '@romejs/ob1'; import {isValidIdentifierName} from '@romejs/js-ast-utils'; import {escapeString} from '@romejs/string-escape'; import { - AbsoluteFilePath, - RelativeFilePath, - URLFilePath, - UnknownFilePath, - createAbsoluteFilePath, - createURLFilePath, - createUnknownFilePath, + AbsoluteFilePath, + RelativeFilePath, + URLFilePath, + UnknownFilePath, + createAbsoluteFilePath, + createURLFilePath, + createUnknownFilePath, } from '@romejs/path'; type UnexpectedConsumerOptions = { - loc?: SourceLocation; - target?: ConsumeSourceLocationRequestTarget; - at?: 'suffix' | 'prefix' | 'none'; - atParent?: boolean; + loc?: SourceLocation; + target?: ConsumeSourceLocationRequestTarget; + at?: 'suffix' | 'prefix' | 'none'; + atParent?: boolean; }; function isComputedPart(part: ConsumeKey): boolean { - return typeof part === 'number' || !isValidIdentifierName(part); + return typeof part === 'number' || !isValidIdentifierName(part); } export default class Consumer { - constructor(opts: ConsumerOptions) { - this.path = opts.filePath; - this.filename = this.path === undefined ? undefined : this.path.join(); - - this.value = opts.value; - this.parent = opts.parent; - this.keyPath = opts.objectPath; - this.context = opts.context; - this.onDefinition = opts.onDefinition; - this.propertyMetadata = opts.propertyMetadata; - this.usedNames = new Set(opts.usedNames); - this.forkCache = new Map(); - this.forceDiagnosticTarget = opts.forceDiagnosticTarget; - this.declared = false; - - // See shouldDispatchUnexpected for explanation - this.hasHandledUnexpected = false; - this.handleUnexpected = opts.handleUnexpectedDiagnostic; - } - - path: undefined | UnknownFilePath; - filename: undefined | string; - declared: boolean; - handleUnexpected: undefined | ConsumerHandleUnexpected; - onDefinition: undefined | ConsumerOnDefinition; - propertyMetadata: undefined | ConsumePropertyMetadata; - parent: undefined | Consumer; - value: unknown; - context: ConsumeContext; - keyPath: ConsumePath; - usedNames: Set; - forkCache: Map; - hasHandledUnexpected: boolean; - forceDiagnosticTarget: undefined | ConsumeSourceLocationRequestTarget; - - capture(): { - consumer: Consumer; - definitions: Array; - diagnostics: Diagnostics; - } { - let diagnostics: Diagnostics = []; - const definitions: Array = []; - - const consumer = this.clone({ - onDefinition: (def, consumer) => { - if (this.onDefinition !== undefined) { - this.onDefinition(def, consumer); - } - - definitions.push(def); - }, - handleUnexpectedDiagnostic(diag) { - diagnostics.push(diag); - }, - }); - return {consumer, definitions, diagnostics}; - } - - async bufferDiagnostics( - callback: (consumer: Consumer) => Promise | T, - ): Promise { - const {diagnostics, consumer} = await this.capture(); - const result = await callback(consumer); - if (result === undefined || diagnostics.length > 0) { - throw new DiagnosticsError('Captured diagnostics', diagnostics); - } - return result; - } - - handleThrownDiagnostics(callback: () => void) { - if (this.handleUnexpected === undefined) { - callback(); - } else { - const {diagnostics} = catchDiagnosticsSync(callback); - - if (diagnostics !== undefined) { - for (const diag of diagnostics) { - this.handleUnexpected(diag); - } - } - } - } - - declareDefinition( - partialDef: - | Omit - | Omit - | Omit, - inputName?: string, - ) { - if (this.declared) { - return; - } - - if (this.onDefinition === undefined) { - return; - } - - const metadata: ConsumePropertyMetadata = { - inputName, - ...this.propertyMetadata, - }; - - const def: ConsumePropertyDefinition = { - ...partialDef, - objectPath: this.keyPath, - metadata, - }; - - this.declared = true; - - this.onDefinition(def, this); - } - - getDiagnosticLocation( - target: ConsumeSourceLocationRequestTarget = 'all', - ): DiagnosticLocation { - const {getDiagnosticPointer} = this.context; - if (getDiagnosticPointer === undefined) { - return {}; - } - - const {forceDiagnosticTarget} = this; - if (forceDiagnosticTarget !== undefined) { - target = forceDiagnosticTarget; - } - return getDiagnosticPointer(this.keyPath, target); - } - - getLocation(target?: ConsumeSourceLocationRequestTarget): SourceLocation { - const location = this.getDiagnosticLocation(target); - if ( - location === undefined || - location.start === undefined || - location.end === undefined - ) { - return { - filename: this.filename, - start: UNKNOWN_POSITION, - end: UNKNOWN_POSITION, - }; - } else { - return { - filename: location.filename, - start: location.start, - end: location.end, - }; - } - } - - getLocationRange( - startIndex: Number0, - endIndex: Number0 = startIndex, - target?: ConsumeSourceLocationRequestTarget, - ): SourceLocation { - const loc = this.getLocation(target); - if (loc.start === UNKNOWN_POSITION) { - return loc; - } - - const {start, end} = loc; - - // We don't support handling line differences here... yet? - if (start.line !== end.line) { - return loc; - } - - return { - ...loc, - start: { - ...start, - column: ob1Add(start.column, startIndex), - index: ob1Add(start.index, startIndex), - }, - end: { - ...start, - column: ob1Add(start.column, endIndex), - index: ob1Add(start.index, endIndex), - }, - }; - } - - getKey(): Consumer { - return this.clone({ - forceDiagnosticTarget: 'key', - value: this.getParentKey(), - }); - } - - getParentKey(): ConsumeKey { - return this.keyPath[this.keyPath.length - 1]; - } - - hasChangedFromSource(): boolean { - const {getOriginalValue} = this.context; - if (getOriginalValue === undefined) { - return false; - } - - const originalValue = getOriginalValue(this.keyPath); - return !this.wasInSource() || this.value !== originalValue; - } - - wasInSource() { - return this.getDiagnosticLocation() !== undefined; - } - - getKeyPathString(path: ConsumePath = this.keyPath): string { - const {normalizeKey} = this.context; - let str = ''; - - for (let i = 0; i < path.length; i++) { - let part = path[i]; - const nextPart = path[i + 1]; - - if (typeof part === 'string' && normalizeKey !== undefined) { - part = normalizeKey(part); - } - - // If we are a computed property then wrap in brackets, the previous part would not have inserted a dot - // We allow a computed part at the beginning of a path - if (isComputedPart(part) && i > 0) { - const inner = - typeof part === 'number' - ? String(part) - : escapeString( - part, - { - quote: "'", - }, - ); - - str += `[${inner}]`; - } else { - if (nextPart === undefined || isComputedPart(nextPart)) { - // Don't append a dot if there are no parts or the next is computed - str += part; - } else { - str += `${part}.`; - } - } - } - - return str; - } - - generateUnexpectedMessage( - msg: string, - opts: UnexpectedConsumerOptions, - ): string { - const {at = 'suffix', atParent = false} = opts; - const {parent} = this; - - let target: Consumer = this; - - if (atParent) { - if (parent === undefined) { - // Cannot target the parent if it does not exist - return msg; - } else { - target = parent; - } - } - - if (at === 'suffix') { - msg += ` at ${target.getKeyPathString()}`; - } else { - msg = `${target.getKeyPathString()} ${msg}`; - } - - return msg; - } - - unexpected( - description: DiagnosticDescriptionOptionalCategory = descriptions.CONSUME.INVALID, - opts: UnexpectedConsumerOptions = {}, - ): DiagnosticsError { - const {target = 'value'} = opts; - - const {filename} = this; - let location = this.getDiagnosticLocation(target); - const fromSource = location !== undefined; - - const message = this.generateUnexpectedMessage( - description.message.value, - opts, - ); - description = { - ...description, - message: createBlessedDiagnosticMessage(message), - }; - - const advice: DiagnosticAdvice = [...(description.advice || [])]; - - // Make the errors more descriptive - if (fromSource) { - if (this.hasChangedFromSource()) { - advice.push({ - type: 'log', - category: 'warn', - text: 'Our internal value has been modified since we read the original source', - }); - } - } else { - // Go up the consumer tree and take the position from the first consumer found in the source - let consumer: undefined | Consumer = this; - do { - const possibleLocation = consumer.getDiagnosticLocation(target); - if (possibleLocation !== undefined) { - location = possibleLocation; - break; - } - consumer = consumer.parent; - } while (consumer !== undefined); - - // If consumer is undefined and we have no filename then we were not able to find a location, - // in this case, just throw a normal error - if (consumer === undefined && filename === undefined) { - throw new Error(message); - } - - // Warn that we didn't find this value in the source if it's parent wasn't either - if (this.parent === undefined || !this.parent.wasInSource()) { - advice.push({ - type: 'log', - category: 'warn', - text: `This value was expected to be found at ${this.getKeyPathString()} but was not in the original source`, - }); - } - } - - if (opts.loc !== undefined) { - location = opts.loc; - } - - if (location === undefined) { - throw new Error(message); - } - - const diagnostic: Diagnostic = { - description: { - category: this.context.category, - ...description, - advice, - }, - location: { - ...location, - filename: this.filename, - }, - }; - - const err = createSingleDiagnosticError(diagnostic); - - if (this.handleUnexpected === undefined) { - throw err; - } else { - if (this.shouldDispatchUnexpected()) { - this.handleUnexpected(diagnostic); - this.hasHandledUnexpected = true; - } - - // Still allow throwing the diagnostic - return err; - } - } - - // Only dispatch a single error for the current consumer, and suppress any if we have a parent consumer with errors - // We do this since we could be producing redundant stale errors based on - // results we've normalized to allow us to continue - shouldDispatchUnexpected(): boolean { - if (this.hasHandledUnexpected) { - return false; - } - - const {parent} = this; - if (parent !== undefined) { - return parent.shouldDispatchUnexpected(); - } - - return true; - } - - clone(opts: Partial): Consumer { - return new Consumer({ - usedNames: this.usedNames, - onDefinition: this.onDefinition, - handleUnexpectedDiagnostic: this.handleUnexpected, - filePath: this.path, - context: this.context, - value: this.value, - parent: this.parent, - objectPath: this.keyPath, - propertyMetadata: this.propertyMetadata, - ...opts, - }); - } - - fork( - key: ConsumeKey, - value: unknown, - propertyMetadata?: ConsumePropertyMetadata, - ) { - // We require this cache as we sometimes want to store state about a forked property such as used items - const cached = this.forkCache.get(String(key)); - if ( - cached !== undefined && - cached.value === value && - (cached.propertyMetadata === undefined || - cached.propertyMetadata === propertyMetadata) - ) { - return cached; - } - - const forked = this.clone({ - propertyMetadata, - value, - parent: this, - objectPath: [...this.keyPath, key], - }); - this.forkCache.set(String(key), forked); - return forked; - } - - _normalizeValueForSet(value: unknown): unknown { - if (value instanceof Set) { - return Array.from(value); - } - - if (value instanceof Map) { - const obj: UnknownObject = {}; - for (const [key, val] of value) { - obj[key] = val; - } - return obj; - } - - return value; - } - - getValue(def?: unknown): unknown { - if (this.exists()) { - return this.value; - } else { - return def; - } - } - - setValue(rawValue: unknown): this { - const value = this._normalizeValueForSet(rawValue); - this.value = value; - - // If we're at the root (as indicated by the lack of these properties) then go no where else - const {parent, keyPath} = this; - if (parent === undefined || keyPath.length === 0) { - return this; - } - - // Validate the parent is an object - const parentValue = parent.asUnknown(); - if ( - parentValue === undefined || - parentValue === null || - typeof parentValue !== 'object' - ) { - throw parent.unexpected(descriptions.CONSUME.SET_PROPERTY_NON_OBJECT); - } - - // Mutate the parent - const parentObj = parent.asOriginalUnknownObject(); - const key = this.getParentKey(); - parentObj[String(key)] = value; - parent.setValue(parentObj); - - return this; - } - - has(key: string): boolean { - return this.get(key).asUnknown() != null; - } - - setProperty(key: string, value: unknown): Consumer { - return this.get(key).setValue(value); - } - - get(key: string, metadata?: ConsumePropertyMetadata): Consumer { - const value = this.asOriginalUnknownObject(); - this.markUsedProperty(key); - return this.fork(key, value[key], metadata); - } - - markUsedProperty(name: string) { - this.usedNames.add(name); - } - - enforceUsedProperties(type: string = 'property', recursive: boolean = true) { - if (!this.isObject()) { - return; - } - - let knownProperties = Array.from(this.usedNames.keys()); - - const {normalizeKey} = this.context; - if (normalizeKey !== undefined) { - knownProperties = knownProperties.map((key) => normalizeKey(key)); - } - - for (const [key, value] of this.asMap(false, false)) { - if (!this.usedNames.has(key)) { - value.unexpected( - descriptions.CONSUME.UNUSED_PROPERTY( - this.getKeyPathString([key]), - type, - knownProperties, - ), - { - target: 'key', - at: 'suffix', - atParent: true, - }, - ); - } - - if (recursive) { - value.enforceUsedProperties(type, true); - } - } - } - - asPossibleParsedJSON(): Consumer { - if (typeof this.asUnknown() === 'string') { - return this.clone({ - value: JSON.parse(this.asString()), - }); - } else { - return this; - } - } - - // JSON - asJSONValue(): JSONValue { - const {value} = this; - - switch (typeof value) { - case 'number': - case 'string': - case 'boolean': - return value; - } - - if (value === null) { - return null; - } - - if (Array.isArray(value)) { - return this.asJSONArray(); - } - - if (this.isObject()) { - return this.asJSONObject(); - } - - this.unexpected(descriptions.CONSUME.EXPECTED_JSON_VALUE); - return ''; - } - - asJSONArray(): JSONArray { - const arr: JSONArray = []; - for (const value of this.asArray()) { - arr.push(value.asJSONValue()); - } - return arr; - } - - asJSONObject(): JSONObject { - const obj: JSONObject = {}; - for (const [key, value] of this.asMap()) { - obj[key] = value.asJSONPropertyValue(); - } - return obj; - } - - asJSONPropertyValue(): JSONPropertyValue { - if (this.exists()) { - return this.asJSONValue(); - } else { - return undefined; - } - } - - exists() { - return this.value != null; - } - - isObject(): boolean { - const {value} = this; - return ( - typeof value === 'object' && - value !== null && - value.constructor === Object - ); - } - - asUnknownObject(optional: boolean = false): UnknownObject { - this.declareDefinition({ - type: 'object', - default: undefined, - required: !optional, - }); - - return { - ...this.asOriginalUnknownObject(optional), - }; - } - - asOriginalUnknownObject(optional: boolean = false): UnknownObject { - if (optional === true && !this.exists()) { - return {}; - } - - const {value} = this; - if (!isPlainObject(value)) { - this.unexpected(descriptions.CONSUME.EXPECTED_OBJECT); - return {}; - } - - return value; - } - - asMap(optional?: boolean, markUsed = true): Map { - this.declareDefinition({ - type: 'object', - default: undefined, - required: !optional, - }); - - const value = this.asOriginalUnknownObject(optional); - const map = new Map(); - for (const key in value) { - if (markUsed) { - this.markUsedProperty(key); - } - map.set(key, this.fork(key, value[key])); - } - return map; - } - - asPlainArray(optional: boolean = false): Array { - this.declareDefinition({ - type: 'array', - default: undefined, - required: !optional, - }); - - if (optional === true && !this.exists()) { - return []; - } - - const {value} = this; - - if (!Array.isArray(value)) { - this.unexpected(descriptions.CONSUME.EXPECTED_ARRAY); - return []; - } - - return [...value]; - } - - asArray(optional?: boolean): Array { - const arr = this.asPlainArray(optional); - - return arr.map((val, index) => { - return this.fork(index, val); - }); - } - - asImplicitArray(): Array { - if (Array.isArray(this.asUnknown())) { - return this.asArray(); - } else if (this.exists()) { - return [this]; - } else { - return []; - } - } - - asDateOrVoid(def?: Date): undefined | Date { - this.declareDefinition({ - type: 'date', - default: def, - required: false, - }); - if (this.exists()) { - return this.asUndeclaredDate(def); - } else { - return undefined; - } - } - - asDate(def?: Date): Date { - this.declareDefinition({ - type: 'date', - default: def, - required: def === undefined, - }); - return this.asUndeclaredDate(def); - } - - asUndeclaredDate(def?: Date): Date { - const value = this.getValue(def); - if (!(value instanceof Date)) { - this.unexpected(descriptions.CONSUME.EXPECTED_DATE); - return new Date(); - } - return value; - } - - asBooleanOrVoid(def?: boolean): undefined | boolean { - this.declareDefinition({ - type: 'boolean', - default: def, - required: false, - }); - if (this.exists()) { - return this.asUndeclaredBoolean(def); - } else { - return undefined; - } - } - - asBoolean(def?: boolean): boolean { - this.declareDefinition({ - type: 'boolean', - default: def, - required: def === undefined, - }); - return this.asUndeclaredBoolean(def); - } - - asUndeclaredBoolean(def?: boolean): boolean { - const value = this.getValue(def); - if (typeof value !== 'boolean') { - this.unexpected(descriptions.CONSUME.EXPECTED_BOOLEAN); - return false; - } - return value; - } - - asStringOrVoid(def?: string): undefined | string { - this.declareDefinition({ - type: 'string', - default: def, - required: false, - }); - - if (this.exists()) { - return this.asUndeclaredString(def); - } else { - return undefined; - } - } - - asString(def?: string): string { - this.declareDefinition({ - type: 'string', - default: def, - required: def === undefined, - }); - return this.asUndeclaredString(def); - } - - asUndeclaredString(def?: string): string { - const value = this.getValue(def); - if (typeof value !== 'string') { - this.unexpected(descriptions.CONSUME.EXPECTED_STRING); - return ''; - } - return value; - } - - asStringSet( - validValues: Array, - def?: ValidValue, - ): ValidValue { - this.declareDefinition({ - type: 'string', - default: def, - required: def === undefined, - allowedValues: validValues, - }); - return this.asUndeclaredStringSet(validValues, def); - } - - asUndeclaredStringSet( - validValues: Array, - def?: ValidValue, - ): ValidValue { - const value = this.asUndeclaredString(String(def)); - - // @ts-ignore - if (validValues.includes(value)) { - // @ts-ignore - return value; - } else { - this.unexpected( - descriptions.CONSUME.INVALID_STRING_SET_VALUE( - value, - // rome-ignore lint/noExplicitAny - ((validValues as any) as Array), - ), - { - target: 'value', - }, - ); - return validValues[0]; - } - } - - asStringSetOrVoid( - validValues: Array, - def?: ValidValue, - ): undefined | ValidValue { - this.declareDefinition({ - type: 'string', - default: def, - required: false, - allowedValues: validValues, - }); - - if (this.exists()) { - return this.asUndeclaredStringSet(validValues, def); - } else { - return undefined; - } - } - - asBigIntOrVoid(def?: number | bigint): undefined | bigint { - this.declareDefinition({ - type: 'bigint', - default: def, - required: false, - }); - if (this.exists()) { - return this.asUndeclaredBigInt(def); - } else { - return undefined; - } - } - - asBigInt(def?: number | bigint): bigint { - this.declareDefinition({ - type: 'bigint', - default: def, - required: def === undefined, - }); - return this.asUndeclaredBigInt(def); - } - - asUndeclaredBigInt(def?: number | bigint): bigint { - const value = this.getValue(def); - - if (typeof value === 'number') { - return BigInt(value); - } - - if (typeof value === 'bigint') { - return value; - } - - this.unexpected(descriptions.CONSUME.EXPECTED_BIGINT); - return BigInt('0'); - } - - _declareOptionalFilePath(def?: string) { - this.declareDefinition( - { - type: 'string', - default: def, - required: false, - }, - 'path', - ); - } - - asURLFilePath(def?: string): URLFilePath { - const path = this.asUnknownFilePath(def); - if (path.isURL()) { - return path.assertURL(); - } else { - this.unexpected(descriptions.CONSUME.EXPECTED_URL); - return createURLFilePath('unknown://').append(path); - } - } - - asURLFilePathOrVoid(def?: string): undefined | URLFilePath { - if (this.exists()) { - return this.asURLFilePath(def); - } else { - this._declareOptionalFilePath(def); - return undefined; - } - } - - asUnknownFilePath(def?: string): UnknownFilePath { - this.declareDefinition( - { - type: 'string', - default: def, - required: def === undefined, - }, - 'path', - ); - - return createUnknownFilePath(this.asUndeclaredString(def)); - } - - asUnknownFilePathOrVoid(def?: string): undefined | UnknownFilePath { - if (this.exists()) { - return this.asUnknownFilePath(def); - } else { - this._declareOptionalFilePath(def); - return undefined; - } - } - - asAbsoluteFilePath(def?: string, cwd?: AbsoluteFilePath): AbsoluteFilePath { - const path = this.asUnknownFilePath(def); - if (path.isAbsolute()) { - return path.assertAbsolute(); - } else if (cwd !== undefined && path.isRelative()) { - return cwd.resolve(path); - } else { - this.unexpected(descriptions.CONSUME.EXPECTED_ABSOLUTE_PATH); - return createAbsoluteFilePath('/').append(path); - } - } - - asAbsoluteFilePathOrVoid( - def?: string, - cwd?: AbsoluteFilePath, - ): undefined | AbsoluteFilePath { - if (this.exists()) { - return this.asAbsoluteFilePath(def, cwd); - } else { - this._declareOptionalFilePath(def); - return undefined; - } - } - - asRelativeFilePath(def?: string): RelativeFilePath { - const path = this.asUnknownFilePath(def); - if (path.isRelative()) { - return path.assertRelative(); - } else { - this.unexpected(descriptions.CONSUME.EXPECTED_RELATIVE_PATH); - return path.toExplicitRelative(); - } - } - - asRelativeFilePathOrVoid(def?: string): undefined | RelativeFilePath { - if (this.exists()) { - return this.asRelativeFilePath(def); - } else { - this._declareOptionalFilePath(def); - return undefined; - } - } - - asExplicitRelativeFilePath(def?: string): RelativeFilePath { - const path = this.asRelativeFilePath(def); - - if (path.isExplicitRelative()) { - return path; - } else { - this.unexpected(descriptions.CONSUME.EXPECTED_EXPLICIT_RELATIVE_PATH); - return path.toExplicitRelative(); - } - } - - asExplicitRelativeFilePathOrVoid(def?: string): undefined | RelativeFilePath { - if (this.exists()) { - return this.asExplicitRelativeFilePath(def); - } else { - this._declareOptionalFilePath(def); - return undefined; - } - } - - asNumberOrVoid(def?: number): undefined | number { - this.declareDefinition({ - type: 'number', - default: def, - required: false, - }); - - if (this.exists()) { - return this.asUndeclaredNumber(def); - } else { - return undefined; - } - } - - asZeroIndexedNumber(): Number0 { - return ob1Coerce0(this.asNumber()); - } - - asOneIndexedNumber(): Number1 { - return ob1Coerce1(this.asNumber()); - } - - asNumberFromString(def?: number): number { - this.declareDefinition({ - type: 'number', - default: def, - required: def === undefined, - }); - return this.asUndeclaredNumberFromString(def); - } - - asNumberFromStringOrVoid(def?: number): undefined | number { - this.declareDefinition({ - type: 'number', - default: def, - required: false, - }); - - if (this.exists()) { - return this.asUndeclaredNumberFromString(def); - } else { - return undefined; - } - } - - asUndeclaredNumberFromString(def?: number): number { - if (def !== undefined && !this.exists()) { - return def; - } - - const str = this.asUndeclaredString(); - const num = Number(str); - if (isNaN(num)) { - this.unexpected(descriptions.CONSUME.EXPECTED_VALID_NUMBER); - return NaN; - } else { - return num; - } - } - - asNumber(def?: number): number { - this.declareDefinition({ - type: 'number', - default: def, - required: def === undefined, - }); - return this.asUndeclaredNumber(def); - } - - asNumberInRange( - opts: { - min?: number; - max?: number; - default?: number; - }, - ): number - - asNumberInRange( - opts: { - min: Number0; - max?: Number0; - default?: Number0; - }, - ): Number0 - - asNumberInRange( - opts: { - min: Number1; - max?: Number1; - default?: Number1; - }, - ): Number1 - - asNumberInRange( - opts: { - min?: UnknownNumber; - max?: UnknownNumber; - default?: UnknownNumber; - }, - ): UnknownNumber { - const num = this.asUndeclaredNumber(opts.default); - const min = ob1Get(opts.min); - const max = ob1Get(opts.max); - - this.declareDefinition({ - type: 'number', - default: opts.default, - required: opts.default !== undefined, - min, - max, - }); - - // Nice error message when both min and max are specified - if (min !== undefined && max !== undefined && (num < min || num > max)) { - this.unexpected(descriptions.CONSUME.EXPECTED_NUMBER_BETWEEN(min, max)); - return num; - } - - if (min !== undefined && num < min) { - this.unexpected(descriptions.CONSUME.EXPECTED_NUMBER_HIGHER(min)); - } - - if (max !== undefined && num > max) { - this.unexpected(descriptions.CONSUME.EXPECTED_NUMBER_LOWER(max)); - } - - return num; - } - - asUndeclaredNumber(def?: UnknownNumber): number { - const value = this.getValue(def); - if (typeof value !== 'number') { - this.unexpected(descriptions.CONSUME.EXPECTED_NUMBER); - return 0; - } - return value; - } - - asUnknown(): unknown { - return this.value; - } - - // rome-ignore lint/noExplicitAny - asAny(): any { - return this.value; - } + constructor(opts: ConsumerOptions) { + this.path = opts.filePath; + this.filename = this.path === undefined ? undefined : this.path.join(); + + this.value = opts.value; + this.parent = opts.parent; + this.keyPath = opts.objectPath; + this.context = opts.context; + this.onDefinition = opts.onDefinition; + this.propertyMetadata = opts.propertyMetadata; + this.usedNames = new Set(opts.usedNames); + this.forkCache = new Map(); + this.forceDiagnosticTarget = opts.forceDiagnosticTarget; + this.declared = false; + + // See shouldDispatchUnexpected for explanation + this.hasHandledUnexpected = false; + this.handleUnexpected = opts.handleUnexpectedDiagnostic; + } + + path: undefined | UnknownFilePath; + filename: undefined | string; + declared: boolean; + handleUnexpected: undefined | ConsumerHandleUnexpected; + onDefinition: undefined | ConsumerOnDefinition; + propertyMetadata: undefined | ConsumePropertyMetadata; + parent: undefined | Consumer; + value: unknown; + context: ConsumeContext; + keyPath: ConsumePath; + usedNames: Set; + forkCache: Map; + hasHandledUnexpected: boolean; + forceDiagnosticTarget: undefined | ConsumeSourceLocationRequestTarget; + + capture(): { + consumer: Consumer; + definitions: Array; + diagnostics: Diagnostics; + } { + let diagnostics: Diagnostics = []; + const definitions: Array = []; + + const consumer = this.clone({ + onDefinition: (def, consumer) => { + if (this.onDefinition !== undefined) { + this.onDefinition(def, consumer); + } + + definitions.push(def); + }, + handleUnexpectedDiagnostic(diag) { + diagnostics.push(diag); + }, + }); + return {consumer, definitions, diagnostics}; + } + + async bufferDiagnostics( + callback: (consumer: Consumer) => Promise | T, + ): Promise { + const {diagnostics, consumer} = await this.capture(); + const result = await callback(consumer); + if (result === undefined || diagnostics.length > 0) { + throw new DiagnosticsError('Captured diagnostics', diagnostics); + } + return result; + } + + handleThrownDiagnostics(callback: () => void) { + if (this.handleUnexpected === undefined) { + callback(); + } else { + const {diagnostics} = catchDiagnosticsSync(callback); + + if (diagnostics !== undefined) { + for (const diag of diagnostics) { + this.handleUnexpected(diag); + } + } + } + } + + declareDefinition( + partialDef: + | Omit + | Omit + | Omit, + inputName?: string, + ) { + if (this.declared) { + return; + } + + if (this.onDefinition === undefined) { + return; + } + + const metadata: ConsumePropertyMetadata = { + inputName, + ...this.propertyMetadata, + }; + + const def: ConsumePropertyDefinition = { + ...partialDef, + objectPath: this.keyPath, + metadata, + }; + + this.declared = true; + + this.onDefinition(def, this); + } + + getDiagnosticLocation( + target: ConsumeSourceLocationRequestTarget = 'all', + ): DiagnosticLocation { + const {getDiagnosticPointer} = this.context; + if (getDiagnosticPointer === undefined) { + return {}; + } + + const {forceDiagnosticTarget} = this; + if (forceDiagnosticTarget !== undefined) { + target = forceDiagnosticTarget; + } + return getDiagnosticPointer(this.keyPath, target); + } + + getLocation(target?: ConsumeSourceLocationRequestTarget): SourceLocation { + const location = this.getDiagnosticLocation(target); + if ( + location === undefined || + location.start === undefined || + location.end === undefined + ) { + return { + filename: this.filename, + start: UNKNOWN_POSITION, + end: UNKNOWN_POSITION, + }; + } else { + return { + filename: location.filename, + start: location.start, + end: location.end, + }; + } + } + + getLocationRange( + startIndex: Number0, + endIndex: Number0 = startIndex, + target?: ConsumeSourceLocationRequestTarget, + ): SourceLocation { + const loc = this.getLocation(target); + if (loc.start === UNKNOWN_POSITION) { + return loc; + } + + const {start, end} = loc; + + // We don't support handling line differences here... yet? + if (start.line !== end.line) { + return loc; + } + + return { + ...loc, + start: { + ...start, + column: ob1Add(start.column, startIndex), + index: ob1Add(start.index, startIndex), + }, + end: { + ...start, + column: ob1Add(start.column, endIndex), + index: ob1Add(start.index, endIndex), + }, + }; + } + + getKey(): Consumer { + return this.clone({ + forceDiagnosticTarget: 'key', + value: this.getParentKey(), + }); + } + + getParentKey(): ConsumeKey { + return this.keyPath[this.keyPath.length - 1]; + } + + hasChangedFromSource(): boolean { + const {getOriginalValue} = this.context; + if (getOriginalValue === undefined) { + return false; + } + + const originalValue = getOriginalValue(this.keyPath); + return !this.wasInSource() || this.value !== originalValue; + } + + wasInSource() { + return this.getDiagnosticLocation() !== undefined; + } + + getKeyPathString(path: ConsumePath = this.keyPath): string { + const {normalizeKey} = this.context; + let str = ''; + + for (let i = 0; i < path.length; i++) { + let part = path[i]; + const nextPart = path[i + 1]; + + if (typeof part === 'string' && normalizeKey !== undefined) { + part = normalizeKey(part); + } + + // If we are a computed property then wrap in brackets, the previous part would not have inserted a dot + // We allow a computed part at the beginning of a path + if (isComputedPart(part) && i > 0) { + const inner = + typeof part === 'number' + ? String(part) + : escapeString( + part, + { + quote: "'", + }, + ); + + str += `[${inner}]`; + } else { + if (nextPart === undefined || isComputedPart(nextPart)) { + // Don't append a dot if there are no parts or the next is computed + str += part; + } else { + str += `${part}.`; + } + } + } + + return str; + } + + generateUnexpectedMessage(msg: string, opts: UnexpectedConsumerOptions): string { + const {at = 'suffix', atParent = false} = opts; + const {parent} = this; + + let target: Consumer = this; + + if (atParent) { + if (parent === undefined) { + // Cannot target the parent if it does not exist + return msg; + } else { + target = parent; + } + } + + if (at === 'suffix') { + msg += ` at ${target.getKeyPathString()}`; + } else { + msg = `${target.getKeyPathString()} ${msg}`; + } + + return msg; + } + + unexpected( + description: DiagnosticDescriptionOptionalCategory = descriptions.CONSUME.INVALID, + opts: UnexpectedConsumerOptions = {}, + ): DiagnosticsError { + const {target = 'value'} = opts; + + const {filename} = this; + let location = this.getDiagnosticLocation(target); + const fromSource = location !== undefined; + + const message = this.generateUnexpectedMessage( + description.message.value, + opts, + ); + description = { + ...description, + message: createBlessedDiagnosticMessage(message), + }; + + const advice: DiagnosticAdvice = [...(description.advice || [])]; + + // Make the errors more descriptive + if (fromSource) { + if (this.hasChangedFromSource()) { + advice.push({ + type: 'log', + category: 'warn', + text: 'Our internal value has been modified since we read the original source', + }); + } + } else { + // Go up the consumer tree and take the position from the first consumer found in the source + let consumer: undefined | Consumer = this; + do { + const possibleLocation = consumer.getDiagnosticLocation(target); + if (possibleLocation !== undefined) { + location = possibleLocation; + break; + } + consumer = consumer.parent; + } while (consumer !== undefined); + + // If consumer is undefined and we have no filename then we were not able to find a location, + // in this case, just throw a normal error + if (consumer === undefined && filename === undefined) { + throw new Error(message); + } + + // Warn that we didn't find this value in the source if it's parent wasn't either + if (this.parent === undefined || !this.parent.wasInSource()) { + advice.push({ + type: 'log', + category: 'warn', + text: `This value was expected to be found at ${this.getKeyPathString()} but was not in the original source`, + }); + } + } + + if (opts.loc !== undefined) { + location = opts.loc; + } + + if (location === undefined) { + throw new Error(message); + } + + const diagnostic: Diagnostic = { + description: { + category: this.context.category, + ...description, + advice, + }, + location: { + ...location, + filename: this.filename, + }, + }; + + const err = createSingleDiagnosticError(diagnostic); + + if (this.handleUnexpected === undefined) { + throw err; + } else { + if (this.shouldDispatchUnexpected()) { + this.handleUnexpected(diagnostic); + this.hasHandledUnexpected = true; + } + + // Still allow throwing the diagnostic + return err; + } + } + + // Only dispatch a single error for the current consumer, and suppress any if we have a parent consumer with errors + // We do this since we could be producing redundant stale errors based on + // results we've normalized to allow us to continue + shouldDispatchUnexpected(): boolean { + if (this.hasHandledUnexpected) { + return false; + } + + const {parent} = this; + if (parent !== undefined) { + return parent.shouldDispatchUnexpected(); + } + + return true; + } + + clone(opts: Partial): Consumer { + return new Consumer({ + usedNames: this.usedNames, + onDefinition: this.onDefinition, + handleUnexpectedDiagnostic: this.handleUnexpected, + filePath: this.path, + context: this.context, + value: this.value, + parent: this.parent, + objectPath: this.keyPath, + propertyMetadata: this.propertyMetadata, + ...opts, + }); + } + + fork( + key: ConsumeKey, + value: unknown, + propertyMetadata?: ConsumePropertyMetadata, + ) { + // We require this cache as we sometimes want to store state about a forked property such as used items + const cached = this.forkCache.get(String(key)); + if ( + cached !== undefined && + cached.value === value && + (cached.propertyMetadata === undefined || + cached.propertyMetadata === propertyMetadata) + ) { + return cached; + } + + const forked = this.clone({ + propertyMetadata, + value, + parent: this, + objectPath: [...this.keyPath, key], + }); + this.forkCache.set(String(key), forked); + return forked; + } + + _normalizeValueForSet(value: unknown): unknown { + if (value instanceof Set) { + return Array.from(value); + } + + if (value instanceof Map) { + const obj: UnknownObject = {}; + for (const [key, val] of value) { + obj[key] = val; + } + return obj; + } + + return value; + } + + getValue(def?: unknown): unknown { + if (this.exists()) { + return this.value; + } else { + return def; + } + } + + setValue(rawValue: unknown): this { + const value = this._normalizeValueForSet(rawValue); + this.value = value; + + // If we're at the root (as indicated by the lack of these properties) then go no where else + const {parent, keyPath} = this; + if (parent === undefined || keyPath.length === 0) { + return this; + } + + // Validate the parent is an object + const parentValue = parent.asUnknown(); + if ( + parentValue === undefined || + parentValue === null || + typeof parentValue !== 'object' + ) { + throw parent.unexpected(descriptions.CONSUME.SET_PROPERTY_NON_OBJECT); + } + + // Mutate the parent + const parentObj = parent.asOriginalUnknownObject(); + const key = this.getParentKey(); + parentObj[String(key)] = value; + parent.setValue(parentObj); + + return this; + } + + has(key: string): boolean { + return this.get(key).asUnknown() != null; + } + + setProperty(key: string, value: unknown): Consumer { + return this.get(key).setValue(value); + } + + get(key: string, metadata?: ConsumePropertyMetadata): Consumer { + const value = this.asOriginalUnknownObject(); + this.markUsedProperty(key); + return this.fork(key, value[key], metadata); + } + + markUsedProperty(name: string) { + this.usedNames.add(name); + } + + enforceUsedProperties(type: string = 'property', recursive: boolean = true) { + if (!this.isObject()) { + return; + } + + let knownProperties = Array.from(this.usedNames.keys()); + + const {normalizeKey} = this.context; + if (normalizeKey !== undefined) { + knownProperties = knownProperties.map((key) => normalizeKey(key)); + } + + for (const [key, value] of this.asMap(false, false)) { + if (!this.usedNames.has(key)) { + value.unexpected( + descriptions.CONSUME.UNUSED_PROPERTY( + this.getKeyPathString([key]), + type, + knownProperties, + ), + { + target: 'key', + at: 'suffix', + atParent: true, + }, + ); + } + + if (recursive) { + value.enforceUsedProperties(type, true); + } + } + } + + asPossibleParsedJSON(): Consumer { + if (typeof this.asUnknown() === 'string') { + return this.clone({ + value: JSON.parse(this.asString()), + }); + } else { + return this; + } + } + + // JSON + asJSONValue(): JSONValue { + const {value} = this; + + switch (typeof value) { + case 'number': + case 'string': + case 'boolean': + return value; + } + + if (value === null) { + return null; + } + + if (Array.isArray(value)) { + return this.asJSONArray(); + } + + if (this.isObject()) { + return this.asJSONObject(); + } + + this.unexpected(descriptions.CONSUME.EXPECTED_JSON_VALUE); + return ''; + } + + asJSONArray(): JSONArray { + const arr: JSONArray = []; + for (const value of this.asArray()) { + arr.push(value.asJSONValue()); + } + return arr; + } + + asJSONObject(): JSONObject { + const obj: JSONObject = {}; + for (const [key, value] of this.asMap()) { + obj[key] = value.asJSONPropertyValue(); + } + return obj; + } + + asJSONPropertyValue(): JSONPropertyValue { + if (this.exists()) { + return this.asJSONValue(); + } else { + return undefined; + } + } + + exists() { + return this.value != null; + } + + isObject(): boolean { + const {value} = this; + return ( + typeof value === 'object' && value !== null && value.constructor === Object + ); + } + + asUnknownObject(optional: boolean = false): UnknownObject { + this.declareDefinition({ + type: 'object', + default: undefined, + required: !optional, + }); + + return { + ...this.asOriginalUnknownObject(optional), + }; + } + + asOriginalUnknownObject(optional: boolean = false): UnknownObject { + if (optional === true && !this.exists()) { + return {}; + } + + const {value} = this; + if (!isPlainObject(value)) { + this.unexpected(descriptions.CONSUME.EXPECTED_OBJECT); + return {}; + } + + return value; + } + + asMap(optional?: boolean, markUsed = true): Map { + this.declareDefinition({ + type: 'object', + default: undefined, + required: !optional, + }); + + const value = this.asOriginalUnknownObject(optional); + const map = new Map(); + for (const key in value) { + if (markUsed) { + this.markUsedProperty(key); + } + map.set(key, this.fork(key, value[key])); + } + return map; + } + + asPlainArray(optional: boolean = false): Array { + this.declareDefinition({ + type: 'array', + default: undefined, + required: !optional, + }); + + if (optional === true && !this.exists()) { + return []; + } + + const {value} = this; + + if (!Array.isArray(value)) { + this.unexpected(descriptions.CONSUME.EXPECTED_ARRAY); + return []; + } + + return [...value]; + } + + asArray(optional?: boolean): Array { + const arr = this.asPlainArray(optional); + + return arr.map((val, index) => { + return this.fork(index, val); + }); + } + + asImplicitArray(): Array { + if (Array.isArray(this.asUnknown())) { + return this.asArray(); + } else if (this.exists()) { + return [this]; + } else { + return []; + } + } + + asDateOrVoid(def?: Date): undefined | Date { + this.declareDefinition({ + type: 'date', + default: def, + required: false, + }); + if (this.exists()) { + return this.asUndeclaredDate(def); + } else { + return undefined; + } + } + + asDate(def?: Date): Date { + this.declareDefinition({ + type: 'date', + default: def, + required: def === undefined, + }); + return this.asUndeclaredDate(def); + } + + asUndeclaredDate(def?: Date): Date { + const value = this.getValue(def); + if (!(value instanceof Date)) { + this.unexpected(descriptions.CONSUME.EXPECTED_DATE); + return new Date(); + } + return value; + } + + asBooleanOrVoid(def?: boolean): undefined | boolean { + this.declareDefinition({ + type: 'boolean', + default: def, + required: false, + }); + if (this.exists()) { + return this.asUndeclaredBoolean(def); + } else { + return undefined; + } + } + + asBoolean(def?: boolean): boolean { + this.declareDefinition({ + type: 'boolean', + default: def, + required: def === undefined, + }); + return this.asUndeclaredBoolean(def); + } + + asUndeclaredBoolean(def?: boolean): boolean { + const value = this.getValue(def); + if (typeof value !== 'boolean') { + this.unexpected(descriptions.CONSUME.EXPECTED_BOOLEAN); + return false; + } + return value; + } + + asStringOrVoid(def?: string): undefined | string { + this.declareDefinition({ + type: 'string', + default: def, + required: false, + }); + + if (this.exists()) { + return this.asUndeclaredString(def); + } else { + return undefined; + } + } + + asString(def?: string): string { + this.declareDefinition({ + type: 'string', + default: def, + required: def === undefined, + }); + return this.asUndeclaredString(def); + } + + asUndeclaredString(def?: string): string { + const value = this.getValue(def); + if (typeof value !== 'string') { + this.unexpected(descriptions.CONSUME.EXPECTED_STRING); + return ''; + } + return value; + } + + asStringSet( + validValues: Array, + def?: ValidValue, + ): ValidValue { + this.declareDefinition({ + type: 'string', + default: def, + required: def === undefined, + allowedValues: validValues, + }); + return this.asUndeclaredStringSet(validValues, def); + } + + asUndeclaredStringSet( + validValues: Array, + def?: ValidValue, + ): ValidValue { + const value = this.asUndeclaredString(String(def)); + + // @ts-ignore + if (validValues.includes(value)) { + // @ts-ignore + return value; + } else { + this.unexpected( + descriptions.CONSUME.INVALID_STRING_SET_VALUE( + value, + // rome-ignore lint/noExplicitAny + ((validValues as any) as Array), + ), + { + target: 'value', + }, + ); + return validValues[0]; + } + } + + asStringSetOrVoid( + validValues: Array, + def?: ValidValue, + ): undefined | ValidValue { + this.declareDefinition({ + type: 'string', + default: def, + required: false, + allowedValues: validValues, + }); + + if (this.exists()) { + return this.asUndeclaredStringSet(validValues, def); + } else { + return undefined; + } + } + + asBigIntOrVoid(def?: number | bigint): undefined | bigint { + this.declareDefinition({ + type: 'bigint', + default: def, + required: false, + }); + if (this.exists()) { + return this.asUndeclaredBigInt(def); + } else { + return undefined; + } + } + + asBigInt(def?: number | bigint): bigint { + this.declareDefinition({ + type: 'bigint', + default: def, + required: def === undefined, + }); + return this.asUndeclaredBigInt(def); + } + + asUndeclaredBigInt(def?: number | bigint): bigint { + const value = this.getValue(def); + + if (typeof value === 'number') { + return BigInt(value); + } + + if (typeof value === 'bigint') { + return value; + } + + this.unexpected(descriptions.CONSUME.EXPECTED_BIGINT); + return BigInt('0'); + } + + _declareOptionalFilePath(def?: string) { + this.declareDefinition( + { + type: 'string', + default: def, + required: false, + }, + 'path', + ); + } + + asURLFilePath(def?: string): URLFilePath { + const path = this.asUnknownFilePath(def); + if (path.isURL()) { + return path.assertURL(); + } else { + this.unexpected(descriptions.CONSUME.EXPECTED_URL); + return createURLFilePath('unknown://').append(path); + } + } + + asURLFilePathOrVoid(def?: string): undefined | URLFilePath { + if (this.exists()) { + return this.asURLFilePath(def); + } else { + this._declareOptionalFilePath(def); + return undefined; + } + } + + asUnknownFilePath(def?: string): UnknownFilePath { + this.declareDefinition( + { + type: 'string', + default: def, + required: def === undefined, + }, + 'path', + ); + + return createUnknownFilePath(this.asUndeclaredString(def)); + } + + asUnknownFilePathOrVoid(def?: string): undefined | UnknownFilePath { + if (this.exists()) { + return this.asUnknownFilePath(def); + } else { + this._declareOptionalFilePath(def); + return undefined; + } + } + + asAbsoluteFilePath(def?: string, cwd?: AbsoluteFilePath): AbsoluteFilePath { + const path = this.asUnknownFilePath(def); + if (path.isAbsolute()) { + return path.assertAbsolute(); + } else if (cwd !== undefined && path.isRelative()) { + return cwd.resolve(path); + } else { + this.unexpected(descriptions.CONSUME.EXPECTED_ABSOLUTE_PATH); + return createAbsoluteFilePath('/').append(path); + } + } + + asAbsoluteFilePathOrVoid( + def?: string, + cwd?: AbsoluteFilePath, + ): undefined | AbsoluteFilePath { + if (this.exists()) { + return this.asAbsoluteFilePath(def, cwd); + } else { + this._declareOptionalFilePath(def); + return undefined; + } + } + + asRelativeFilePath(def?: string): RelativeFilePath { + const path = this.asUnknownFilePath(def); + if (path.isRelative()) { + return path.assertRelative(); + } else { + this.unexpected(descriptions.CONSUME.EXPECTED_RELATIVE_PATH); + return path.toExplicitRelative(); + } + } + + asRelativeFilePathOrVoid(def?: string): undefined | RelativeFilePath { + if (this.exists()) { + return this.asRelativeFilePath(def); + } else { + this._declareOptionalFilePath(def); + return undefined; + } + } + + asExplicitRelativeFilePath(def?: string): RelativeFilePath { + const path = this.asRelativeFilePath(def); + + if (path.isExplicitRelative()) { + return path; + } else { + this.unexpected(descriptions.CONSUME.EXPECTED_EXPLICIT_RELATIVE_PATH); + return path.toExplicitRelative(); + } + } + + asExplicitRelativeFilePathOrVoid(def?: string): undefined | RelativeFilePath { + if (this.exists()) { + return this.asExplicitRelativeFilePath(def); + } else { + this._declareOptionalFilePath(def); + return undefined; + } + } + + asNumberOrVoid(def?: number): undefined | number { + this.declareDefinition({ + type: 'number', + default: def, + required: false, + }); + + if (this.exists()) { + return this.asUndeclaredNumber(def); + } else { + return undefined; + } + } + + asZeroIndexedNumber(): Number0 { + return ob1Coerce0(this.asNumber()); + } + + asOneIndexedNumber(): Number1 { + return ob1Coerce1(this.asNumber()); + } + + asNumberFromString(def?: number): number { + this.declareDefinition({ + type: 'number', + default: def, + required: def === undefined, + }); + return this.asUndeclaredNumberFromString(def); + } + + asNumberFromStringOrVoid(def?: number): undefined | number { + this.declareDefinition({ + type: 'number', + default: def, + required: false, + }); + + if (this.exists()) { + return this.asUndeclaredNumberFromString(def); + } else { + return undefined; + } + } + + asUndeclaredNumberFromString(def?: number): number { + if (def !== undefined && !this.exists()) { + return def; + } + + const str = this.asUndeclaredString(); + const num = Number(str); + if (isNaN(num)) { + this.unexpected(descriptions.CONSUME.EXPECTED_VALID_NUMBER); + return NaN; + } else { + return num; + } + } + + asNumber(def?: number): number { + this.declareDefinition({ + type: 'number', + default: def, + required: def === undefined, + }); + return this.asUndeclaredNumber(def); + } + + asNumberInRange( + opts: { + min?: number; + max?: number; + default?: number; + }, + ): number + + asNumberInRange( + opts: { + min: Number0; + max?: Number0; + default?: Number0; + }, + ): Number0 + + asNumberInRange( + opts: { + min: Number1; + max?: Number1; + default?: Number1; + }, + ): Number1 + + asNumberInRange( + opts: { + min?: UnknownNumber; + max?: UnknownNumber; + default?: UnknownNumber; + }, + ): UnknownNumber { + const num = this.asUndeclaredNumber(opts.default); + const min = ob1Get(opts.min); + const max = ob1Get(opts.max); + + this.declareDefinition({ + type: 'number', + default: opts.default, + required: opts.default !== undefined, + min, + max, + }); + + // Nice error message when both min and max are specified + if (min !== undefined && max !== undefined && (num < min || num > max)) { + this.unexpected(descriptions.CONSUME.EXPECTED_NUMBER_BETWEEN(min, max)); + return num; + } + + if (min !== undefined && num < min) { + this.unexpected(descriptions.CONSUME.EXPECTED_NUMBER_HIGHER(min)); + } + + if (max !== undefined && num > max) { + this.unexpected(descriptions.CONSUME.EXPECTED_NUMBER_LOWER(max)); + } + + return num; + } + + asUndeclaredNumber(def?: UnknownNumber): number { + const value = this.getValue(def); + if (typeof value !== 'number') { + this.unexpected(descriptions.CONSUME.EXPECTED_NUMBER); + return 0; + } + return value; + } + + asUnknown(): unknown { + return this.value; + } + + // rome-ignore lint/noExplicitAny + asAny(): any { + return this.value; + } } diff --git a/packages/@romejs/consume/index.ts b/packages/@romejs/consume/index.ts index 9b3bf791e69..dc9daacf0d9 100644 --- a/packages/@romejs/consume/index.ts +++ b/packages/@romejs/consume/index.ts @@ -11,35 +11,35 @@ import {RequiredProps} from '@romejs/typescript-helpers'; import {DiagnosticCategory} from '@romejs/diagnostics'; const EMPTY_CONSUME_OPTIONS: Omit = { - propertyMetadata: undefined, - value: undefined, - handleUnexpectedDiagnostic: undefined, - onDefinition: undefined, - filePath: undefined, - objectPath: [], - parent: undefined, + propertyMetadata: undefined, + value: undefined, + handleUnexpectedDiagnostic: undefined, + onDefinition: undefined, + filePath: undefined, + objectPath: [], + parent: undefined, }; export function consume( - opts: RequiredProps, 'context'>, + opts: RequiredProps, 'context'>, ): Consumer { - return new Consumer({ - ...EMPTY_CONSUME_OPTIONS, - ...opts, - }); + return new Consumer({ + ...EMPTY_CONSUME_OPTIONS, + ...opts, + }); } export function consumeUnknown( - value: unknown, - category: DiagnosticCategory, + value: unknown, + category: DiagnosticCategory, ): Consumer { - return new Consumer({ - ...EMPTY_CONSUME_OPTIONS, - context: { - category, - }, - value, - }); + return new Consumer({ + ...EMPTY_CONSUME_OPTIONS, + context: { + category, + }, + value, + }); } export {Consumer}; diff --git a/packages/@romejs/consume/types.ts b/packages/@romejs/consume/types.ts index 0cd059840f4..06181186efc 100644 --- a/packages/@romejs/consume/types.ts +++ b/packages/@romejs/consume/types.ts @@ -6,9 +6,9 @@ */ import { - Diagnostic, - DiagnosticCategory, - DiagnosticLocation, + Diagnostic, + DiagnosticCategory, + DiagnosticLocation, } from '@romejs/diagnostics'; import Consumer from './Consumer'; import {UnknownFilePath} from '@romejs/path'; @@ -20,69 +20,69 @@ export type ConsumeKey = number | string; export type ConsumePath = Array; export type ConsumeSourceLocationRequestTarget = - | 'all' - | 'key' - | 'value' - | 'inner-value'; + | 'all' + | 'key' + | 'value' + | 'inner-value'; export type ConsumeContext = { - category: DiagnosticCategory; - normalizeKey?: (key: string) => string; - getDiagnosticPointer?: ( - keys: ConsumePath, - target: ConsumeSourceLocationRequestTarget, - ) => DiagnosticLocation; - getOriginalValue?: (path: ConsumePath) => unknown; + category: DiagnosticCategory; + normalizeKey?: (key: string) => string; + getDiagnosticPointer?: ( + keys: ConsumePath, + target: ConsumeSourceLocationRequestTarget, + ) => DiagnosticLocation; + getOriginalValue?: (path: ConsumePath) => unknown; }; export type ConsumePropertyMetadata = { - description?: string; - inputName?: string; + description?: string; + inputName?: string; }; type ConsumePropertyDefinitionBase = { - objectPath: ConsumePath; - default: unknown; - required: boolean; - metadata: ConsumePropertyMetadata; + objectPath: ConsumePath; + default: unknown; + required: boolean; + metadata: ConsumePropertyMetadata; }; export type ConsumePropertyPrimitiveDefinition = ConsumePropertyDefinitionBase & { - type: 'boolean' | 'bigint' | 'date' | 'array' | 'object'; + type: 'boolean' | 'bigint' | 'date' | 'array' | 'object'; }; export type ConsumePropertyStringDefinition = ConsumePropertyDefinitionBase & { - type: 'string'; - allowedValues?: Array; + type: 'string'; + allowedValues?: Array; }; export type ConsumePropertyNumberDefinition = ConsumePropertyDefinitionBase & { - type: 'number'; - min?: number; - max?: number; + type: 'number'; + min?: number; + max?: number; }; export type ConsumePropertyDefinition = - | ConsumePropertyStringDefinition - | ConsumePropertyPrimitiveDefinition - | ConsumePropertyNumberDefinition; + | ConsumePropertyStringDefinition + | ConsumePropertyPrimitiveDefinition + | ConsumePropertyNumberDefinition; export type ConsumerOnDefinition = ( - definition: ConsumePropertyDefinition, - consumer: Consumer, + definition: ConsumePropertyDefinition, + consumer: Consumer, ) => void; export type ConsumerHandleUnexpected = (diagnostic: Diagnostic) => void; export type ConsumerOptions = { - usedNames?: Iterable; - handleUnexpectedDiagnostic?: ConsumerHandleUnexpected; - onDefinition?: ConsumerOnDefinition; - propertyMetadata?: ConsumePropertyMetadata; - filePath?: UnknownFilePath; - objectPath: ConsumePath; - context: ConsumeContext; - value: unknown; - parent?: Consumer; - forceDiagnosticTarget?: ConsumeSourceLocationRequestTarget; + usedNames?: Iterable; + handleUnexpectedDiagnostic?: ConsumerHandleUnexpected; + onDefinition?: ConsumerOnDefinition; + propertyMetadata?: ConsumePropertyMetadata; + filePath?: UnknownFilePath; + objectPath: ConsumePath; + context: ConsumeContext; + value: unknown; + parent?: Consumer; + forceDiagnosticTarget?: ConsumeSourceLocationRequestTarget; }; diff --git a/packages/@romejs/core/client/Client.ts b/packages/@romejs/core/client/Client.ts index 967b7080fc2..c13615fe8a2 100644 --- a/packages/@romejs/core/client/Client.ts +++ b/packages/@romejs/core/client/Client.ts @@ -6,23 +6,23 @@ */ import { - ClientFlags, - ClientFlagsJSON, - DEFAULT_CLIENT_FLAGS, + ClientFlags, + ClientFlagsJSON, + DEFAULT_CLIENT_FLAGS, } from '../common/types/client'; import ClientRequest, {ClientRequestType} from './ClientRequest'; import Master from '../master/Master'; import { - CLI_SOCKET_PATH, - MasterBridge, - MasterQueryResponse, - SOCKET_PATH, + CLI_SOCKET_PATH, + MasterBridge, + MasterQueryResponse, + SOCKET_PATH, } from '@romejs/core'; import fork from '../common/utils/fork'; import { - Event, - createBridgeFromLocal, - createBridgeFromSocket, + Event, + createBridgeFromLocal, + createBridgeFromSocket, } from '@romejs/events'; import {Reporter, ReporterDerivedStreams} from '@romejs/cli-reporter'; import prettyFormat from '@romejs/pretty-format'; @@ -31,636 +31,623 @@ import {TarWriter} from '@romejs/codec-tar'; import {Profile, Profiler, Trace, TraceEvent} from '@romejs/v8'; import {PartialMasterQueryRequest} from '../common/bridges/MasterBridge'; import {UserConfig, loadUserConfig} from '../common/userConfig'; +import {unlink} from '@romejs/fs'; +import {stringifyJSON} from '@romejs/codec-json'; import stream = require('stream'); - import net = require('net'); - import zlib = require('zlib'); - import fs = require('fs'); - -import {unlink} from '@romejs/fs'; -import {JSONValue} from '@romejs/codec-json'; import child = require('child_process'); export function getFilenameTimestamp(): string { - return new Date().toISOString().replace(/[^0-9a-zA-Z]/g, ''); + return new Date().toISOString().replace(/[^0-9a-zA-Z]/g, ''); } const NEW_SERVER_INIT_TIMEOUT = 10_000; type ClientOptions = { - globalErrorHandlers?: boolean; - stdout?: stream.Writable; - stderr?: stream.Writable; - stdin?: NodeJS.ReadStream; - flags: Partial>; + globalErrorHandlers?: boolean; + stdout?: stream.Writable; + stderr?: stream.Writable; + stdin?: NodeJS.ReadStream; + flags: Partial>; }; export type ClientProfileOptions = { - samplingInterval: number; - timeoutInterval: undefined | number; - includeWorkers: boolean; + samplingInterval: number; + timeoutInterval: undefined | number; + includeWorkers: boolean; }; type BridgeStatus = { - bridge: MasterBridge; - dedicated: boolean; + bridge: MasterBridge; + dedicated: boolean; }; type ClientRequestResponseResult = { - request: PartialMasterQueryRequest; - response: MasterQueryResponse; + request: PartialMasterQueryRequest; + response: MasterQueryResponse; }; export default class Client { - constructor(opts: ClientOptions) { - this.options = opts; - this.userConfig = loadUserConfig(); - this.queryCounter = 0; - - this.flags = { - ...DEFAULT_CLIENT_FLAGS, - ...opts.flags, - }; - - this.requestResponseEvent = new Event({ - name: 'Client.requestResponseEvent', - }); - this.endEvent = new Event({name: 'Client.endEvent', serial: true}); - this.bridgeStatus = undefined; - - this.bridgeAttachedEvent = new Event({ - name: 'Client.bridgeAttached', - }); - - this.reporter = new Reporter({ - stdin: opts.stdin, - verbose: this.flags.verbose === true, - markupOptions: { - cwd: this.flags.cwd, - }, - }); - - // Suppress stdout when silent is set - const isSilent = - this.flags.silent === true || - opts.stdout === undefined || - opts.stderr === undefined; - const stdout = isSilent ? undefined : opts.stdout; - - this.derivedReporterStreams = this.reporter.attachStdoutStreams( - stdout, - opts.stderr, - ); - - this.endEvent.subscribe(() => { - this.reporter.teardown(); - }); - } - - queryCounter: number; - userConfig: UserConfig; - options: ClientOptions; - flags: ClientFlags; - reporter: Reporter; - derivedReporterStreams: ReporterDerivedStreams; - bridgeStatus: undefined | BridgeStatus; - bridgeAttachedEvent: Event; - - requestResponseEvent: Event; - endEvent: Event; - - setFlags(flags: Partial) { - if (this.bridgeStatus !== undefined) { - throw new Error( - 'Already connected to bridge. Cannot change client flags.', - ); - } - - this.flags = { - ...this.flags, - ...flags, - }; - } - - getClientJSONFlags(): ClientFlagsJSON { - return { - ...this.flags, - cwd: this.flags.cwd.join(), - }; - } - - async profile( - opts: ClientProfileOptions, - callback: (profile: Array) => Promise, - ) { - const {samplingInterval, timeoutInterval, includeWorkers} = opts; - - this.reporter.info('Starting CPU profile...'); - - // Start server and start profiling - const bridge = await this.findOrStartMaster(); - await bridge.profilingStart.call({ - samplingInterval, - }); - - // Start cli profiling - let cliProfiler: undefined | Profiler; - const bridgeStatus = this.getBridge(); - if (bridgeStatus === undefined || bridgeStatus.dedicated) { - cliProfiler = new Profiler(); - await cliProfiler.startProfiling(samplingInterval); - } - - // Start a profile timer if one was specified - let hasProfiled: undefined | Promise; - let timeout: undefined | NodeJS.Timeout; - if (timeoutInterval !== undefined) { - timeout = setTimeout( - () => { - hasProfiled = stopProfile(true); - }, - timeoutInterval, - ); - } - - const stopProfile = async (isTimeout: boolean) => { - // This is to prevent stopping the profile multiple times via the timeout and then at the end - // It's a promise so that the final stopProfile call will block until the first has finished - if (hasProfiled) { - return hasProfiled; - } - - // Stop the timeout if it hasn't been triggered - if (timeout !== undefined) { - clearTimeout(timeout); - } - - // - const trace = new Trace(); - const fetchers: Array<[string, () => Promise]> = []; - - // CLI - if (cliProfiler !== undefined) { - const cliProfilerAssert = cliProfiler; - fetchers.push([ - 'CLI', - async () => { - return cliProfilerAssert.stopProfiling(); - }, - ]); - } - - // Master - fetchers.push([ - cliProfiler === undefined ? 'Master/CLI' : 'Master', - async () => { - return await bridge.profilingStop.call( - undefined, - { - priority: true, - }, - ); - }, - ]); - - // Workers - if (includeWorkers) { - const workerIds = await bridge.profilingGetWorkers.call(); - for (const id of workerIds) { - fetchers.push([ - `Worker ${id}`, - async () => { - return await bridge.profilingStopWorker.call( - id, - { - priority: true, - }, - ); - }, - ]); - } - } - - // Fetch profiles - const progress = this.reporter.progress({title: 'Fetching profiles'}); - progress.setTotal(fetchers.length); - for (const [text, callback] of fetchers) { - progress.setText(text); - const profile = await callback(); - trace.addProfile(text, profile); - progress.tick(); - } - progress.end(); - - const events = trace.build(); - await callback(events); - - // If we're a timeout than separate these logs from the - if (isTimeout) { - this.reporter.hr(); - } - }; - - this.endEvent.subscribe(() => { - return stopProfile(false); - }); - } - - async subscribeLogs( - includeWorkerLogs: boolean, - callback: (chunk: string) => void, - ): Promise { - const bridge = await this.findOrStartMaster(); - - if (includeWorkerLogs) { - await bridge.enableWorkerLogs.call(); - } - - bridge.log.subscribe(({origin, chunk}) => { - if (origin === 'worker' && !includeWorkerLogs) { - // We allow multiple calls to bridge.enableWorkerLogs - // Filter the event if necessary if it wasn't requested by this log subscription - return; - } - - callback(chunk); - }); - } - - async rage(ragePath: string, profileOpts: ClientProfileOptions) { - if (this.bridgeStatus !== undefined) { - throw new Error( - 'rage() can only be called before a query has been dispatched', - ); - } - - let logs = ''; - await this.subscribeLogs( - true, - (chunk) => { - logs += chunk; - }, - ); - - // Collect CPU profile - // Callback will be called later once it has been collected - // Initial async work is just connecting to the processes and setting up handlers - let profileEvents: Array = []; - await this.profile( - profileOpts, - async (_profileEvents) => { - profileEvents = _profileEvents; - }, - ); - - // Collect all responses - const responses: Array = []; - this.requestResponseEvent.subscribe((result) => { - responses.push(result); - }); - - this.endEvent.subscribe(async () => { - const stream = zlib.createGzip(); - stream.pipe(fs.createWriteStream(ragePath)); - - const writer = new TarWriter(stream); - - writer.append({name: 'profile.json'}, stringify(profileEvents)); - writer.append({name: 'logs.txt'}, logs); - - // Add requests - for (let i = 0; i < responses.length; i++) { - const {request, response} = responses[i]; - const dirname = `requests/${i}-${request.commandName}`; - writer.append({name: `${dirname}/request.json`}, stringify(request)); - writer.append({name: `${dirname}/response.json`}, stringify(response)); - } - - // Add client flags - writer.append( - {name: 'clientFlags.json'}, - stringify(this.getClientJSONFlags()), - ); - - function stringify(val: JSONValue): string { - return JSON.stringify(val, null, ' '); - } - - function indent(val: unknown): string { - const str = - typeof val === 'string' - ? val - : prettyFormat( - val, - { - compact: true, - }, - ); - const lines = str.trim().split('\n'); - const indented = lines.join('\n '); - return `\n ${indented}`; - } - - const env = []; - env.push(`PATH: ${indent(process.env.PATH)}`); - env.push(`Rome version: ${indent(VERSION)}`); - env.push(`Node version: ${indent(process.versions.node)}`); - env.push(`Platform: ${indent(`${process.platform} ${process.arch}`)}`); - writer.append({name: 'environment.txt'}, `${env.join('\n\n')}\n`); - - // Don't do this if we never connected to the master - const bridgeStatus = this.getBridge(); - if (bridgeStatus !== undefined) { - const status = await this.query({ - silent: true, - commandName: 'status', - }); - if (status.type === 'SUCCESS') { - writer.append( - {name: 'status.txt'}, - `${prettyFormat( - status.data, - { - compact: true, - }, - )}\n`, - ); - } - } - - await writer.finalize(); - this.reporter.success('Rage archive written to', ragePath); - }); - } - - async query( - query: PartialMasterQueryRequest, - type?: ClientRequestType, - ): Promise { - const request = new ClientRequest(this, type, query); - const res = await request.init(); - this.requestResponseEvent.send({request: query, response: res}); - return res; - } - - cancellableQuery( - query: PartialMasterQueryRequest, - type?: ClientRequestType, - ): { - promise: Promise; - cancel: () => Promise; - } { - const cancelToken = String(this.queryCounter++); - - return { - promise: this.query( - { - ...query, - cancelToken, - }, - type, - ), - cancel: async () => { - const status = this.getBridge(); - if (status !== undefined) { - await status.bridge.cancelQuery.call(cancelToken); - } - }, - }; - } - - getBridge(): undefined | BridgeStatus { - return this.bridgeStatus; - } - - async end() { - await this.endEvent.callOptional(); - - const status = this.bridgeStatus; - if (status !== undefined) { - status.bridge.end(); - this.bridgeStatus = undefined; - } - } - - async attachBridge(bridge: MasterBridge, dedicated: boolean) { - const {stdout, stderr, columnsUpdated} = this.derivedReporterStreams; - - if (this.bridgeStatus !== undefined) { - throw new Error('Already attached bridge to API'); - } - - this.bridgeStatus = {bridge, dedicated}; - - bridge.stderr.subscribe((chunk) => { - stderr.write(chunk); - }); - - bridge.stdout.subscribe((chunk) => { - stdout.write(chunk); - }); - - bridge.reporterRemoteServerMessage.subscribe((msg) => { - this.reporter.processRemoteClientMessage(msg); - }); - - this.reporter.sendRemoteServerMessage.subscribe((msg) => { - bridge.reporterRemoteClientMessage.send(msg); - }); - - // Listen for resize column events if stdout is a TTY - columnsUpdated.subscribe((columns: number) => { - bridge.setColumns.call(columns); - }); - - await Promise.all([ - bridge.getClientInfo.wait({ - version: VERSION, - format: stdout.format, - unicode: stdout.unicode, - hasClearScreen: this.reporter.hasClearScreen, - columns: stdout.columns, - useRemoteReporter: true, - flags: this.getClientJSONFlags(), - }), - bridge.handshake(), - ]); - - await this.bridgeAttachedEvent.call(); - } - - async findOrStartMaster(): Promise { - // First check if we already have a bridge connection - const connected = this.getBridge(); - if (connected !== undefined) { - return connected.bridge; - } - - // Then check if there's already a running daemon - const runningDaemon = await this.tryConnectToExistingDaemon(); - if (runningDaemon) { - return runningDaemon; - } - - // Otherwise, start a master inside this process - const master = new Master({ - dedicated: false, - globalErrorHandlers: this.options.globalErrorHandlers === true, - }); - await master.init(); - - const bridge = createBridgeFromLocal(MasterBridge, {}); - await Promise.all([ - master.attachToBridge(bridge), - this.attachBridge(bridge, false), - ]); - - this.endEvent.subscribe(async () => { - await master.end(); - }); - - return bridge; - } - - async forceStartDaemon(): Promise { - const daemon = await this.startDaemon(); - if (daemon === undefined) { - this.reporter.error('Failed to start daemon'); - throw new Error('Failed to start daemon'); - } else { - return daemon; - } - } - - async startDaemon(): Promise { - const {reporter} = this; - - if (this.bridgeStatus !== undefined) { - throw new Error('Already started master'); - } - - reporter.info('No running daemon found. Starting one...'); - - let exited = false; - let proc: undefined | child.ChildProcess; - - const newDaemon: undefined | MasterBridge = await new Promise((resolve) => { - const timeout = setTimeout( - () => { - reporter.error('Daemon connection timed out'); - cleanup(); - resolve(); - }, - NEW_SERVER_INIT_TIMEOUT, - ); - - const socketServer = net.createServer(() => { - cleanup(); - - resolve( - this.tryConnectToExistingDaemon().then((bridge) => { - if (bridge !== undefined) { - this.reporter.success(`Started daemon!`); - } - return bridge; - }), - ); - }); - - function listen() { - socketServer.listen(CLI_SOCKET_PATH.join()); - - proc = fork( - 'master', - { - detached: true, - }, - ); - proc.unref(); - - proc.on( - 'close', - () => { - exited = true; - cleanup(); - resolve(); - }, - ); - } - - unlink(CLI_SOCKET_PATH).finally(() => { - listen(); - }); - - function cleanup() { - clearTimeout(timeout); - socketServer.close(); - } - }); - if (newDaemon) { - return newDaemon; - } - - // as a final precaution kill the server - if (exited) { - reporter.error('Daemon died while initialising.'); - } else { - reporter.error('Failed to connect. Killing daemon.'); - } - - if (proc !== undefined) { - proc.kill(); - } - - return undefined; - } - - async tryConnectToExistingDaemon(): Promise { - const promise: Promise = new Promise(( - resolve, - reject, - ) => { - const socket = net.createConnection( - { - path: SOCKET_PATH.join(), - }, - () => { - resolve(socket); - }, - ); - - socket.on( - 'error', - (err: NodeJS.ErrnoException) => { - if ( - err.code === 'ENOENT' || - err.code === 'ECONNREFUSED' || - err.code === 'EADDRINUSE' - ) { - resolve(); - } else { - reject(err); - } - }, - ); - }); - - const socket = await promise; - if (socket === undefined) { - return undefined; - } - - const server = createBridgeFromSocket( - MasterBridge, - socket, - { - type: 'server', - }, - ); - await this.attachBridge(server, true); - this.reporter.success('Connected to daemon'); - return server; - } + constructor(opts: ClientOptions) { + this.options = opts; + this.userConfig = loadUserConfig(); + this.queryCounter = 0; + + this.flags = { + ...DEFAULT_CLIENT_FLAGS, + ...opts.flags, + }; + + this.requestResponseEvent = new Event({ + name: 'Client.requestResponseEvent', + }); + this.endEvent = new Event({name: 'Client.endEvent', serial: true}); + this.bridgeStatus = undefined; + + this.bridgeAttachedEvent = new Event({ + name: 'Client.bridgeAttached', + }); + + this.reporter = new Reporter({ + stdin: opts.stdin, + verbose: this.flags.verbose === true, + markupOptions: { + cwd: this.flags.cwd, + }, + }); + + // Suppress stdout when silent is set + const isSilent = + this.flags.silent === true || + opts.stdout === undefined || + opts.stderr === undefined; + const stdout = isSilent ? undefined : opts.stdout; + + this.derivedReporterStreams = this.reporter.attachStdoutStreams( + stdout, + opts.stderr, + ); + + this.endEvent.subscribe(() => { + this.reporter.teardown(); + }); + } + + queryCounter: number; + userConfig: UserConfig; + options: ClientOptions; + flags: ClientFlags; + reporter: Reporter; + derivedReporterStreams: ReporterDerivedStreams; + bridgeStatus: undefined | BridgeStatus; + bridgeAttachedEvent: Event; + + requestResponseEvent: Event; + endEvent: Event; + + setFlags(flags: Partial) { + if (this.bridgeStatus !== undefined) { + throw new Error('Already connected to bridge. Cannot change client flags.'); + } + + this.flags = { + ...this.flags, + ...flags, + }; + } + + getClientJSONFlags(): ClientFlagsJSON { + return { + ...this.flags, + cwd: this.flags.cwd.join(), + }; + } + + async profile( + opts: ClientProfileOptions, + callback: (profile: Array) => Promise, + ) { + const {samplingInterval, timeoutInterval, includeWorkers} = opts; + + this.reporter.info('Starting CPU profile...'); + + // Start server and start profiling + const bridge = await this.findOrStartMaster(); + await bridge.profilingStart.call({ + samplingInterval, + }); + + // Start cli profiling + let cliProfiler: undefined | Profiler; + const bridgeStatus = this.getBridge(); + if (bridgeStatus === undefined || bridgeStatus.dedicated) { + cliProfiler = new Profiler(); + await cliProfiler.startProfiling(samplingInterval); + } + + // Start a profile timer if one was specified + let hasProfiled: undefined | Promise; + let timeout: undefined | NodeJS.Timeout; + if (timeoutInterval !== undefined) { + timeout = setTimeout( + () => { + hasProfiled = stopProfile(true); + }, + timeoutInterval, + ); + } + + const stopProfile = async (isTimeout: boolean) => { + // This is to prevent stopping the profile multiple times via the timeout and then at the end + // It's a promise so that the final stopProfile call will block until the first has finished + if (hasProfiled) { + return hasProfiled; + } + + // Stop the timeout if it hasn't been triggered + if (timeout !== undefined) { + clearTimeout(timeout); + } + + // + const trace = new Trace(); + const fetchers: Array<[string, () => Promise]> = []; + + // CLI + if (cliProfiler !== undefined) { + const cliProfilerAssert = cliProfiler; + fetchers.push([ + 'CLI', + async () => { + return cliProfilerAssert.stopProfiling(); + }, + ]); + } + + // Master + fetchers.push([ + cliProfiler === undefined ? 'Master/CLI' : 'Master', + async () => { + return await bridge.profilingStop.call( + undefined, + { + priority: true, + }, + ); + }, + ]); + + // Workers + if (includeWorkers) { + const workerIds = await bridge.profilingGetWorkers.call(); + for (const id of workerIds) { + fetchers.push([ + `Worker ${id}`, + async () => { + return await bridge.profilingStopWorker.call( + id, + { + priority: true, + }, + ); + }, + ]); + } + } + + // Fetch profiles + const progress = this.reporter.progress({title: 'Fetching profiles'}); + progress.setTotal(fetchers.length); + for (const [text, callback] of fetchers) { + progress.setText(text); + const profile = await callback(); + trace.addProfile(text, profile); + progress.tick(); + } + progress.end(); + + const events = trace.build(); + await callback(events); + + // If we're a timeout than separate these logs from the + if (isTimeout) { + this.reporter.hr(); + } + }; + + this.endEvent.subscribe(() => { + return stopProfile(false); + }); + } + + async subscribeLogs( + includeWorkerLogs: boolean, + callback: (chunk: string) => void, + ): Promise { + const bridge = await this.findOrStartMaster(); + + if (includeWorkerLogs) { + await bridge.enableWorkerLogs.call(); + } + + bridge.log.subscribe(({origin, chunk}) => { + if (origin === 'worker' && !includeWorkerLogs) { + // We allow multiple calls to bridge.enableWorkerLogs + // Filter the event if necessary if it wasn't requested by this log subscription + return; + } + + callback(chunk); + }); + } + + async rage(ragePath: string, profileOpts: ClientProfileOptions) { + if (this.bridgeStatus !== undefined) { + throw new Error( + 'rage() can only be called before a query has been dispatched', + ); + } + + let logs = ''; + await this.subscribeLogs( + true, + (chunk) => { + logs += chunk; + }, + ); + + // Collect CPU profile + // Callback will be called later once it has been collected + // Initial async work is just connecting to the processes and setting up handlers + let profileEvents: Array = []; + await this.profile( + profileOpts, + async (_profileEvents) => { + profileEvents = _profileEvents; + }, + ); + + // Collect all responses + const responses: Array = []; + this.requestResponseEvent.subscribe((result) => { + responses.push(result); + }); + + this.endEvent.subscribe(async () => { + const stream = zlib.createGzip(); + stream.pipe(fs.createWriteStream(ragePath)); + + const writer = new TarWriter(stream); + + writer.append({name: 'profile.json'}, stringifyJSON(profileEvents)); + writer.append({name: 'logs.txt'}, logs); + + // Add requests + for (let i = 0; i < responses.length; i++) { + const {request, response} = responses[i]; + const dirname = `requests/${i}-${request.commandName}`; + writer.append({name: `${dirname}/request.json`}, stringifyJSON(request)); + writer.append({name: `${dirname}/response.json`}, stringifyJSON(response)); + } + + // Add client flags + writer.append( + {name: 'clientFlags.json'}, + stringifyJSON(this.getClientJSONFlags()), + ); + + function indent(val: unknown): string { + const str = + typeof val === 'string' + ? val + : prettyFormat( + val, + { + compact: true, + }, + ); + const lines = str.trim().split('\n'); + const indented = lines.join('\n '); + return `\n ${indented}`; + } + + const env = []; + env.push(`PATH: ${indent(process.env.PATH)}`); + env.push(`Rome version: ${indent(VERSION)}`); + env.push(`Node version: ${indent(process.versions.node)}`); + env.push(`Platform: ${indent(`${process.platform} ${process.arch}`)}`); + writer.append({name: 'environment.txt'}, `${env.join('\n\n')}\n`); + + // Don't do this if we never connected to the master + const bridgeStatus = this.getBridge(); + if (bridgeStatus !== undefined) { + const status = await this.query({ + silent: true, + commandName: 'status', + }); + if (status.type === 'SUCCESS') { + writer.append( + {name: 'status.txt'}, + `${prettyFormat( + status.data, + { + compact: true, + }, + )}\n`, + ); + } + } + + await writer.finalize(); + this.reporter.success('Rage archive written to', ragePath); + }); + } + + async query( + query: PartialMasterQueryRequest, + type?: ClientRequestType, + ): Promise { + const request = new ClientRequest(this, type, query); + const res = await request.init(); + this.requestResponseEvent.send({request: query, response: res}); + return res; + } + + cancellableQuery( + query: PartialMasterQueryRequest, + type?: ClientRequestType, + ): { + promise: Promise; + cancel: () => Promise; + } { + const cancelToken = String(this.queryCounter++); + + return { + promise: this.query( + { + ...query, + cancelToken, + }, + type, + ), + cancel: async () => { + const status = this.getBridge(); + if (status !== undefined) { + await status.bridge.cancelQuery.call(cancelToken); + } + }, + }; + } + + getBridge(): undefined | BridgeStatus { + return this.bridgeStatus; + } + + async end() { + await this.endEvent.callOptional(); + + const status = this.bridgeStatus; + if (status !== undefined) { + status.bridge.end(); + this.bridgeStatus = undefined; + } + } + + async attachBridge(bridge: MasterBridge, dedicated: boolean) { + const {stdout, stderr, columnsUpdated} = this.derivedReporterStreams; + + if (this.bridgeStatus !== undefined) { + throw new Error('Already attached bridge to API'); + } + + this.bridgeStatus = {bridge, dedicated}; + + bridge.stderr.subscribe((chunk) => { + stderr.write(chunk); + }); + + bridge.stdout.subscribe((chunk) => { + stdout.write(chunk); + }); + + bridge.reporterRemoteServerMessage.subscribe((msg) => { + this.reporter.processRemoteClientMessage(msg); + }); + + this.reporter.sendRemoteServerMessage.subscribe((msg) => { + bridge.reporterRemoteClientMessage.send(msg); + }); + + // Listen for resize column events if stdout is a TTY + columnsUpdated.subscribe((columns: number) => { + bridge.setColumns.call(columns); + }); + + await Promise.all([ + bridge.getClientInfo.wait({ + version: VERSION, + format: stdout.format, + unicode: stdout.unicode, + hasClearScreen: this.reporter.hasClearScreen, + columns: stdout.columns, + useRemoteReporter: true, + flags: this.getClientJSONFlags(), + }), + bridge.handshake(), + ]); + + await this.bridgeAttachedEvent.call(); + } + + async findOrStartMaster(): Promise { + // First check if we already have a bridge connection + const connected = this.getBridge(); + if (connected !== undefined) { + return connected.bridge; + } + + // Then check if there's already a running daemon + const runningDaemon = await this.tryConnectToExistingDaemon(); + if (runningDaemon) { + return runningDaemon; + } + + // Otherwise, start a master inside this process + const master = new Master({ + dedicated: false, + globalErrorHandlers: this.options.globalErrorHandlers === true, + }); + await master.init(); + + const bridge = createBridgeFromLocal(MasterBridge, {}); + await Promise.all([ + master.attachToBridge(bridge), + this.attachBridge(bridge, false), + ]); + + this.endEvent.subscribe(async () => { + await master.end(); + }); + + return bridge; + } + + async forceStartDaemon(): Promise { + const daemon = await this.startDaemon(); + if (daemon === undefined) { + this.reporter.error('Failed to start daemon'); + throw new Error('Failed to start daemon'); + } else { + return daemon; + } + } + + async startDaemon(): Promise { + const {reporter} = this; + + if (this.bridgeStatus !== undefined) { + throw new Error('Already started master'); + } + + reporter.info('No running daemon found. Starting one...'); + + let exited = false; + let proc: undefined | child.ChildProcess; + + const newDaemon: undefined | MasterBridge = await new Promise((resolve) => { + const timeout = setTimeout( + () => { + reporter.error('Daemon connection timed out'); + cleanup(); + resolve(); + }, + NEW_SERVER_INIT_TIMEOUT, + ); + + const socketServer = net.createServer(() => { + cleanup(); + + resolve( + this.tryConnectToExistingDaemon().then((bridge) => { + if (bridge !== undefined) { + this.reporter.success(`Started daemon!`); + } + return bridge; + }), + ); + }); + + function listen() { + socketServer.listen(CLI_SOCKET_PATH.join()); + + proc = fork( + 'master', + { + detached: true, + }, + ); + proc.unref(); + + proc.on( + 'close', + () => { + exited = true; + cleanup(); + resolve(); + }, + ); + } + + unlink(CLI_SOCKET_PATH).finally(() => { + listen(); + }); + + function cleanup() { + clearTimeout(timeout); + socketServer.close(); + } + }); + if (newDaemon) { + return newDaemon; + } + + // as a final precaution kill the server + if (exited) { + reporter.error('Daemon died while initialising.'); + } else { + reporter.error('Failed to connect. Killing daemon.'); + } + + if (proc !== undefined) { + proc.kill(); + } + + return undefined; + } + + async tryConnectToExistingDaemon(): Promise { + const promise: Promise = new Promise((resolve, reject) => { + const socket = net.createConnection( + { + path: SOCKET_PATH.join(), + }, + () => { + resolve(socket); + }, + ); + + socket.on( + 'error', + (err: NodeJS.ErrnoException) => { + if ( + err.code === 'ENOENT' || + err.code === 'ECONNREFUSED' || + err.code === 'EADDRINUSE' + ) { + resolve(); + } else { + reject(err); + } + }, + ); + }); + + const socket = await promise; + if (socket === undefined) { + return undefined; + } + + const server = createBridgeFromSocket( + MasterBridge, + socket, + { + type: 'server', + }, + ); + await this.attachBridge(server, true); + this.reporter.success('Connected to daemon'); + return server; + } } diff --git a/packages/@romejs/core/client/ClientRequest.ts b/packages/@romejs/core/client/ClientRequest.ts index a3a4321a1ab..73f80ab07e2 100644 --- a/packages/@romejs/core/client/ClientRequest.ts +++ b/packages/@romejs/core/client/ClientRequest.ts @@ -6,8 +6,8 @@ */ import { - MasterQueryResponse, - PartialMasterQueryRequest, + MasterQueryResponse, + PartialMasterQueryRequest, } from '../common/bridges/MasterBridge'; import {LocalCommand, localCommands} from './commands'; import Client from './Client'; @@ -18,113 +18,113 @@ import review from './review'; export type ClientRequestType = 'local' | 'master'; export default class ClientRequest { - constructor( - client: Client, - type: ClientRequestType = 'local', - query: PartialMasterQueryRequest, - ) { - this.client = client; - this.type = type; - this.query = query; - } + constructor( + client: Client, + type: ClientRequestType = 'local', + query: PartialMasterQueryRequest, + ) { + this.client = client; + this.type = type; + this.query = query; + } - query: PartialMasterQueryRequest; - type: ClientRequestType; - client: Client; + query: PartialMasterQueryRequest; + type: ClientRequestType; + client: Client; - fork(query: PartialMasterQueryRequest): ClientRequest { - return new ClientRequest(this.client, this.type, query); - } + fork(query: PartialMasterQueryRequest): ClientRequest { + return new ClientRequest(this.client, this.type, query); + } - async init(): Promise { - try { - const {requestFlags} = this.query; - if (requestFlags !== undefined && requestFlags.review) { - return await this.initReview(); - } else { - return await this.initCommand(); - } - } catch (err) { - return { - type: 'ERROR', - fatal: false, - handled: false, - name: err.name, - message: err.message, - stack: err.stack, - }; - } - } + async init(): Promise { + try { + const {requestFlags} = this.query; + if (requestFlags !== undefined && requestFlags.review) { + return await this.initReview(); + } else { + return await this.initCommand(); + } + } catch (err) { + return { + type: 'ERROR', + fatal: false, + handled: false, + name: err.name, + message: err.message, + stack: err.stack, + }; + } + } - async initReview(): Promise { - return review(this); - } + async initReview(): Promise { + return review(this); + } - async initCommand(): Promise { - const localCommand = localCommands.get(this.query.commandName); + async initCommand(): Promise { + const localCommand = localCommands.get(this.query.commandName); - if (this.type === 'master' || localCommand === undefined) { - return this.initFromMaster(); - } else { - return this.initFromLocal(localCommand); - } - } + if (this.type === 'master' || localCommand === undefined) { + return this.initFromMaster(); + } else { + return this.initFromLocal(localCommand); + } + } - async initFromLocal( - // rome-ignore lint/noExplicitAny - localCommand: LocalCommand, - ): Promise { - const {query} = this; + async initFromLocal( + // rome-ignore lint/noExplicitAny + localCommand: LocalCommand, + ): Promise { + const {query} = this; - let flags; - if (localCommand.defineFlags !== undefined) { - flags = localCommand.defineFlags( - consumeUnknown(query.commandFlags, 'flags/invalid'), - ); - } + let flags; + if (localCommand.defineFlags !== undefined) { + flags = localCommand.defineFlags( + consumeUnknown(query.commandFlags, 'flags/invalid'), + ); + } - const res = await localCommand.callback(this, flags); - if (res === true) { - return { - type: 'SUCCESS', - data: undefined, - hasData: false, - markers: [], - }; - } else if (res === false) { - return { - type: 'ERROR', - fatal: false, - // Local command would have printed something - handled: true, - name: 'Error', - message: 'Command was not successful', - stack: undefined, - }; - } else { - return res; - } - } + const res = await localCommand.callback(this, flags); + if (res === true) { + return { + type: 'SUCCESS', + data: undefined, + hasData: false, + markers: [], + }; + } else if (res === false) { + return { + type: 'ERROR', + fatal: false, + // Local command would have printed something + handled: true, + name: 'Error', + message: 'Command was not successful', + stack: undefined, + }; + } else { + return res; + } + } - async initFromMaster(): Promise { - const {client} = this; + async initFromMaster(): Promise { + const {client} = this; - try { - const bridge = await client.findOrStartMaster(); - return await bridge.query.call(this.query); - } catch (err) { - if (err instanceof BridgeError) { - return { - type: 'ERROR', - fatal: true, - handled: false, - name: 'Error', - message: 'Server died while processing command. Results may be incomplete.', - stack: undefined, - }; - } else { - throw err; - } - } - } + try { + const bridge = await client.findOrStartMaster(); + return await bridge.query.call(this.query); + } catch (err) { + if (err instanceof BridgeError) { + return { + type: 'ERROR', + fatal: true, + handled: false, + name: 'Error', + message: 'Server died while processing command. Results may be incomplete.', + stack: undefined, + }; + } else { + throw err; + } + } + } } diff --git a/packages/@romejs/core/client/commands.ts b/packages/@romejs/core/client/commands.ts index 4f6782df6c7..5a4bb9a41ec 100644 --- a/packages/@romejs/core/client/commands.ts +++ b/packages/@romejs/core/client/commands.ts @@ -21,16 +21,16 @@ import {SharedCommand} from '../common/commands'; import {MasterQueryResponse} from '../common/bridges/MasterBridge'; export type LocalCommand> = SharedCommand & { - callback: ( - req: ClientRequest, - commandFlags: Flags, - ) => Promise; + callback: ( + req: ClientRequest, + commandFlags: Flags, + ) => Promise; }; export function createLocalCommand>( - cmd: LocalCommand, + cmd: LocalCommand, ): LocalCommand { - return cmd; + return cmd; } // rome-ignore lint/noExplicitAny diff --git a/packages/@romejs/core/client/commands/develop.ts b/packages/@romejs/core/client/commands/develop.ts index 27da6e6b85f..2c4aff6bc8e 100644 --- a/packages/@romejs/core/client/commands/develop.ts +++ b/packages/@romejs/core/client/commands/develop.ts @@ -10,29 +10,29 @@ import {createLocalCommand} from '../commands'; import ClientRequest from '../ClientRequest'; export default createLocalCommand({ - category: commandCategories.PROCESS_MANAGEMENT, - description: 'TODO', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: ClientRequest) { - const existingServer = await req.client.tryConnectToExistingDaemon(); - const hasExistingServer = existingServer !== undefined; + category: commandCategories.PROCESS_MANAGEMENT, + description: 'TODO', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: ClientRequest) { + const existingServer = await req.client.tryConnectToExistingDaemon(); + const hasExistingServer = existingServer !== undefined; - if (!hasExistingServer) { - await req.client.forceStartDaemon(); - } + if (!hasExistingServer) { + await req.client.forceStartDaemon(); + } - await req.client.query( - { - ...req.query, - terminateWhenIdle: true, - }, - 'master', - ); + await req.client.query( + { + ...req.query, + terminateWhenIdle: true, + }, + 'master', + ); - return true; - }, + return true; + }, }); diff --git a/packages/@romejs/core/client/commands/init.ts b/packages/@romejs/core/client/commands/init.ts index e74a3c11c94..64b562a9bc6 100644 --- a/packages/@romejs/core/client/commands/init.ts +++ b/packages/@romejs/core/client/commands/init.ts @@ -15,42 +15,42 @@ import {Consumer} from '@romejs/consume'; import {stringifyRJSON} from '@romejs/codec-json'; export default createLocalCommand({ - category: commandCategories.PROJECT_MANAGEMENT, - description: 'create a project config', - usage: '', - examples: [], - defineFlags(consumer: Consumer) { - return {}; - }, - async callback(req: ClientRequest) { - const {reporter} = req.client; - - const configPath = req.client.flags.cwd.append('rome.rjson'); - if (await exists(configPath)) { - reporter.error( - `rome.rjson file already exists`, - ); - reporter.info( - 'Use rome config to update an existing config', - ); - return false; - } - - const config: Dict = { - version: `^${VERSION}`, - }; - await writeConfig(); - - async function writeConfig() { - await writeFile(configPath, stringifyRJSON(config)); - } - - // Run lint, capture diagnostics - - reporter.success( - `Created config `, - ); - - return true; - }, + category: commandCategories.PROJECT_MANAGEMENT, + description: 'create a project config', + usage: '', + examples: [], + defineFlags(consumer: Consumer) { + return {}; + }, + async callback(req: ClientRequest) { + const {reporter} = req.client; + + const configPath = req.client.flags.cwd.append('rome.rjson'); + if (await exists(configPath)) { + reporter.error( + `rome.rjson file already exists`, + ); + reporter.info( + 'Use rome config to update an existing config', + ); + return false; + } + + const config: Dict = { + version: `^${VERSION}`, + }; + await writeConfig(); + + async function writeConfig() { + await writeFile(configPath, stringifyRJSON(config)); + } + + // Run lint, capture diagnostics + + reporter.success( + `Created config `, + ); + + return true; + }, }); diff --git a/packages/@romejs/core/client/commands/lsp.ts b/packages/@romejs/core/client/commands/lsp.ts index 8791398247f..64ba2f848a0 100644 --- a/packages/@romejs/core/client/commands/lsp.ts +++ b/packages/@romejs/core/client/commands/lsp.ts @@ -10,47 +10,47 @@ import {createLocalCommand} from '../commands'; import ClientRequest from '../ClientRequest'; export default createLocalCommand({ - description: 'connect to an lsp', - category: commandCategories.PROJECT_MANAGEMENT, - usage: '', - examples: [], - // vscode-languageclient adds these on - ignoreFlags: ['stdio', 'clientProcessId'], - defineFlags() { - return {}; - }, - async callback(req: ClientRequest) { - req.client.setFlags({ - clientName: 'lsp', - silent: true, - }); - - const stdin = req.client.reporter.getStdin(); - req.client.reporter.teardown(); - - const bridge = await req.client.findOrStartMaster(); - if (bridge === undefined) { - return false; - } - - bridge.lspFromServerBuffer.subscribe((chunk) => { - req.client.derivedReporterStreams.stdout.write(chunk); - }); - - stdin.on( - 'data', - (chunk) => { - bridge.lspFromClientBuffer.call(chunk.toString()); - }, - ); - - await req.client.query( - { - commandName: 'lsp', - }, - 'master', - ); - - return true; - }, + description: 'connect to an lsp', + category: commandCategories.PROJECT_MANAGEMENT, + usage: '', + examples: [], + // vscode-languageclient adds these on + ignoreFlags: ['stdio', 'clientProcessId'], + defineFlags() { + return {}; + }, + async callback(req: ClientRequest) { + req.client.setFlags({ + clientName: 'lsp', + silent: true, + }); + + const stdin = req.client.reporter.getStdin(); + req.client.reporter.teardown(); + + const bridge = await req.client.findOrStartMaster(); + if (bridge === undefined) { + return false; + } + + bridge.lspFromServerBuffer.subscribe((chunk) => { + req.client.derivedReporterStreams.stdout.write(chunk); + }); + + stdin.on( + 'data', + (chunk) => { + bridge.lspFromClientBuffer.call(chunk.toString()); + }, + ); + + await req.client.query( + { + commandName: 'lsp', + }, + 'master', + ); + + return true; + }, }); diff --git a/packages/@romejs/core/client/commands/restart.ts b/packages/@romejs/core/client/commands/restart.ts index f154f04027e..9cfff5d4f0f 100644 --- a/packages/@romejs/core/client/commands/restart.ts +++ b/packages/@romejs/core/client/commands/restart.ts @@ -10,25 +10,25 @@ import {createLocalCommand} from '../commands'; import ClientRequest from '../ClientRequest'; export default createLocalCommand({ - category: commandCategories.PROCESS_MANAGEMENT, - description: 'restart daemon', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: ClientRequest) { - const stopped = await req.client.query({ - commandName: 'stop', - }); + category: commandCategories.PROCESS_MANAGEMENT, + description: 'restart daemon', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: ClientRequest) { + const stopped = await req.client.query({ + commandName: 'stop', + }); - if (stopped.type === 'SUCCESS' && stopped.data === true) { - const started = await req.client.query({ - commandName: 'start', - }); - return started.type === 'SUCCESS' && started.data === true; - } else { - return false; - } - }, + if (stopped.type === 'SUCCESS' && stopped.data === true) { + const started = await req.client.query({ + commandName: 'start', + }); + return started.type === 'SUCCESS' && started.data === true; + } else { + return false; + } + }, }); diff --git a/packages/@romejs/core/client/commands/run.ts b/packages/@romejs/core/client/commands/run.ts index 9467f586d4a..288af6e0c25 100644 --- a/packages/@romejs/core/client/commands/run.ts +++ b/packages/@romejs/core/client/commands/run.ts @@ -15,66 +15,66 @@ import {createSingleDiagnosticError} from '@romejs/diagnostics'; import {SourceMapConsumer} from '@romejs/codec-source-map'; export default createLocalCommand({ - category: commandCategories.PROJECT_MANAGEMENT, - description: 'TODO', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: ClientRequest) { - const bridge = await req.client.findOrStartMaster(); - if (bridge === undefined) { - return false; - } + category: commandCategories.PROJECT_MANAGEMENT, + description: 'TODO', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: ClientRequest) { + const bridge = await req.client.findOrStartMaster(); + if (bridge === undefined) { + return false; + } - process.on( - 'unhandledRejection', - (error) => { - error; - //console.log('unhandledRejection', error.stack); - }, - ); + process.on( + 'unhandledRejection', + (error) => { + error; + //console.log('unhandledRejection', error.stack); + }, + ); - const res = await req.client.query( - { - commandName: 'run', - args: req.query.args, - }, - 'master', - ); + const res = await req.client.query( + { + commandName: 'run', + args: req.query.args, + }, + 'master', + ); - if (res.type !== 'SUCCESS') { - return false; - } + if (res.type !== 'SUCCESS') { + return false; + } - const data = consumeUnknown(res.data, 'parse/json'); + const data = consumeUnknown(res.data, 'parse/json'); - if (data.exists()) { - const type = data.get('type').asString(); + if (data.exists()) { + const type = data.get('type').asString(); - switch (type) { - case 'executeCode': { - process.execArgv = [...process.execArgv, process.argv[1], 'run']; - process.argv = [ - process.argv[0], - String(data.filename), - ...process.argv.slice(4), - ]; - const {syntaxError} = await executeMain({ - path: createAbsoluteFilePath(data.get('filename').asString()), - code: data.get('code').asString(), - sourceMap: SourceMapConsumer.fromJSON(data.get('map').asAny()), - }); - if (syntaxError !== undefined) { - throw createSingleDiagnosticError(syntaxError); - } - await new Promise(() => {}); - break; - } - } - } + switch (type) { + case 'executeCode': { + process.execArgv = [...process.execArgv, process.argv[1], 'run']; + process.argv = [ + process.argv[0], + String(data.filename), + ...process.argv.slice(4), + ]; + const {syntaxError} = await executeMain({ + path: createAbsoluteFilePath(data.get('filename').asString()), + code: data.get('code').asString(), + sourceMap: SourceMapConsumer.fromJSON(data.get('map').asAny()), + }); + if (syntaxError !== undefined) { + throw createSingleDiagnosticError(syntaxError); + } + await new Promise(() => {}); + break; + } + } + } - return true; - }, + return true; + }, }); diff --git a/packages/@romejs/core/client/commands/start.ts b/packages/@romejs/core/client/commands/start.ts index 62100ff0646..f9e9576371e 100644 --- a/packages/@romejs/core/client/commands/start.ts +++ b/packages/@romejs/core/client/commands/start.ts @@ -10,21 +10,21 @@ import {createLocalCommand} from '../commands'; import ClientRequest from '../ClientRequest'; export default createLocalCommand({ - category: commandCategories.PROCESS_MANAGEMENT, - description: 'start daemon (if none running)', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: ClientRequest) { - const existingServer = await req.client.tryConnectToExistingDaemon(); - if (existingServer) { - req.client.reporter.success('Already running server.'); - return true; - } + category: commandCategories.PROCESS_MANAGEMENT, + description: 'start daemon (if none running)', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: ClientRequest) { + const existingServer = await req.client.tryConnectToExistingDaemon(); + if (existingServer) { + req.client.reporter.success('Already running server.'); + return true; + } - const bridge = await req.client.startDaemon(); - return bridge !== undefined; - }, + const bridge = await req.client.startDaemon(); + return bridge !== undefined; + }, }); diff --git a/packages/@romejs/core/client/commands/status.ts b/packages/@romejs/core/client/commands/status.ts index 4e9b19f6251..a41cb125421 100644 --- a/packages/@romejs/core/client/commands/status.ts +++ b/packages/@romejs/core/client/commands/status.ts @@ -10,32 +10,32 @@ import {createLocalCommand} from '../commands'; import ClientRequest from '../ClientRequest'; export default createLocalCommand({ - description: 'get the current daemon status', - category: commandCategories.PROCESS_MANAGEMENT, - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: ClientRequest) { - const {reporter} = req.client; - const bridge = await req.client.tryConnectToExistingDaemon(); - if (bridge) { - const status = await req.client.query( - { - commandName: 'status', - }, - 'master', - ); - if (status.type === 'SUCCESS') { - reporter.inspect(status.data); - return true; - } else { - return false; - } - } else { - reporter.error('Server not running.'); - return false; - } - }, + description: 'get the current daemon status', + category: commandCategories.PROCESS_MANAGEMENT, + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: ClientRequest) { + const {reporter} = req.client; + const bridge = await req.client.tryConnectToExistingDaemon(); + if (bridge) { + const status = await req.client.query( + { + commandName: 'status', + }, + 'master', + ); + if (status.type === 'SUCCESS') { + reporter.inspect(status.data); + return true; + } else { + return false; + } + } else { + reporter.error('Server not running.'); + return false; + } + }, }); diff --git a/packages/@romejs/core/client/commands/stop.ts b/packages/@romejs/core/client/commands/stop.ts index 34b48b63fa3..e75ecc7e2e1 100644 --- a/packages/@romejs/core/client/commands/stop.ts +++ b/packages/@romejs/core/client/commands/stop.ts @@ -10,33 +10,33 @@ import {createLocalCommand} from '../commands'; import ClientRequest from '../ClientRequest'; export default createLocalCommand({ - category: commandCategories.PROCESS_MANAGEMENT, - description: 'stop a running daemon if one exists', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: ClientRequest) { - // We might want to use `terminateWhenIdle` here combined with a timeout instead of forcing it to die straight away - const {reporter} = req.client; - const bridge = await req.client.tryConnectToExistingDaemon(); - if (bridge) { - const stop = await req.client.query( - { - commandName: 'stop', - }, - 'master', - ); - if (stop.type === 'ERROR' && stop.fatal) { - reporter.success('Stopped server.'); - } else { - reporter.error('Failed to stop server.'); - return false; - } - } else { - reporter.warn('No running server to stop.'); - } - return true; - }, + category: commandCategories.PROCESS_MANAGEMENT, + description: 'stop a running daemon if one exists', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: ClientRequest) { + // We might want to use `terminateWhenIdle` here combined with a timeout instead of forcing it to die straight away + const {reporter} = req.client; + const bridge = await req.client.tryConnectToExistingDaemon(); + if (bridge) { + const stop = await req.client.query( + { + commandName: 'stop', + }, + 'master', + ); + if (stop.type === 'ERROR' && stop.fatal) { + reporter.success('Stopped server.'); + } else { + reporter.error('Failed to stop server.'); + return false; + } + } else { + reporter.warn('No running server to stop.'); + } + return true; + }, }); diff --git a/packages/@romejs/core/client/review.ts b/packages/@romejs/core/client/review.ts index 93989bad9ab..ea51a50dc7a 100644 --- a/packages/@romejs/core/client/review.ts +++ b/packages/@romejs/core/client/review.ts @@ -9,9 +9,9 @@ import ClientRequest from './ClientRequest'; import {DiagnosticsPrinter, printDiagnostics} from '@romejs/cli-diagnostics'; import {SelectOption} from '@romejs/cli-reporter'; import { - Diagnostic, - DiagnosticAdviceAction, - derivePositionlessKeyFromDiagnostic, + Diagnostic, + DiagnosticAdviceAction, + derivePositionlessKeyFromDiagnostic, } from '@romejs/diagnostics'; import {MasterQueryResponse} from '../common/bridges/MasterBridge'; import {ClientRequestFlags} from '../common/types/client'; @@ -19,266 +19,266 @@ import {Dict} from '@romejs/typescript-helpers'; import {EMPTY_SUCCESS_RESPONSE} from '../master/MasterRequest'; type State = { - initial: boolean; - seen: Set; - resolvedCount: number; + initial: boolean; + seen: Set; + resolvedCount: number; }; async function check( - req: ClientRequest, - state: State, + req: ClientRequest, + state: State, ): Promise { - const {reporter} = req.client; - - reporter.clearScreen(); - - if (state.initial) { - reporter.info('Fetching initial diagnostics'); - state.initial = false; - } else { - reporter.info('Updating diagnostics'); - } - - const res = await req.fork({ - ...req.query, - // We want data no matter what - noData: false, - }).initCommand(); - - if (res.type === 'SUCCESS') { - throw new Error('Expected diagnostics or an error'); - } - - // In case it returned an error - if (res.type !== 'DIAGNOSTICS') { - return res; - } - - const diagnostics = res.diagnostics; - let diag: undefined | Diagnostic; - - for (const _diag of diagnostics) { - const key = derivePositionlessKeyFromDiagnostic(_diag); - if (!state.seen.has(key)) { - state.seen.add(key); - diag = _diag; - break; - } - } - - if (diag === undefined) { - return res; - } - - return await ask(diag, req, state, false); + const {reporter} = req.client; + + reporter.clearScreen(); + + if (state.initial) { + reporter.info('Fetching initial diagnostics'); + state.initial = false; + } else { + reporter.info('Updating diagnostics'); + } + + const res = await req.fork({ + ...req.query, + // We want data no matter what + noData: false, + }).initCommand(); + + if (res.type === 'SUCCESS') { + throw new Error('Expected diagnostics or an error'); + } + + // In case it returned an error + if (res.type !== 'DIAGNOSTICS') { + return res; + } + + const diagnostics = res.diagnostics; + let diag: undefined | Diagnostic; + + for (const _diag of diagnostics) { + const key = derivePositionlessKeyFromDiagnostic(_diag); + if (!state.seen.has(key)) { + state.seen.add(key); + diag = _diag; + break; + } + } + + if (diag === undefined) { + return res; + } + + return await ask(diag, req, state, false); } async function ask( - diag: Diagnostic, - req: ClientRequest, - state: State, - showMoreOptions: boolean, + diag: Diagnostic, + req: ClientRequest, + state: State, + showMoreOptions: boolean, ): Promise { - const {client} = req; - const {reporter} = client; - reporter.clearScreen(); - - // Extract actions and remove them from the diagnostic - let {advice = []} = diag.description; - let hasExtraOptions = false; - const actions: Array = []; - for (const item of advice) { - if (item.type === 'action') { - // Only show extra items and hide all non-extra items when `more === true` - if (item.extra === true) { - hasExtraOptions = true; - if (!showMoreOptions) { - continue; - } - } else if (showMoreOptions) { - continue; - } - - actions.push(item); - } - } - advice = advice.filter((item) => item.type !== 'action'); - diag = { - ...diag, - description: { - ...diag.description, - advice, - }, - }; - - const optionToAction: Map = new Map(); - const chosenShortcuts: Set = new Set(['n', 'escape']); - - const actionOptions: Dict = {}; - - let counter = 0; - for (const action of actions) { - const key = String(counter++); - let shortcut = - action.shortcut !== undefined && !chosenShortcuts.has(action.shortcut) - ? action.shortcut - : undefined; - optionToAction.set(key, action); - actionOptions[key] = { - label: action.noun, - shortcut, - }; - } - - const options: { - ignore: SelectOption; - exit: SelectOption; - more?: SelectOption; - less?: SelectOption; - } = { - ignore: { - label: 'Do nothing', - shortcut: 'n', - }, - ...actionOptions, - exit: { - label: 'Exit', - shortcut: 'escape', - }, - }; - - if (hasExtraOptions) { - if (showMoreOptions) { - options.more = { - label: 'Less options...', - shortcut: 'l', - }; - } else { - options.more = { - label: 'More options...', - shortcut: 'm', - }; - } - } - - const printer = new DiagnosticsPrinter({ - reporter, - }); - diag = printer.processor.addDiagnosticAssert(diag); - printer.print(); - - const answer = await reporter.radio( - 'How do you want to resolve this?', - { - options, - }, - ); - - // Check if this diagnostic is now out of date - printer.fetchFileSources([diag]); - const outdatedFiles = printer.getOutdatedFiles(diag); - if (outdatedFiles.size > 0) { - const files = Array.from( - outdatedFiles, - (path) => ``, - ); - - reporter.br(); - - if (files.length === 1) { - reporter.warn( - `The file ${files[0]} changed while waiting for your response.`, - ); - } else { - reporter.warn( - 'The following diagnostic dependencies changed while waiting for your response.', - ); - reporter.list(files); - } - - await reporter.confirm('Press any key to try again'); - - return await check(req, state); - } - - if (answer === 'less') { - return await ask(diag, req, state, false); - } - - if (answer === 'more') { - return await ask(diag, req, state, true); - } - - if (answer === 'ignore') { - return await check(req, state); - } - - if (answer === 'exit') { - return EMPTY_SUCCESS_RESPONSE; - } - - const action = optionToAction.get(answer); - if (action === undefined) { - throw new Error('Should have found an action for this option'); - } - - const requestFlags: Partial = { - ...action.requestFlags, - }; - - // Execute action - const actionRes = await client.query( - { - commandName: action.command, - args: action.args, - commandFlags: action.commandFlags, - requestFlags, - }, - 'master', - ); - if (actionRes.type !== 'DIAGNOSTICS' && actionRes.type !== 'SUCCESS') { - return actionRes; - } - - state.resolvedCount++; - return await check(req, state); + const {client} = req; + const {reporter} = client; + reporter.clearScreen(); + + // Extract actions and remove them from the diagnostic + let {advice = []} = diag.description; + let hasExtraOptions = false; + const actions: Array = []; + for (const item of advice) { + if (item.type === 'action') { + // Only show extra items and hide all non-extra items when `more === true` + if (item.extra === true) { + hasExtraOptions = true; + if (!showMoreOptions) { + continue; + } + } else if (showMoreOptions) { + continue; + } + + actions.push(item); + } + } + advice = advice.filter((item) => item.type !== 'action'); + diag = { + ...diag, + description: { + ...diag.description, + advice, + }, + }; + + const optionToAction: Map = new Map(); + const chosenShortcuts: Set = new Set(['n', 'escape']); + + const actionOptions: Dict = {}; + + let counter = 0; + for (const action of actions) { + const key = String(counter++); + let shortcut = + action.shortcut !== undefined && !chosenShortcuts.has(action.shortcut) + ? action.shortcut + : undefined; + optionToAction.set(key, action); + actionOptions[key] = { + label: action.noun, + shortcut, + }; + } + + const options: { + ignore: SelectOption; + exit: SelectOption; + more?: SelectOption; + less?: SelectOption; + } = { + ignore: { + label: 'Do nothing', + shortcut: 'n', + }, + ...actionOptions, + exit: { + label: 'Exit', + shortcut: 'escape', + }, + }; + + if (hasExtraOptions) { + if (showMoreOptions) { + options.more = { + label: 'Less options...', + shortcut: 'l', + }; + } else { + options.more = { + label: 'More options...', + shortcut: 'm', + }; + } + } + + const printer = new DiagnosticsPrinter({ + reporter, + }); + diag = printer.processor.addDiagnosticAssert(diag); + printer.print(); + + const answer = await reporter.radio( + 'How do you want to resolve this?', + { + options, + }, + ); + + // Check if this diagnostic is now out of date + printer.fetchFileSources([diag]); + const outdatedFiles = printer.getOutdatedFiles(diag); + if (outdatedFiles.size > 0) { + const files = Array.from( + outdatedFiles, + (path) => ``, + ); + + reporter.br(); + + if (files.length === 1) { + reporter.warn( + `The file ${files[0]} changed while waiting for your response.`, + ); + } else { + reporter.warn( + 'The following diagnostic dependencies changed while waiting for your response.', + ); + reporter.list(files); + } + + await reporter.confirm('Press any key to try again'); + + return await check(req, state); + } + + if (answer === 'less') { + return await ask(diag, req, state, false); + } + + if (answer === 'more') { + return await ask(diag, req, state, true); + } + + if (answer === 'ignore') { + return await check(req, state); + } + + if (answer === 'exit') { + return EMPTY_SUCCESS_RESPONSE; + } + + const action = optionToAction.get(answer); + if (action === undefined) { + throw new Error('Should have found an action for this option'); + } + + const requestFlags: Partial = { + ...action.requestFlags, + }; + + // Execute action + const actionRes = await client.query( + { + commandName: action.command, + args: action.args, + commandFlags: action.commandFlags, + requestFlags, + }, + 'master', + ); + if (actionRes.type !== 'DIAGNOSTICS' && actionRes.type !== 'SUCCESS') { + return actionRes; + } + + state.resolvedCount++; + return await check(req, state); } export default async function review( - req: ClientRequest, + req: ClientRequest, ): Promise { - const {reporter} = req.client; - const state: State = { - initial: true, - seen: new Set(), - resolvedCount: 0, - }; - const res = await check(req, state); - - reporter.clearScreen(); - - if (state.seen.size === 0) { - reporter.success('Nothing to review!'); - } else { - if (res.type === 'DIAGNOSTICS') { - printDiagnostics({ - diagnostics: res.diagnostics, - suppressions: [], - excludeFooter: true, - printerOptions: { - reporter, - }, - }); - reporter.hr(); - reporter.error( - `${res.diagnostics.length} unresolved ${res.diagnostics.length} remaining`, - ); - } - - reporter.success( - `${state.resolvedCount} ${state.resolvedCount} resolved`, - ); - } - - return res; + const {reporter} = req.client; + const state: State = { + initial: true, + seen: new Set(), + resolvedCount: 0, + }; + const res = await check(req, state); + + reporter.clearScreen(); + + if (state.seen.size === 0) { + reporter.success('Nothing to review!'); + } else { + if (res.type === 'DIAGNOSTICS') { + printDiagnostics({ + diagnostics: res.diagnostics, + suppressions: [], + excludeFooter: true, + printerOptions: { + reporter, + }, + }); + reporter.hr(); + reporter.error( + `${res.diagnostics.length} unresolved ${res.diagnostics.length} remaining`, + ); + } + + reporter.success( + `${state.resolvedCount} ${state.resolvedCount} resolved`, + ); + } + + return res; } diff --git a/packages/@romejs/core/common/bridges/MasterBridge.ts b/packages/@romejs/core/common/bridges/MasterBridge.ts index aef01d27d9d..0561edecf60 100644 --- a/packages/@romejs/core/common/bridges/MasterBridge.ts +++ b/packages/@romejs/core/common/bridges/MasterBridge.ts @@ -11,173 +11,173 @@ import {ClientFlagsJSON, ClientRequestFlags} from '../types/client'; import {Bridge} from '@romejs/events'; import {JSONObject, JSONPropertyValue} from '@romejs/codec-json'; import { - RemoteReporterClientMessage, - RemoteReporterReceiveMessage, - ReporterStream, + RemoteReporterClientMessage, + RemoteReporterReceiveMessage, + ReporterStream, } from '@romejs/cli-reporter'; import {MasterMarker} from '../../master/Master'; export type MasterQueryRequest = { - requestFlags: ClientRequestFlags; - commandFlags: JSONObject; - args: Array; - commandName: string; - silent: boolean; - noData: boolean; - terminateWhenIdle: boolean; - cancelToken?: string; + requestFlags: ClientRequestFlags; + commandFlags: JSONObject; + args: Array; + commandName: string; + silent: boolean; + noData: boolean; + terminateWhenIdle: boolean; + cancelToken?: string; }; export type PartialMasterQueryRequest = Partial> & { - requestFlags?: Partial; - commandName: string; + requestFlags?: Partial; + commandName: string; }; export type MasterQueryResponseSuccess = { - type: 'SUCCESS'; - hasData: boolean; - data: JSONPropertyValue; - markers: Array; + type: 'SUCCESS'; + hasData: boolean; + data: JSONPropertyValue; + markers: Array; }; export type MasterQueryResponseError = { - type: 'ERROR'; - fatal: boolean; - handled: boolean; - name: string; - message: string; - stack: undefined | string; + type: 'ERROR'; + fatal: boolean; + handled: boolean; + name: string; + message: string; + stack: undefined | string; }; export type MasterQueryResponseDiagnostics = { - type: 'DIAGNOSTICS'; - diagnostics: Diagnostics; + type: 'DIAGNOSTICS'; + diagnostics: Diagnostics; }; export type MasterQueryResponseInvalid = { - type: 'INVALID_REQUEST'; - diagnostics: Diagnostics; - showHelp: boolean; + type: 'INVALID_REQUEST'; + diagnostics: Diagnostics; + showHelp: boolean; }; export type MasterQueryResponseCancelled = { - type: 'CANCELLED'; + type: 'CANCELLED'; }; export type MasterQueryResponse = - | MasterQueryResponseInvalid - | MasterQueryResponseSuccess - | MasterQueryResponseError - | MasterQueryResponseCancelled - | MasterQueryResponseDiagnostics; + | MasterQueryResponseInvalid + | MasterQueryResponseSuccess + | MasterQueryResponseError + | MasterQueryResponseCancelled + | MasterQueryResponseDiagnostics; export type ProfilingStartData = { - samplingInterval: number; + samplingInterval: number; }; export type MasterBridgeInfo = { - version: string; - columns: number; - hasClearScreen: boolean; - useRemoteReporter: boolean; - unicode: boolean; - format: ReporterStream['format']; - flags: ClientFlagsJSON; + version: string; + columns: number; + hasClearScreen: boolean; + useRemoteReporter: boolean; + unicode: boolean; + format: ReporterStream['format']; + flags: ClientFlagsJSON; }; export default class MasterBridge extends Bridge { - getClientInfo = this.createEvent({ - name: 'getClientInfo', - direction: 'server->client', - }); - - stdout = this.createEvent({ - name: 'stdout', - direction: 'server->client', - }); - - stderr = this.createEvent({ - name: 'stderr', - direction: 'server->client', - }); - - enableWorkerLogs = this.createEvent({ - name: 'enableWorkerLogs', - direction: 'server<-client', - }); - - log = this.createEvent< - { - origin: 'master' | 'worker'; - chunk: string; - }, - void - >({ - name: 'log', - direction: 'server->client', - }); - - setColumns = this.createEvent({ - name: 'columns.set', - direction: 'server<-client', - }); - - reporterRemoteServerMessage = this.createEvent< - RemoteReporterClientMessage, - void - >({ - name: 'reporterRemoteToLocalMessage', - direction: 'server->client', - }); - - reporterRemoteClientMessage = this.createEvent< - RemoteReporterReceiveMessage, - void - >({ - name: 'reporterLocalToRemoteMessage', - direction: 'server<-client', - }); - - query = this.createEvent({ - name: 'query', - direction: 'server<-client', - }); - - cancelQuery = this.createEvent({ - name: 'cancel', - direction: 'server<-client', - }); - - profilingGetWorkers = this.createEvent>({ - name: 'profiling.getWorkers', - direction: 'server<-client', - }); - - profilingStart = this.createEvent({ - name: 'profiling.start', - direction: 'server<-client', - }); - - profilingStop = this.createEvent({ - name: 'profiling.stop', - direction: 'server<-client', - }); - - profilingStopWorker = this.createEvent({ - name: 'profile.stopWorker', - direction: 'server<-client', - }); - - lspFromClientBuffer = this.createEvent({ - name: 'lspFromClientBuffer', - direction: 'server<-client', - }); - - lspFromServerBuffer = this.createEvent({ - name: 'lspFromServerBuffer', - direction: 'server->client', - }); + getClientInfo = this.createEvent({ + name: 'getClientInfo', + direction: 'server->client', + }); + + stdout = this.createEvent({ + name: 'stdout', + direction: 'server->client', + }); + + stderr = this.createEvent({ + name: 'stderr', + direction: 'server->client', + }); + + enableWorkerLogs = this.createEvent({ + name: 'enableWorkerLogs', + direction: 'server<-client', + }); + + log = this.createEvent< + { + origin: 'master' | 'worker'; + chunk: string; + }, + void + >({ + name: 'log', + direction: 'server->client', + }); + + setColumns = this.createEvent({ + name: 'columns.set', + direction: 'server<-client', + }); + + reporterRemoteServerMessage = this.createEvent< + RemoteReporterClientMessage, + void + >({ + name: 'reporterRemoteToLocalMessage', + direction: 'server->client', + }); + + reporterRemoteClientMessage = this.createEvent< + RemoteReporterReceiveMessage, + void + >({ + name: 'reporterLocalToRemoteMessage', + direction: 'server<-client', + }); + + query = this.createEvent({ + name: 'query', + direction: 'server<-client', + }); + + cancelQuery = this.createEvent({ + name: 'cancel', + direction: 'server<-client', + }); + + profilingGetWorkers = this.createEvent>({ + name: 'profiling.getWorkers', + direction: 'server<-client', + }); + + profilingStart = this.createEvent({ + name: 'profiling.start', + direction: 'server<-client', + }); + + profilingStop = this.createEvent({ + name: 'profiling.stop', + direction: 'server<-client', + }); + + profilingStopWorker = this.createEvent({ + name: 'profile.stopWorker', + direction: 'server<-client', + }); + + lspFromClientBuffer = this.createEvent({ + name: 'lspFromClientBuffer', + direction: 'server<-client', + }); + + lspFromServerBuffer = this.createEvent({ + name: 'lspFromServerBuffer', + direction: 'server->client', + }); } diff --git a/packages/@romejs/core/common/bridges/TestWorkerBridge.ts b/packages/@romejs/core/common/bridges/TestWorkerBridge.ts index 6ee79d8b9fb..bf64ab6cb25 100644 --- a/packages/@romejs/core/common/bridges/TestWorkerBridge.ts +++ b/packages/@romejs/core/common/bridges/TestWorkerBridge.ts @@ -10,92 +10,92 @@ import {TestMasterRunnerOptions} from '../../master/testing/types'; import {Bridge} from '@romejs/events'; import {JSONFileReference} from '../types/files'; import { - FocusedTest, - TestWorkerFileResult, + FocusedTest, + TestWorkerFileResult, } from '@romejs/core/test-worker/TestWorkerRunner'; export type TestRef = { - filename: string; - testName: string; + filename: string; + testName: string; }; export type TestWorkerPrepareTestOptions = { - id: number; - file: JSONFileReference; - projectFolder: string; - code: string; - cwd: string; - options: TestMasterRunnerOptions; + id: number; + file: JSONFileReference; + projectFolder: string; + code: string; + cwd: string; + options: TestMasterRunnerOptions; }; export type TestWorkerPrepareTestResult = { - focusedTests: Array; + focusedTests: Array; }; export type TestWorkerRunTestOptions = { - id: number; - onlyFocusedTests: boolean; + id: number; + onlyFocusedTests: boolean; }; export default class TestWorkerBridge extends Bridge { - inspectorDetails = this.createEvent< - void, - { - inspectorUrl: undefined | string; - } - >({ - name: 'inspectorDetails', - direction: 'server->client', - }); + inspectorDetails = this.createEvent< + void, + { + inspectorUrl: undefined | string; + } + >({ + name: 'inspectorDetails', + direction: 'server->client', + }); - prepareTest = this.createEvent< - TestWorkerPrepareTestOptions, - TestWorkerPrepareTestResult - >({ - name: 'prepareTest', - direction: 'server->client', - }); + prepareTest = this.createEvent< + TestWorkerPrepareTestOptions, + TestWorkerPrepareTestResult + >({ + name: 'prepareTest', + direction: 'server->client', + }); - runTest = this.createEvent({ - name: 'runTest', - direction: 'server->client', - }); + runTest = this.createEvent({ + name: 'runTest', + direction: 'server->client', + }); - testsFound = this.createEvent, void>({ - name: 'onTestFounds', - direction: 'server<-client', - }); + testsFound = this.createEvent, void>({ + name: 'onTestFounds', + direction: 'server<-client', + }); - testStart = this.createEvent< - { - ref: TestRef; - timeout: undefined | number; - }, - void - >({ - name: 'onTestStart', - direction: 'server<-client', - }); + testStart = this.createEvent< + { + ref: TestRef; + timeout: undefined | number; + }, + void + >({ + name: 'onTestStart', + direction: 'server<-client', + }); - testDiagnostic = this.createEvent< - { - diagnostic: Diagnostic; - origin: undefined | DiagnosticOrigin; - }, - void - >({ - name: 'testDiagnostic', - direction: 'server<-client', - }); + testDiagnostic = this.createEvent< + { + diagnostic: Diagnostic; + origin: undefined | DiagnosticOrigin; + }, + void + >({ + name: 'testDiagnostic', + direction: 'server<-client', + }); - testFinish = this.createEvent< - { - success: boolean; - ref: TestRef; - }, - void - >({ - name: 'onTestSuccess', - direction: 'server<-client', - }); + testFinish = this.createEvent< + { + success: boolean; + ref: TestRef; + }, + void + >({ + name: 'onTestSuccess', + direction: 'server<-client', + }); } diff --git a/packages/@romejs/core/common/bridges/WebBridge.ts b/packages/@romejs/core/common/bridges/WebBridge.ts index 4c9664637e9..c8850cd586f 100644 --- a/packages/@romejs/core/common/bridges/WebBridge.ts +++ b/packages/@romejs/core/common/bridges/WebBridge.ts @@ -9,14 +9,14 @@ import {Bridge} from '@romejs/events'; import {WebMasterClient, WebMasterRequest} from '../../master/web'; export default class WebBridge extends Bridge { - requests = this.createEvent< - { - requests: Array; - clients: Array; - }, - void - >({ - name: 'WebBridge.requests', - direction: 'server->client', - }); + requests = this.createEvent< + { + requests: Array; + clients: Array; + }, + void + >({ + name: 'WebBridge.requests', + direction: 'server->client', + }); } diff --git a/packages/@romejs/core/common/bridges/WorkerBridge.ts b/packages/@romejs/core/common/bridges/WorkerBridge.ts index f3e026d00e3..d05d9ddc004 100644 --- a/packages/@romejs/core/common/bridges/WorkerBridge.ts +++ b/packages/@romejs/core/common/bridges/WorkerBridge.ts @@ -9,17 +9,17 @@ import {ModuleSignature} from '@romejs/js-analysis'; import {Manifest} from '@romejs/codec-js-manifest'; import {ConstProgramSyntax, ConstSourceType, Program} from '@romejs/js-ast'; import { - BundleCompileOptions, - CompileResult, - LintCompilerOptions, - TransformStageName, + BundleCompileOptions, + CompileResult, + LintCompilerOptions, + TransformStageName, } from '@romejs/js-compiler'; import {Profile} from '@romejs/v8'; import {ProfilingStartData} from './MasterBridge'; import { - DiagnosticSuppressions, - Diagnostics, - DiagnosticsError, + DiagnosticSuppressions, + Diagnostics, + DiagnosticsError, } from '@romejs/diagnostics'; import {ProjectConfigJSON} from '@romejs/project'; import {Bridge} from '@romejs/events'; @@ -28,247 +28,247 @@ import {AnalyzeDependencyResult} from '../types/analyzeDependencies'; import {InlineSnapshotUpdates} from '@romejs/core/test-worker/SnapshotManager'; export type WorkerProjects = Array<{ - id: number; - folder: string; - config: undefined | ProjectConfigJSON; + id: number; + folder: string; + config: undefined | ProjectConfigJSON; }>; export type WorkerCompileResult = CompileResult & { - cached: boolean; + cached: boolean; }; export type WorkerPartialManifest = { - path: string; - type: Manifest['type']; + path: string; + type: Manifest['type']; }; export type WorkerPartialManifests = Array<{ - id: number; - manifest: undefined | WorkerPartialManifest; + id: number; + manifest: undefined | WorkerPartialManifest; }>; // Omit analyze value as the worker will fetch it itself, skips sending over a large payload that it already has in memory export type WorkerCompilerOptions = { - bundle?: WorkerBundleCompileOptions; + bundle?: WorkerBundleCompileOptions; }; export type WorkerBundleCompileOptions = Omit; // export type WorkerAnalyzeDependencyResult = AnalyzeDependencyResult & { - cached: boolean; + cached: boolean; }; export type WorkerLintOptions = { - compilerOptions?: LintCompilerOptions; - prefetchedModuleSignatures: PrefetchedModuleSignatures; - applyFixes: boolean; - save: boolean; + compilerOptions?: LintCompilerOptions; + prefetchedModuleSignatures: PrefetchedModuleSignatures; + applyFixes: boolean; + save: boolean; }; export type WorkerParseOptions = { - sourceType?: ConstSourceType; - syntax?: Array; - cache?: boolean; - allowParserDiagnostics?: boolean; + sourceType?: ConstSourceType; + syntax?: Array; + cache?: boolean; + allowParserDiagnostics?: boolean; }; export type WorkerStatus = { - astCacheSize: number; - memoryUsage: { - rss: number; - heapTotal: number; - heapUsed: number; - external: number; - }; - pid: number; - uptime: number; + astCacheSize: number; + memoryUsage: { + rss: number; + heapTotal: number; + heapUsed: number; + external: number; + }; + pid: number; + uptime: number; }; export type PrefetchedModuleSignatures = { - [key: string]: - | { - type: 'USE_CACHED'; - filename: string; - } - | { - type: 'RESOLVED'; - graph: ModuleSignature; - } - | { - type: 'OWNED'; - file: JSONFileReference; - } - | { - type: 'POINTER'; - key: string; - }; + [key: string]: + | { + type: 'USE_CACHED'; + filename: string; + } + | { + type: 'RESOLVED'; + graph: ModuleSignature; + } + | { + type: 'OWNED'; + file: JSONFileReference; + } + | { + type: 'POINTER'; + key: string; + }; }; export type WorkerFormatResult = { - original: string; - formatted: string; - diagnostics: Diagnostics; + original: string; + formatted: string; + diagnostics: Diagnostics; }; export type WorkerLintResult = { - saved: boolean; - diagnostics: Diagnostics; - suppressions: DiagnosticSuppressions; + saved: boolean; + diagnostics: Diagnostics; + suppressions: DiagnosticSuppressions; }; export default class WorkerBridge extends Bridge { - log = this.createEvent({ - name: 'log', - direction: 'server->client', - }); + log = this.createEvent({ + name: 'log', + direction: 'server->client', + }); - updateProjects = this.createEvent< - { - projects: WorkerProjects; - }, - void - >({ - name: 'updateProjects', - direction: 'server->client', - }); + updateProjects = this.createEvent< + { + projects: WorkerProjects; + }, + void + >({ + name: 'updateProjects', + direction: 'server->client', + }); - updateManifests = this.createEvent< - { - manifests: WorkerPartialManifests; - }, - void - >({ - name: 'updateManifests', - direction: 'server->client', - }); + updateManifests = this.createEvent< + { + manifests: WorkerPartialManifests; + }, + void + >({ + name: 'updateManifests', + direction: 'server->client', + }); - profilingStart = this.createEvent({ - name: 'profiling.start', - direction: 'server->client', - }); + profilingStart = this.createEvent({ + name: 'profiling.start', + direction: 'server->client', + }); - profilingStop = this.createEvent({ - name: 'profiling.stop', - direction: 'server->client', - }); + profilingStop = this.createEvent({ + name: 'profiling.stop', + direction: 'server->client', + }); - status = this.createEvent({ - name: 'status', - direction: 'server->client', - }); + status = this.createEvent({ + name: 'status', + direction: 'server->client', + }); - evict = this.createEvent< - { - filename: string; - }, - void - >({ - name: 'evict', - direction: 'server->client', - }); + evict = this.createEvent< + { + filename: string; + }, + void + >({ + name: 'evict', + direction: 'server->client', + }); - format = this.createEvent< - { - file: JSONFileReference; - parseOptions: WorkerParseOptions; - }, - undefined | WorkerFormatResult - >({ - name: 'format', - direction: 'server->client', - }); + format = this.createEvent< + { + file: JSONFileReference; + parseOptions: WorkerParseOptions; + }, + undefined | WorkerFormatResult + >({ + name: 'format', + direction: 'server->client', + }); - moduleSignatureJS = this.createEvent< - { - file: JSONFileReference; - parseOptions: WorkerParseOptions; - }, - ModuleSignature - >({ - name: 'moduleSignatureJS', - direction: 'server->client', - }); + moduleSignatureJS = this.createEvent< + { + file: JSONFileReference; + parseOptions: WorkerParseOptions; + }, + ModuleSignature + >({ + name: 'moduleSignatureJS', + direction: 'server->client', + }); - analyzeDependencies = this.createEvent< - { - file: JSONFileReference; - parseOptions: WorkerParseOptions; - }, - AnalyzeDependencyResult - >({ - name: 'analyzeDependencies', - direction: 'server->client', - }); + analyzeDependencies = this.createEvent< + { + file: JSONFileReference; + parseOptions: WorkerParseOptions; + }, + AnalyzeDependencyResult + >({ + name: 'analyzeDependencies', + direction: 'server->client', + }); - lint = this.createEvent< - { - file: JSONFileReference; - options: WorkerLintOptions; - parseOptions: WorkerParseOptions; - }, - WorkerLintResult - >({name: 'lint', direction: 'server->client'}); + lint = this.createEvent< + { + file: JSONFileReference; + options: WorkerLintOptions; + parseOptions: WorkerParseOptions; + }, + WorkerLintResult + >({name: 'lint', direction: 'server->client'}); - updateInlineSnapshots = this.createEvent< - { - file: JSONFileReference; - updates: InlineSnapshotUpdates; - parseOptions: WorkerParseOptions; - }, - Diagnostics - >({name: 'updateInlineSnapshots', direction: 'server->client'}); + updateInlineSnapshots = this.createEvent< + { + file: JSONFileReference; + updates: InlineSnapshotUpdates; + parseOptions: WorkerParseOptions; + }, + Diagnostics + >({name: 'updateInlineSnapshots', direction: 'server->client'}); - compileJS = this.createEvent< - { - file: JSONFileReference; - stage: TransformStageName; - options: WorkerCompilerOptions; - parseOptions: WorkerParseOptions; - }, - CompileResult - >({name: 'compileJS', direction: 'server->client'}); + compileJS = this.createEvent< + { + file: JSONFileReference; + stage: TransformStageName; + options: WorkerCompilerOptions; + parseOptions: WorkerParseOptions; + }, + CompileResult + >({name: 'compileJS', direction: 'server->client'}); - parseJS = this.createEvent< - { - file: JSONFileReference; - options: WorkerParseOptions; - }, - Program - >({name: 'parseJS', direction: 'server->client'}); + parseJS = this.createEvent< + { + file: JSONFileReference; + options: WorkerParseOptions; + }, + Program + >({name: 'parseJS', direction: 'server->client'}); - updateBuffer = this.createEvent< - { - file: JSONFileReference; - content: string; - }, - void - >({ - name: 'updateBuffer', - direction: 'server->client', - }); + updateBuffer = this.createEvent< + { + file: JSONFileReference; + content: string; + }, + void + >({ + name: 'updateBuffer', + direction: 'server->client', + }); - init() { - this.addErrorTransport( - 'DiagnosticsError', - { - serialize(err: Error) { - if (!(err instanceof DiagnosticsError)) { - throw new Error('Expected DiagnosticsError'); - } + init() { + this.addErrorTransport( + 'DiagnosticsError', + { + serialize(err: Error) { + if (!(err instanceof DiagnosticsError)) { + throw new Error('Expected DiagnosticsError'); + } - return { - diagnostics: err.diagnostics, - }; - }, - hydrate(err, data) { - return new DiagnosticsError( - String(err.message), - // rome-ignore lint/noExplicitAny - (data.diagnostics as any), - ); - }, - }, - ); - } + return { + diagnostics: err.diagnostics, + }; + }, + hydrate(err, data) { + return new DiagnosticsError( + String(err.message), + // rome-ignore lint/noExplicitAny + (data.diagnostics as any), + ); + }, + }, + ); + } } diff --git a/packages/@romejs/core/common/commands.ts b/packages/@romejs/core/common/commands.ts index 0c7b4978660..6c8113ad47b 100644 --- a/packages/@romejs/core/common/commands.ts +++ b/packages/@romejs/core/common/commands.ts @@ -9,23 +9,23 @@ import {Consumer} from '@romejs/consume'; import {Dict} from '@romejs/typescript-helpers'; export type SharedCommand> = { - category: string; - description: string; - defineFlags: (c: Consumer) => Flags; - usage: string; - examples: Array<{ - description: string; - command: string; - }>; - ignoreFlags?: Array; - allowRequestFlags?: Array<'review' | 'watch'>; + category: string; + description: string; + defineFlags: (c: Consumer) => Flags; + usage: string; + examples: Array<{ + description: string; + command: string; + }>; + ignoreFlags?: Array; + allowRequestFlags?: Array<'review' | 'watch'>; }; export const commandCategories = { - PROCESS_MANAGEMENT: 'Process Management', - CODE_QUALITY: 'Code Quality', - SOURCE_CODE: 'Source Code', - PROJECT_MANAGEMENT: 'Project Management', - SOURCE_CONTROL: 'Source Control', - INTERNAL: 'Internal', + PROCESS_MANAGEMENT: 'Process Management', + CODE_QUALITY: 'Code Quality', + SOURCE_CODE: 'Source Code', + PROJECT_MANAGEMENT: 'Project Management', + SOURCE_CONTROL: 'Source Control', + INTERNAL: 'Internal', }; diff --git a/packages/@romejs/core/common/file-handlers/index.ts b/packages/@romejs/core/common/file-handlers/index.ts index 51aa1e6a0cb..84d5baf8ea1 100644 --- a/packages/@romejs/core/common/file-handlers/index.ts +++ b/packages/@romejs/core/common/file-handlers/index.ts @@ -9,12 +9,12 @@ import {ProjectConfig} from '@romejs/project'; import {UnknownFilePath} from '@romejs/path'; import {ExtensionHandler} from './types'; import { - cjsHandler, - jsHandler, - jsxHandler, - mjsHandler, - tsHandler, - tsxHandler, + cjsHandler, + jsHandler, + jsxHandler, + mjsHandler, + tsHandler, + tsxHandler, } from './javascript'; import {textHandler} from './text'; import {jsonHandler, rjsonHandler} from './json'; @@ -22,67 +22,67 @@ import {jsonHandler, rjsonHandler} from './json'; type ExtensionsMap = Map; export type GetFileHandlerResult = { - ext: string; - handler?: ExtensionHandler; + ext: string; + handler?: ExtensionHandler; }; export function getFileHandlerExtensions( - projectConfig: ProjectConfig, + projectConfig: ProjectConfig, ): Array { - return [...DEFAULT_HANDLERS.keys(), ...projectConfig.files.assetExtensions]; + return [...DEFAULT_HANDLERS.keys(), ...projectConfig.files.assetExtensions]; } export function getFileHandler( - path: UnknownFilePath, - projectConfig: ProjectConfig, + path: UnknownFilePath, + projectConfig: ProjectConfig, ): GetFileHandlerResult { - const basename = path.getBasename(); + const basename = path.getBasename(); - const match = basename.match(/\.([a-zA-Z]+)$/); - if (match == null) { - return {ext: '', handler: undefined}; - } + const match = basename.match(/\.([a-zA-Z]+)$/); + if (match == null) { + return {ext: '', handler: undefined}; + } - const ext: string = match[1]; - let handler = DEFAULT_HANDLERS.get(ext); + const ext: string = match[1]; + let handler = DEFAULT_HANDLERS.get(ext); - // Allow setting custom assert extensions in the project config - if (handler === undefined && projectConfig.files.assetExtensions.includes(ext)) { - handler = assetHandler; - } + // Allow setting custom assert extensions in the project config + if (handler === undefined && projectConfig.files.assetExtensions.includes(ext)) { + handler = assetHandler; + } - return {ext, handler}; + return {ext, handler}; } export function getFileHandlerAssert( - path: UnknownFilePath, - projectConfig: ProjectConfig, + path: UnknownFilePath, + projectConfig: ProjectConfig, ): Required { - const {handler, ext} = getFileHandler(path, projectConfig); + const {handler, ext} = getFileHandler(path, projectConfig); - if (handler === undefined) { - throw new Error(`No file handler found for '${path.join()}'`); - } else { - return {handler, ext}; - } + if (handler === undefined) { + throw new Error(`No file handler found for '${path.join()}'`); + } else { + return {handler, ext}; + } } export const ASSET_EXPORT_TEMPORARY_VALUE = 'VALUE_INJECTED_BY_BUNDLER'; const assetHandler: ExtensionHandler = { - // analyzeDependencies shim - ...textHandler, - ext: 'unknown', - canHaveScale: true, - isAsset: true, - async toJavaScript() { - // This exists just so analyzeDependencies has something to look at - // When bundling we'll have custom logic in the compiler to handle assets and inject the correct string - return { - generated: true, - sourceText: `export default '${ASSET_EXPORT_TEMPORARY_VALUE}';`, - }; - }, + // analyzeDependencies shim + ...textHandler, + ext: 'unknown', + canHaveScale: true, + isAsset: true, + async toJavaScript() { + // This exists just so analyzeDependencies has something to look at + // When bundling we'll have custom logic in the compiler to handle assets and inject the correct string + return { + generated: true, + sourceText: `export default '${ASSET_EXPORT_TEMPORARY_VALUE}';`, + }; + }, }; // Extensions that have a `lint` handler @@ -92,45 +92,45 @@ export const LINTABLE_EXTENSIONS: Array = []; export const FORMATTABLE_EXTENSIONS: Array = []; function setHandler(handler: ExtensionHandler) { - const {ext} = handler; + const {ext} = handler; - if (handler.lint !== undefined) { - LINTABLE_EXTENSIONS.push(ext); - } + if (handler.lint !== undefined) { + LINTABLE_EXTENSIONS.push(ext); + } - if (handler.format !== undefined) { - FORMATTABLE_EXTENSIONS.push(ext); - } + if (handler.format !== undefined) { + FORMATTABLE_EXTENSIONS.push(ext); + } - DEFAULT_HANDLERS.set(ext, handler); + DEFAULT_HANDLERS.set(ext, handler); } const DEFAULT_HANDLERS: ExtensionsMap = new Map(); const DEFAULT_ASSET_EXTENSIONS = [ - // Images - 'png', - 'jpg', - 'jpeg', - 'gif', - // Video - 'webm', - 'mp4', - 'm4v', - 'avi', - 'mkv', - // Audio - 'mp3', - // Fonts - 'woff', - 'woff2', - 'eot', - 'ttf', - 'otf', + // Images + 'png', + 'jpg', + 'jpeg', + 'gif', + // Video + 'webm', + 'mp4', + 'm4v', + 'avi', + 'mkv', + // Audio + 'mp3', + // Fonts + 'woff', + 'woff2', + 'eot', + 'ttf', + 'otf', ]; for (const ext of DEFAULT_ASSET_EXTENSIONS) { - setHandler({ext, ...assetHandler}); + setHandler({ext, ...assetHandler}); } setHandler(jsHandler); diff --git a/packages/@romejs/core/common/file-handlers/javascript.ts b/packages/@romejs/core/common/file-handlers/javascript.ts index 7a244803c75..73202760cb9 100644 --- a/packages/@romejs/core/common/file-handlers/javascript.ts +++ b/packages/@romejs/core/common/file-handlers/javascript.ts @@ -10,10 +10,10 @@ import {check as typeCheck} from '@romejs/js-analysis'; import {ConstProgramSyntax, ConstSourceType} from '@romejs/js-ast'; import {formatJS} from '@romejs/js-formatter'; import { - ExtensionHandler, - ExtensionHandlerMethodInfo, - ExtensionLintInfo, - ExtensionLintResult, + ExtensionHandler, + ExtensionHandlerMethodInfo, + ExtensionLintInfo, + ExtensionLintResult, } from './types'; import {ParseJSResult} from '@romejs/core/worker/Worker'; @@ -29,127 +29,127 @@ export const IMPLICIT_JS_EXTENSIONS = ['js', 'ts', 'tsx', 'json']; export const JS_EXTENSIONS: Array = []; function buildJSHandler( - ext: string, - syntax: Array, - sourceType?: ConstSourceType, + ext: string, + syntax: Array, + sourceType?: ConstSourceType, ): ExtensionHandler { - JS_EXTENSIONS.push(ext); - - return { - ext, - syntax, - sourceType, - - async analyzeDependencies({file, worker, parseOptions}) { - const {ast, sourceText, project, generated} = await worker.parseJS( - file, - parseOptions, - ); - worker.logger.info(`Analyzing:`, file.real); - - return worker.api.interceptAndAddGeneratedToDiagnostics( - await compiler.analyzeDependencies({ - ref: file, - ast, - sourceText, - project, - options: {}, - }), - generated, - ); - }, - - async toJavaScript({file, worker}) { - return { - sourceText: await worker.readFile(file.real), - generated: false, - }; - }, - - async format(info: ExtensionHandlerMethodInfo): Promise { - const {file: ref, parseOptions, worker} = info; - - const {ast, sourceText, generated}: ParseJSResult = await worker.parseJS( - ref, - parseOptions, - ); - - const out = formatJS( - ast, - { - sourceText, - }, - ); - - return worker.api.interceptAndAddGeneratedToDiagnostics( - { - formatted: out.code, - sourceText, - suppressions: [], - diagnostics: ast.diagnostics, - }, - generated, - ); - }, - - async lint(info: ExtensionLintInfo): Promise { - const {file: ref, project, parseOptions, options, worker} = info; - - const {ast, sourceText, generated}: ParseJSResult = await worker.parseJS( - ref, - parseOptions, - ); - - worker.logger.info(`Linting: `, ref.real); - - // Run the compiler in lint-mode which is where all the rules are actually ran - const res = await compiler.lint({ - applyFixes: options.applyFixes, - ref, - options: { - lint: options.compilerOptions, - }, - ast, - project, - sourceText, - }); - - // Extract lint diagnostics - let {diagnostics} = res; - - // Only enable typechecking if enabled in .romeconfig - let typeCheckingEnabled = project.config.typeCheck.enabled === true; - if (project.config.typeCheck.libs.has(ref.real)) { - // don't typecheck lib files - typeCheckingEnabled = false; - } - - // Run type checking if necessary - if (typeCheckingEnabled) { - const typeCheckProvider = await worker.getTypeCheckProvider( - ref.project, - options.prefetchedModuleSignatures, - parseOptions, - ); - const typeDiagnostics = await typeCheck({ - ast, - provider: typeCheckProvider, - project, - }); - diagnostics = [...diagnostics, ...typeDiagnostics]; - } - - return worker.api.interceptAndAddGeneratedToDiagnostics( - { - suppressions: res.suppressions, - diagnostics, - sourceText, - formatted: res.src, - }, - generated, - ); - }, - }; + JS_EXTENSIONS.push(ext); + + return { + ext, + syntax, + sourceType, + + async analyzeDependencies({file, worker, parseOptions}) { + const {ast, sourceText, project, generated} = await worker.parseJS( + file, + parseOptions, + ); + worker.logger.info(`Analyzing:`, file.real); + + return worker.api.interceptAndAddGeneratedToDiagnostics( + await compiler.analyzeDependencies({ + ref: file, + ast, + sourceText, + project, + options: {}, + }), + generated, + ); + }, + + async toJavaScript({file, worker}) { + return { + sourceText: await worker.readFile(file.real), + generated: false, + }; + }, + + async format(info: ExtensionHandlerMethodInfo): Promise { + const {file: ref, parseOptions, worker} = info; + + const {ast, sourceText, generated}: ParseJSResult = await worker.parseJS( + ref, + parseOptions, + ); + + const out = formatJS( + ast, + { + sourceText, + }, + ); + + return worker.api.interceptAndAddGeneratedToDiagnostics( + { + formatted: out.code, + sourceText, + suppressions: [], + diagnostics: ast.diagnostics, + }, + generated, + ); + }, + + async lint(info: ExtensionLintInfo): Promise { + const {file: ref, project, parseOptions, options, worker} = info; + + const {ast, sourceText, generated}: ParseJSResult = await worker.parseJS( + ref, + parseOptions, + ); + + worker.logger.info(`Linting: `, ref.real); + + // Run the compiler in lint-mode which is where all the rules are actually ran + const res = await compiler.lint({ + applyFixes: options.applyFixes, + ref, + options: { + lint: options.compilerOptions, + }, + ast, + project, + sourceText, + }); + + // Extract lint diagnostics + let {diagnostics} = res; + + // Only enable typechecking if enabled in .romeconfig + let typeCheckingEnabled = project.config.typeCheck.enabled === true; + if (project.config.typeCheck.libs.has(ref.real)) { + // don't typecheck lib files + typeCheckingEnabled = false; + } + + // Run type checking if necessary + if (typeCheckingEnabled) { + const typeCheckProvider = await worker.getTypeCheckProvider( + ref.project, + options.prefetchedModuleSignatures, + parseOptions, + ); + const typeDiagnostics = await typeCheck({ + ast, + provider: typeCheckProvider, + project, + }); + diagnostics = [...diagnostics, ...typeDiagnostics]; + } + + return worker.api.interceptAndAddGeneratedToDiagnostics( + { + suppressions: res.suppressions, + diagnostics, + sourceText, + formatted: res.src, + }, + generated, + ); + }, + }; } export const jsHandler = buildJSHandler('js', []); diff --git a/packages/@romejs/core/common/file-handlers/json.ts b/packages/@romejs/core/common/file-handlers/json.ts index bd316a19d4a..aa6ec7b9983 100644 --- a/packages/@romejs/core/common/file-handlers/json.ts +++ b/packages/@romejs/core/common/file-handlers/json.ts @@ -6,84 +6,83 @@ */ import { - consumeJSONExtra, - parseJSON, - stringifyRJSONFromConsumer, + consumeJSONExtra, + parseJSON, + stringifyJSON, + stringifyRJSONFromConsumer, } from '@romejs/codec-json'; import {createAbsoluteFilePath, createUnknownFilePath} from '@romejs/path'; import { - ExtensionHandler, - ExtensionHandlerMethodInfo, - ExtensionLintResult, + ExtensionHandler, + ExtensionHandlerMethodInfo, + ExtensionLintResult, } from './types'; import {textHandler} from './text'; export const jsonHandler: ExtensionHandler = { - ext: 'json', + ext: 'json', - // analyzeDependencies shim - ...textHandler, + // analyzeDependencies shim + ...textHandler, - async format(info: ExtensionHandlerMethodInfo): Promise { - const {file, worker} = info; - const {uid} = file; + async format(info: ExtensionHandlerMethodInfo): Promise { + const {file, worker} = info; + const {uid} = file; - const real = createAbsoluteFilePath(file.real); - const sourceText = await worker.readFile(real); - const path = createUnknownFilePath(uid); + const real = createAbsoluteFilePath(file.real); + const sourceText = await worker.readFile(real); + const path = createUnknownFilePath(uid); - let formatted: string = sourceText; + let formatted: string = sourceText; - if (sourceText.length > 50_000) { - // Fast path for big JSON files - parseJSON({ - path, - input: sourceText, - }); - } else { - const {consumer, comments, hasExtensions} = consumeJSONExtra({ - input: sourceText, - path, - }); + if (sourceText.length > 50_000) { + // Fast path for big JSON files + parseJSON({ + path, + input: sourceText, + }); + } else { + const {consumer, comments, hasExtensions} = consumeJSONExtra({ + input: sourceText, + path, + }); - if (hasExtensions) { - formatted = stringifyRJSONFromConsumer({consumer, comments}); - } else { - formatted = String( - JSON.stringify(consumer.asUnknown(), undefined, ' '), - ); - } - } + if (hasExtensions) { + formatted = stringifyRJSONFromConsumer({consumer, comments}); + } else { + formatted = String(stringifyJSON(consumer.asUnknown())); + } + } - return { - sourceText, - diagnostics: [], - suppressions: [], - formatted, - }; - }, + return { + sourceText, + diagnostics: [], + suppressions: [], + formatted, + }; + }, - async toJavaScript({file, worker}) { - const src = await worker.readFile(file.real); + async toJavaScript({file, worker}) { + const src = await worker.readFile(file.real); - // Parse the JSON to make sure it's valid - const obj = parseJSON({ - path: createUnknownFilePath(file.uid), - input: src, - }); + // Parse the JSON to make sure it's valid + const obj = parseJSON({ + path: createUnknownFilePath(file.uid), + input: src, + }); - const rawJson = JSON.stringify(obj); - const json: string = rawJson === undefined ? 'undefined' : rawJson; + const rawJson = JSON.stringify(obj); + const json: string = rawJson === undefined ? 'undefined' : rawJson; - // TODO handle unicode newlines here - return { - sourceText: `export default ${json};`, - generated: true, - }; - }, + // TODO handle unicode newlines here + return { + sourceText: `export default ${json};`, + generated: true, + }; + }, }; export const rjsonHandler: ExtensionHandler = { - ...jsonHandler, - ext: 'rjson', + ...jsonHandler, + ext: 'rjson', }; diff --git a/packages/@romejs/core/common/file-handlers/text.ts b/packages/@romejs/core/common/file-handlers/text.ts index d821a33970e..351c17a59fb 100644 --- a/packages/@romejs/core/common/file-handlers/text.ts +++ b/packages/@romejs/core/common/file-handlers/text.ts @@ -9,34 +9,34 @@ import {UNKNOWN_ANALYZE_DEPENDENCIES_RESULT} from '../types/analyzeDependencies' import {PartialExtensionHandler} from './types'; export const textHandler: PartialExtensionHandler = { - sourceType: 'module', + sourceType: 'module', - // Mock a single default export - // We could always just pass this through to analyzeDependencies and get the same result due to the toJavaScript call below, - // but the return value is predictable so we inline it - async analyzeDependencies() { - return { - ...UNKNOWN_ANALYZE_DEPENDENCIES_RESULT, - moduleType: 'es', - exports: [ - { - type: 'local', - // TODO we could fake this? - loc: undefined, - kind: 'value', - valueType: 'other', - name: 'default', - }, - ], - }; - }, + // Mock a single default export + // We could always just pass this through to analyzeDependencies and get the same result due to the toJavaScript call below, + // but the return value is predictable so we inline it + async analyzeDependencies() { + return { + ...UNKNOWN_ANALYZE_DEPENDENCIES_RESULT, + moduleType: 'es', + exports: [ + { + type: 'local', + // TODO we could fake this? + loc: undefined, + kind: 'value', + valueType: 'other', + name: 'default', + }, + ], + }; + }, - async toJavaScript({file, worker}) { - const src = await worker.readFile(file.real); - const serial = JSON.stringify(src); - return { - sourceText: `export default ${serial};`, - generated: true, - }; - }, + async toJavaScript({file, worker}) { + const src = await worker.readFile(file.real); + const serial = JSON.stringify(src); + return { + sourceText: `export default ${serial};`, + generated: true, + }; + }, }; diff --git a/packages/@romejs/core/common/file-handlers/types.ts b/packages/@romejs/core/common/file-handlers/types.ts index e127da85cd8..16b98eaffa5 100644 --- a/packages/@romejs/core/common/file-handlers/types.ts +++ b/packages/@romejs/core/common/file-handlers/types.ts @@ -14,41 +14,41 @@ import {ConstProgramSyntax, ConstSourceType} from '@romejs/js-ast'; import {AnalyzeDependencyResult} from '../types/analyzeDependencies'; export type ExtensionLintInfo = ExtensionHandlerMethodInfo & { - options: WorkerLintOptions; + options: WorkerLintOptions; }; export type ExtensionLintResult = { - sourceText: string; - diagnostics: Diagnostics; - formatted: string; - suppressions: DiagnosticSuppressions; + sourceText: string; + diagnostics: Diagnostics; + formatted: string; + suppressions: DiagnosticSuppressions; }; export type ExtensionHandlerMethodInfo = { - parseOptions: WorkerParseOptions; - file: FileReference; - project: compiler.TransformProjectDefinition; - worker: Worker; + parseOptions: WorkerParseOptions; + file: FileReference; + project: compiler.TransformProjectDefinition; + worker: Worker; }; export type PartialExtensionHandler = { - sourceType?: ConstSourceType; - syntax?: Array; - isAsset?: boolean; - canHaveScale?: boolean; - lint?: (info: ExtensionLintInfo) => Promise; - format?: (info: ExtensionHandlerMethodInfo) => Promise; - toJavaScript?: ( - opts: ExtensionHandlerMethodInfo, - ) => Promise<{ - generated: boolean; - sourceText: string; - }>; - analyzeDependencies?: ( - opts: ExtensionHandlerMethodInfo, - ) => Promise; + sourceType?: ConstSourceType; + syntax?: Array; + isAsset?: boolean; + canHaveScale?: boolean; + lint?: (info: ExtensionLintInfo) => Promise; + format?: (info: ExtensionHandlerMethodInfo) => Promise; + toJavaScript?: ( + opts: ExtensionHandlerMethodInfo, + ) => Promise<{ + generated: boolean; + sourceText: string; + }>; + analyzeDependencies?: ( + opts: ExtensionHandlerMethodInfo, + ) => Promise; }; export type ExtensionHandler = PartialExtensionHandler & { - ext: string; + ext: string; }; diff --git a/packages/@romejs/core/common/types/analyzeDependencies.ts b/packages/@romejs/core/common/types/analyzeDependencies.ts index ceeaa548d41..7bfc1964cb8 100644 --- a/packages/@romejs/core/common/types/analyzeDependencies.ts +++ b/packages/@romejs/core/common/types/analyzeDependencies.ts @@ -7,9 +7,9 @@ import {Diagnostics} from '@romejs/diagnostics'; import { - ConstExportModuleKind, - ConstImportModuleKind, - ConstProgramSyntax, + ConstExportModuleKind, + ConstImportModuleKind, + ConstProgramSyntax, } from '@romejs/js-ast'; import {SourceLocation} from '@romejs/parser-core'; import {Dict} from '@romejs/typescript-helpers'; @@ -17,88 +17,88 @@ import {Dict} from '@romejs/typescript-helpers'; export type AnalyzeModuleType = 'es' | 'cjs' | 'unknown'; export type AnalyzeDependencyName = { - name: string; - kind: ConstImportModuleKind; - loc?: SourceLocation; + name: string; + kind: ConstImportModuleKind; + loc?: SourceLocation; }; export type AnalyzeExportValueType = 'class' | 'function' | 'other'; export type AnalyzeExportLocal = { - type: 'local'; - loc?: SourceLocation; - kind: ConstExportModuleKind; - valueType: AnalyzeExportValueType; - name: string; + type: 'local'; + loc?: SourceLocation; + kind: ConstExportModuleKind; + valueType: AnalyzeExportValueType; + name: string; }; export type AnyAnalyzeExport = - | AnalyzeExportLocal - | { - type: 'externalNamespace'; - kind: ConstImportModuleKind; - loc?: SourceLocation; - exported: string; - source: string; - } - | { - type: 'external'; - kind: ConstImportModuleKind; - loc?: SourceLocation; - imported: string; - exported: string; - source: string; - } - | { - type: 'externalAll'; - loc?: SourceLocation; - kind: ConstImportModuleKind; - source: string; - }; + | AnalyzeExportLocal + | { + type: 'externalNamespace'; + kind: ConstImportModuleKind; + loc?: SourceLocation; + exported: string; + source: string; + } + | { + type: 'external'; + kind: ConstImportModuleKind; + loc?: SourceLocation; + imported: string; + exported: string; + source: string; + } + | { + type: 'externalAll'; + loc?: SourceLocation; + kind: ConstImportModuleKind; + source: string; + }; export type AnalyzeDependency = { - names: Array; - async: boolean; - kind: ConstImportModuleKind; - type: AnalyzeModuleType; - loc?: SourceLocation; - all: boolean; - optional: boolean; - source: string; + names: Array; + async: boolean; + kind: ConstImportModuleKind; + type: AnalyzeModuleType; + loc?: SourceLocation; + all: boolean; + optional: boolean; + source: string; }; export type AnalyzeDependencyImportUsageItem = { - imported: string; - local: string; - source: string; - loc?: SourceLocation; - kind: ConstImportModuleKind; + imported: string; + local: string; + source: string; + loc?: SourceLocation; + kind: ConstImportModuleKind; }; export type AnalyzeDependencyImportFirstUsage = Array; export type AnalyzeDependencyTopLevelLocalBindings = Dict< - undefined | SourceLocation + undefined | SourceLocation >; export type AnalyzeDependencyResult = { - topLevelLocalBindings: AnalyzeDependencyTopLevelLocalBindings; - moduleType: AnalyzeModuleType; - syntax: Array; - diagnostics: Diagnostics; - firstTopAwaitLocation: undefined | SourceLocation; - importFirstUsage: AnalyzeDependencyImportFirstUsage; - exports: Array; - dependencies: Array; + topLevelLocalBindings: AnalyzeDependencyTopLevelLocalBindings; + moduleType: AnalyzeModuleType; + syntax: Array; + diagnostics: Diagnostics; + firstTopAwaitLocation: undefined | SourceLocation; + importFirstUsage: AnalyzeDependencyImportFirstUsage; + exports: Array; + dependencies: Array; }; export const UNKNOWN_ANALYZE_DEPENDENCIES_RESULT: AnalyzeDependencyResult = { - topLevelLocalBindings: {}, - moduleType: 'unknown', - syntax: [], - diagnostics: [], - firstTopAwaitLocation: undefined, - importFirstUsage: [], - exports: [], - dependencies: [], + topLevelLocalBindings: {}, + moduleType: 'unknown', + syntax: [], + diagnostics: [], + firstTopAwaitLocation: undefined, + importFirstUsage: [], + exports: [], + dependencies: [], }; diff --git a/packages/@romejs/core/common/types/bundler.ts b/packages/@romejs/core/common/types/bundler.ts index 116b2f2effc..d8696da502a 100644 --- a/packages/@romejs/core/common/types/bundler.ts +++ b/packages/@romejs/core/common/types/bundler.ts @@ -11,9 +11,9 @@ import {AbsoluteFilePath} from '@romejs/path'; import {ResolverOptions} from '../../master/fs/Resolver'; export type BundlerConfig = { - inlineSourceMap: boolean; - cwd: AbsoluteFilePath; - resolver: ResolverOptions; + inlineSourceMap: boolean; + cwd: AbsoluteFilePath; + resolver: ResolverOptions; }; export type BundlerMode = 'modern' | 'legacy'; @@ -21,41 +21,41 @@ export type BundlerMode = 'modern' | 'legacy'; export const BUNDLER_MODES: Array = ['modern', 'legacy']; export type BundleRequestResult = { - cached: boolean; - diagnostics: Diagnostics; - content: string; - sourceMap: SourceMapGenerator; - assets: Map; + cached: boolean; + diagnostics: Diagnostics; + content: string; + sourceMap: SourceMapGenerator; + assets: Map; }; export type BundleBuddyStats = Array; export type BundleBuddyGraphNode = { - source: string; - target: string | undefined; + source: string; + target: string | undefined; }; export type BundlerFiles = Map< - string, - { - kind: 'asset' | 'entry' | 'sourcemap' | 'stats' | 'manifest' | 'file'; - content: () => string | Buffer; - } + string, + { + kind: 'asset' | 'entry' | 'sourcemap' | 'stats' | 'manifest' | 'file'; + content: () => string | Buffer; + } >; export type BundleResultBundle = { - sourceMap: { - path: string; - map: SourceMapGenerator; - }; - js: { - path: string; - content: string; - }; + sourceMap: { + path: string; + map: SourceMapGenerator; + }; + js: { + path: string; + content: string; + }; }; export type BundleResult = { - files: BundlerFiles; - bundles: Array; - entry: BundleResultBundle; + files: BundlerFiles; + bundles: Array; + entry: BundleResultBundle; }; diff --git a/packages/@romejs/core/common/types/client.ts b/packages/@romejs/core/common/types/client.ts index 4c0238a6372..f9f54b90cfb 100644 --- a/packages/@romejs/core/common/types/client.ts +++ b/packages/@romejs/core/common/types/client.ts @@ -6,56 +6,56 @@ */ import { - DEFAULT_PRINTER_FLAGS, - DiagnosticsPrinterFlags, + DEFAULT_PRINTER_FLAGS, + DiagnosticsPrinterFlags, } from '@romejs/cli-diagnostics'; import {Platform} from './platform'; import {AbsoluteFilePath, CWD_PATH} from '@romejs/path'; export const DEFAULT_CLIENT_FLAGS: ClientFlags = { - clientName: 'unknown', - cwd: CWD_PATH, - silent: false, - verbose: false, + clientName: 'unknown', + cwd: CWD_PATH, + silent: false, + verbose: false, }; export const DEFAULT_CLIENT_REQUEST_FLAGS: ClientRequestFlags = { - ...DEFAULT_PRINTER_FLAGS, - showAllDiagnostics: false, - collectMarkers: false, - timing: false, - benchmark: false, - benchmarkIterations: 10, - watch: false, - review: false, - resolverPlatform: undefined, - resolverScale: undefined, - resolverMocks: false, + ...DEFAULT_PRINTER_FLAGS, + showAllDiagnostics: false, + collectMarkers: false, + timing: false, + benchmark: false, + benchmarkIterations: 10, + watch: false, + review: false, + resolverPlatform: undefined, + resolverScale: undefined, + resolverMocks: false, }; export type ClientRequestFlags = DiagnosticsPrinterFlags & { - watch: boolean; - review: boolean; - - // Debugging - timing: boolean; - collectMarkers: boolean; - benchmark: boolean; - benchmarkIterations: number; - - // Bundler flags - resolverPlatform: undefined | Platform; - resolverScale: undefined | number; - resolverMocks: boolean; + watch: boolean; + review: boolean; + + // Debugging + timing: boolean; + collectMarkers: boolean; + benchmark: boolean; + benchmarkIterations: number; + + // Bundler flags + resolverPlatform: undefined | Platform; + resolverScale: undefined | number; + resolverMocks: boolean; }; export type ClientFlags = { - clientName: string; - cwd: AbsoluteFilePath; - silent: boolean; - verbose: boolean; + clientName: string; + cwd: AbsoluteFilePath; + silent: boolean; + verbose: boolean; }; export type ClientFlagsJSON = Omit & { - cwd: string; + cwd: string; }; diff --git a/packages/@romejs/core/common/types/files.ts b/packages/@romejs/core/common/types/files.ts index 3ae15c0c5dc..79b91545127 100644 --- a/packages/@romejs/core/common/types/files.ts +++ b/packages/@romejs/core/common/types/files.ts @@ -6,31 +6,31 @@ */ import { - AbsoluteFilePath, - RelativeFilePath, - createAbsoluteFilePath, - createRelativeFilePath, + AbsoluteFilePath, + RelativeFilePath, + createAbsoluteFilePath, + createRelativeFilePath, } from '@romejs/path'; export type FileReference = { - project: number; - manifest: undefined | number; - uid: string; - relative: RelativeFilePath; - real: AbsoluteFilePath; - remote: boolean; + project: number; + manifest: undefined | number; + uid: string; + relative: RelativeFilePath; + real: AbsoluteFilePath; + remote: boolean; }; export type JSONFileReference = Omit & { - real: string; - relative: string; + real: string; + relative: string; }; export function convertTransportFileReference( - ref: JSONFileReference, + ref: JSONFileReference, ): FileReference { - return { - ...ref, - relative: createRelativeFilePath(ref.relative), - real: createAbsoluteFilePath(ref.real), - }; + return { + ...ref, + relative: createRelativeFilePath(ref.relative), + real: createAbsoluteFilePath(ref.real), + }; } diff --git a/packages/@romejs/core/common/types/platform.ts b/packages/@romejs/core/common/types/platform.ts index b3ee42f19f0..cd450f403fc 100644 --- a/packages/@romejs/core/common/types/platform.ts +++ b/packages/@romejs/core/common/types/platform.ts @@ -6,27 +6,27 @@ */ export type Platform = - | 'ios' - | 'android' - | 'mobile' - | 'electron' - | 'web' - | 'node'; + | 'ios' + | 'android' + | 'mobile' + | 'electron' + | 'web' + | 'node'; export const PLATFORMS: Array = [ - 'ios', - 'android', - 'mobile', - 'electron', - 'web', - 'node', + 'ios', + 'android', + 'mobile', + 'electron', + 'web', + 'node', ]; export const PLATFORM_ALIASES = { - ios: ['mobile'], - android: ['mobile'], - electron: ['web'], - mobile: [], - node: [], - web: [], + ios: ['mobile'], + android: ['mobile'], + electron: ['web'], + mobile: [], + node: [], + web: [], }; diff --git a/packages/@romejs/core/common/userConfig.ts b/packages/@romejs/core/common/userConfig.ts index 2a7f7b96dcc..029655610ec 100644 --- a/packages/@romejs/core/common/userConfig.ts +++ b/packages/@romejs/core/common/userConfig.ts @@ -9,59 +9,59 @@ import {consumeJSON} from '@romejs/codec-json'; import {VERSION} from './constants'; import {ROME_CONFIG_FILENAMES} from '@romejs/project'; import { - AbsoluteFilePath, - HOME_PATH, - TEMP_PATH, - createAbsoluteFilePath, + AbsoluteFilePath, + HOME_PATH, + TEMP_PATH, + createAbsoluteFilePath, } from '@romejs/path'; import {existsSync, readFileTextSync} from '@romejs/fs'; export type UserConfig = { - runtimeModulesPath: AbsoluteFilePath; - cachePath: AbsoluteFilePath; + runtimeModulesPath: AbsoluteFilePath; + cachePath: AbsoluteFilePath; }; const VERSION_PATH = TEMP_PATH.append(`rome-${VERSION}`); export const DEFAULT_USER_CONFIG: UserConfig = { - runtimeModulesPath: VERSION_PATH.append('runtime'), - cachePath: VERSION_PATH.append('cache'), + runtimeModulesPath: VERSION_PATH.append('runtime'), + cachePath: VERSION_PATH.append('cache'), }; export function loadUserConfig(): UserConfig { - for (const configFilename of ROME_CONFIG_FILENAMES) { - const configPath = HOME_PATH.append(['.config', configFilename]); + for (const configFilename of ROME_CONFIG_FILENAMES) { + const configPath = HOME_PATH.append(['.config', configFilename]); - if (!existsSync(configPath)) { - continue; - } + if (!existsSync(configPath)) { + continue; + } - const configFile = readFileTextSync(configPath); - const consumer = consumeJSON({ - path: configPath, - input: configFile, - }); + const configFile = readFileTextSync(configPath); + const consumer = consumeJSON({ + path: configPath, + input: configFile, + }); - const userConfig: UserConfig = { - ...DEFAULT_USER_CONFIG, - }; + const userConfig: UserConfig = { + ...DEFAULT_USER_CONFIG, + }; - if (consumer.has('cachePath')) { - userConfig.cachePath = createAbsoluteFilePath( - consumer.get('cachePath').asString(), - ); - } + if (consumer.has('cachePath')) { + userConfig.cachePath = createAbsoluteFilePath( + consumer.get('cachePath').asString(), + ); + } - if (consumer.has('runtimeModulesPath')) { - userConfig.runtimeModulesPath = createAbsoluteFilePath( - consumer.get('runtimeModulesPath').asString(), - ); - } + if (consumer.has('runtimeModulesPath')) { + userConfig.runtimeModulesPath = createAbsoluteFilePath( + consumer.get('runtimeModulesPath').asString(), + ); + } - consumer.enforceUsedProperties('config property'); + consumer.enforceUsedProperties('config property'); - return userConfig; - } + return userConfig; + } - return DEFAULT_USER_CONFIG; + return DEFAULT_USER_CONFIG; } diff --git a/packages/@romejs/core/common/utils/Locker.ts b/packages/@romejs/core/common/utils/Locker.ts index 0395dbd6448..28ab90dcaeb 100644 --- a/packages/@romejs/core/common/utils/Locker.ts +++ b/packages/@romejs/core/common/utils/Locker.ts @@ -8,72 +8,72 @@ type LockResolve = (lock: Lock) => void; class Lock { - constructor(locker: Locker, key: Key) { - this.locker = locker; - this.resolves = []; - this.key = key; - } + constructor(locker: Locker, key: Key) { + this.locker = locker; + this.resolves = []; + this.key = key; + } - locker: Locker; - resolves: Array>; - key: Key; + locker: Locker; + resolves: Array>; + key: Key; - addResolve(resolve: LockResolve) { - this.resolves.push(resolve); - } + addResolve(resolve: LockResolve) { + this.resolves.push(resolve); + } - release() { - const {resolves} = this; + release() { + const {resolves} = this; - if (resolves.length === 0) { - this.locker.locks.delete(this.key); - } else { - const resolve = resolves.shift(); - if (resolve === undefined) { - throw new Error('Already validated resolved.length aboved'); - } - resolve(this); - } - } + if (resolves.length === 0) { + this.locker.locks.delete(this.key); + } else { + const resolve = resolves.shift(); + if (resolve === undefined) { + throw new Error('Already validated resolved.length aboved'); + } + resolve(this); + } + } } export default class Locker { - constructor() { - this.locks = new Map(); - } + constructor() { + this.locks = new Map(); + } - locks: Map>; + locks: Map>; - hasLock(id: Key): boolean { - return this.locks.has(id); - } + hasLock(id: Key): boolean { + return this.locks.has(id); + } - getNewLock(key: Key): Lock { - if (this.locks.has(key)) { - throw new Error('Expected no lock to exist'); - } + getNewLock(key: Key): Lock { + if (this.locks.has(key)) { + throw new Error('Expected no lock to exist'); + } - const lock = new Lock(this, key); - this.locks.set(key, lock); - return lock; - } + const lock = new Lock(this, key); + this.locks.set(key, lock); + return lock; + } - async getLock(key: Key): Promise> { - const existingLock = this.locks.get(key); + async getLock(key: Key): Promise> { + const existingLock = this.locks.get(key); - if (existingLock === undefined) { - return this.getNewLock(key); - } else { - return new Promise((resolve) => { - existingLock.addResolve(resolve); - }); - } - } + if (existingLock === undefined) { + return this.getNewLock(key); + } else { + return new Promise((resolve) => { + existingLock.addResolve(resolve); + }); + } + } - async waitLock(key: Key): Promise { - if (this.hasLock(key)) { - const lock = await this.getLock(key); - lock.release(); - } - } + async waitLock(key: Key): Promise { + if (this.hasLock(key)) { + const lock = await this.getLock(key); + lock.release(); + } + } } diff --git a/packages/@romejs/core/common/utils/Logger.ts b/packages/@romejs/core/common/utils/Logger.ts index 2405ac385a3..0e5c5c37277 100644 --- a/packages/@romejs/core/common/utils/Logger.ts +++ b/packages/@romejs/core/common/utils/Logger.ts @@ -8,18 +8,18 @@ import {Reporter, ReporterOptions} from '@romejs/cli-reporter'; export default class Logger extends Reporter { - constructor(name: string, isEnabled: () => boolean, opts: ReporterOptions) { - super({ - verbose: true, - ...opts, - }); - this._loggerName = name; - this.isEnabled = isEnabled; - } + constructor(name: string, isEnabled: () => boolean, opts: ReporterOptions) { + super({ + verbose: true, + ...opts, + }); + this._loggerName = name; + this.isEnabled = isEnabled; + } - _loggerName: string; + _loggerName: string; - getMessagePrefix() { - return `[${this._loggerName} ${process.pid}] `; - } + getMessagePrefix() { + return `[${this._loggerName} ${process.pid}] `; + } } diff --git a/packages/@romejs/core/common/utils/executeMain.ts b/packages/@romejs/core/common/utils/executeMain.ts index 333e1faa141..f249bc0b52f 100644 --- a/packages/@romejs/core/common/utils/executeMain.ts +++ b/packages/@romejs/core/common/utils/executeMain.ts @@ -13,102 +13,102 @@ import internalModule = require('module'); import vm = require('vm'); import { - Diagnostic, - INTERNAL_ERROR_LOG_ADVICE, - descriptions, - truncateSourceText, + Diagnostic, + INTERNAL_ERROR_LOG_ADVICE, + descriptions, + truncateSourceText, } from '@romejs/diagnostics'; import {AbsoluteFilePath} from '@romejs/path'; import {Position} from '@romejs/parser-core'; import {ob1Coerce1, ob1Number0, ob1Number0Neg1} from '@romejs/ob1'; type ExecuteMainOptions = { - path: AbsoluteFilePath; - code: string; - sourceMap?: SourceMapConsumer; - globals?: UnknownObject; + path: AbsoluteFilePath; + code: string; + sourceMap?: SourceMapConsumer; + globals?: UnknownObject; }; export default async function executeMain( - opts: ExecuteMainOptions, + opts: ExecuteMainOptions, ): Promise<{ - syntaxError: undefined | Diagnostic; + syntaxError: undefined | Diagnostic; }> { - const {path, code, sourceMap, globals} = opts; + const {path, code, sourceMap, globals} = opts; - const filename = path.join(); + const filename = path.join(); - // Create global context - const sandbox: UnknownObject = { - process: { - argv: [process.argv[0], filename], - __proto__: process, - }, - Buffer, - clearImmediate, - clearInterval, - clearTimeout, - setImmediate, - setInterval, - setTimeout, - require: internalModule.createRequire - ? internalModule.createRequire(filename) - : internalModule.createRequireFromPath(filename), - console, - __dirname: path.getParent().join(), - __filename: filename, - ...globals, - }; - sandbox.global = sandbox; - const context = vm.createContext(sandbox); + // Create global context + const sandbox: UnknownObject = { + process: { + argv: [process.argv[0], filename], + __proto__: process, + }, + Buffer, + clearImmediate, + clearInterval, + clearTimeout, + setImmediate, + setInterval, + setTimeout, + require: internalModule.createRequire + ? internalModule.createRequire(filename) + : internalModule.createRequireFromPath(filename), + console, + __dirname: path.getParent().join(), + __filename: filename, + ...globals, + }; + sandbox.global = sandbox; + const context = vm.createContext(sandbox); - // Here we do some gymnastics to catch a syntax error to correctly identify it as being our fault - let script; - try { - script = new vm.Script( - code, - { - filename, - displayErrors: true, - }, - ); - } catch (err) { - if (err instanceof SyntaxError && err.stack !== undefined) { - const lineMatch = err.stack.match(/^(.*?):(\d+)/); - if (lineMatch == null) { - throw err; - } + // Here we do some gymnastics to catch a syntax error to correctly identify it as being our fault + let script; + try { + script = new vm.Script( + code, + { + filename, + displayErrors: true, + }, + ); + } catch (err) { + if (err instanceof SyntaxError && err.stack !== undefined) { + const lineMatch = err.stack.match(/^(.*?):(\d+)/); + if (lineMatch == null) { + throw err; + } - const line = Number(lineMatch[2]); + const line = Number(lineMatch[2]); - const pos: Position = { - index: ob1Number0Neg1, - column: ob1Number0, - line: ob1Coerce1(line), - }; + const pos: Position = { + index: ob1Number0Neg1, + column: ob1Number0, + line: ob1Coerce1(line), + }; - const syntaxError: Diagnostic = { - description: { - ...descriptions.V8.SYNTAX_ERROR(err.message), - advice: [INTERNAL_ERROR_LOG_ADVICE], - }, - location: { - start: pos, - end: pos, - filename, - sourceText: truncateSourceText(code, pos, pos), - }, - }; - return {syntaxError}; - } + const syntaxError: Diagnostic = { + description: { + ...descriptions.V8.SYNTAX_ERROR(err.message), + advice: [INTERNAL_ERROR_LOG_ADVICE], + }, + location: { + start: pos, + end: pos, + filename, + sourceText: truncateSourceText(code, pos, pos), + }, + }; + return {syntaxError}; + } - throw err; - } + throw err; + } - // Execute the script if there was no syntax error - if (sourceMap !== undefined) { - sourceMapManager.addSourceMap(filename, () => sourceMap); - } - await script.runInContext(context); - return {syntaxError: undefined}; + // Execute the script if there was no syntax error + if (sourceMap !== undefined) { + sourceMapManager.add(filename, sourceMap); + } + await script.runInContext(context); + return {syntaxError: undefined}; } diff --git a/packages/@romejs/core/common/utils/fork.ts b/packages/@romejs/core/common/utils/fork.ts index 8194d7d5282..1b2fd97fbfd 100644 --- a/packages/@romejs/core/common/utils/fork.ts +++ b/packages/@romejs/core/common/utils/fork.ts @@ -9,22 +9,22 @@ import {BIN, CHILD_ARGS, VERSION} from '@romejs/core'; import child = require('child_process'); export default function fork( - processType: string, - opts: child.ForkOptions = {}, - args: Array = [], + processType: string, + opts: child.ForkOptions = {}, + args: Array = [], ): child.ChildProcess { - return child.fork( - BIN.join(), - args, - { - stdio: 'inherit', - execArgv: CHILD_ARGS, - ...opts, - env: { - ...process.env, - ROME_PROCESS_VERSION: VERSION, - ROME_PROCESS_TYPE: processType, - }, - }, - ); + return child.fork( + BIN.join(), + args, + { + stdio: 'inherit', + execArgv: CHILD_ARGS, + ...opts, + env: { + ...process.env, + ROME_PROCESS_VERSION: VERSION, + ROME_PROCESS_TYPE: processType, + }, + }, + ); } diff --git a/packages/@romejs/core/common/utils/setupGlobalErrorHandlers.ts b/packages/@romejs/core/common/utils/setupGlobalErrorHandlers.ts index e12f0b7a0e0..51704b557b6 100644 --- a/packages/@romejs/core/common/utils/setupGlobalErrorHandlers.ts +++ b/packages/@romejs/core/common/utils/setupGlobalErrorHandlers.ts @@ -6,28 +6,28 @@ */ export default function setupGlobalErrorHandlers( - callback: (err: Error) => void, + callback: (err: Error) => void, ): () => void { - const onUncaughtException: NodeJS.UncaughtExceptionListener = (err: Error) => { - callback(err); - }; - process.on('uncaughtException', onUncaughtException); + const onUncaughtException: NodeJS.UncaughtExceptionListener = (err: Error) => { + callback(err); + }; + process.on('uncaughtException', onUncaughtException); - const onUnhandledRejection: NodeJS.UnhandledRejectionListener = ( - reason: unknown, - promise: Promise, - ) => { - promise.then(() => { - throw new Error('Promise is rejected so should never hit this condition'); - }).catch((err) => { - console.error(err); - callback(err); - }); - }; - process.on('unhandledRejection', onUnhandledRejection); + const onUnhandledRejection: NodeJS.UnhandledRejectionListener = ( + reason: unknown, + promise: Promise, + ) => { + promise.then(() => { + throw new Error('Promise is rejected so should never hit this condition'); + }).catch((err) => { + console.error(err); + callback(err); + }); + }; + process.on('unhandledRejection', onUnhandledRejection); - return () => { - process.removeListener('uncaughtException', onUncaughtException); - process.removeListener('unhandledRejection', onUnhandledRejection); - }; + return () => { + process.removeListener('uncaughtException', onUncaughtException); + process.removeListener('unhandledRejection', onUnhandledRejection); + }; } diff --git a/packages/@romejs/core/index.ts b/packages/@romejs/core/index.ts index 817d5bd6468..25955472cba 100644 --- a/packages/@romejs/core/index.ts +++ b/packages/@romejs/core/index.ts @@ -39,7 +39,7 @@ export {default as WorkerBridge} from './common/bridges/WorkerBridge'; export {default as MasterBridge} from './common/bridges/MasterBridge'; export {default as WebBridge} from './common/bridges/WebBridge'; export { - MasterQueryRequest, - MasterQueryResponse, + MasterQueryRequest, + MasterQueryResponse, } from './common/bridges/MasterBridge'; export {default as TestWorkerBridge} from './common/bridges/TestWorkerBridge'; diff --git a/packages/@romejs/core/master/Cache.ts b/packages/@romejs/core/master/Cache.ts index 4ffde80a05f..741b568a165 100644 --- a/packages/@romejs/core/master/Cache.ts +++ b/packages/@romejs/core/master/Cache.ts @@ -6,9 +6,9 @@ */ import { - WorkerAnalyzeDependencyResult, - WorkerCompileResult, - WorkerLintResult, + WorkerAnalyzeDependencyResult, + WorkerCompileResult, + WorkerLintResult, } from '../common/bridges/WorkerBridge'; import {ModuleSignature} from '@romejs/js-analysis'; import Master from './Master'; @@ -16,182 +16,183 @@ import {DEFAULT_PROJECT_CONFIG, ProjectDefinition} from '@romejs/project'; import {VERSION} from '../common/constants'; import {AbsoluteFilePath, AbsoluteFilePathMap} from '@romejs/path'; import {createDirectory, readFileText, unlink, writeFile} from '@romejs/fs'; +import {stringifyJSON} from '@romejs/codec-json'; type CacheEntry = { - version: string; - configHash: string; - projectDir: string; - mtime: number; - compile: { - [key: string]: WorkerCompileResult; - }; - lint: { - [key: string]: WorkerLintResult; - }; - analyzeDependencies: undefined | WorkerAnalyzeDependencyResult; - moduleSignature: undefined | ModuleSignature; + version: string; + configHash: string; + projectDir: string; + mtime: number; + compile: { + [key: string]: WorkerCompileResult; + }; + lint: { + [key: string]: WorkerLintResult; + }; + analyzeDependencies: undefined | WorkerAnalyzeDependencyResult; + moduleSignature: undefined | ModuleSignature; }; // Basic checks to determine if we can consider a and b to be mergable function areEntriesEqual(a: CacheEntry, b: CacheEntry): boolean { - if (a.version !== b.version) { - // Outdated cache file - return false; - } - - if (a.configHash !== b.configHash) { - // Project config has been changed since this was last updated - return false; - } - - if (a.mtime !== b.mtime) { - // File has been changed - return false; - } - - return true; + if (a.version !== b.version) { + // Outdated cache file + return false; + } + + if (a.configHash !== b.configHash) { + // Project config has been changed since this was last updated + return false; + } + + if (a.mtime !== b.mtime) { + // File has been changed + return false; + } + + return true; } export default class Cache { - constructor(master: Master) { - this.master = master; - this.loadedEntries = new AbsoluteFilePathMap(); - this.disabled = process.env.ROME_CACHE === '0'; - this.cachePath = master.userConfig.cachePath; - } - - disabled: boolean; - loadedEntries: AbsoluteFilePathMap; - master: Master; - cachePath: AbsoluteFilePath; - - async init() { - this.master.memoryFs.deletedFileEvent.subscribe((filename) => { - return this.master.cache.handleDeleted(filename); - }); - - const {memoryFs} = this.master; - await createDirectory(this.cachePath, {recursive: true}); - await memoryFs.watch(this.cachePath, DEFAULT_PROJECT_CONFIG); - } - - async createEmptyEntry(path: AbsoluteFilePath): Promise { - const {projectManager, memoryFs} = this.master; - - const project: ProjectDefinition = await projectManager.assertProject(path); - - const configHashes = [...project.meta.configHashes]; - const pkg = this.master.memoryFs.getOwnedManifest(path); - if (pkg !== undefined) { - configHashes.push(pkg.hash); - } - - const entry: CacheEntry = { - version: VERSION, - projectDir: project.folder.join(), - configHash: configHashes.join(';'), - mtime: memoryFs.getMtime(path), - compile: {}, - analyzeDependencies: undefined, - moduleSignature: undefined, - lint: {}, - }; - - return entry; - } - - getCacheFilename(path: AbsoluteFilePath): AbsoluteFilePath { - const uid = this.master.projectManager.getUid(path); - return this.cachePath.append(uid); - } - - async handleDeleted(path: AbsoluteFilePath) { - // Handle the file not existing - const cacheFilename = this.getCacheFilename(path); - await unlink(cacheFilename); - this.loadedEntries.delete(path); - } - - async get(path: AbsoluteFilePath): Promise { - const emptyEntry = await this.createEmptyEntry(path); - - // If we have a loaded memory entry, make sure it's valid compared to the default entry (file changes etc) - let loaded = this.loadedEntries.get(path); - if (loaded !== undefined && areEntriesEqual(loaded, emptyEntry)) { - return loaded; - } - - if (this.disabled) { - return emptyEntry; - } - - const cacheFilename = this.getCacheFilename(path); - const entry = await this.checkPossibleDiskCacheEntry( - cacheFilename, - emptyEntry, - ); - this.loadedEntries.set(path, entry); - return entry; - } - - async checkPossibleDiskCacheEntry( - cacheFilename: AbsoluteFilePath, - emptyEntry: CacheEntry, - ): Promise { - const {memoryFs} = this.master; - - if (!memoryFs.exists(cacheFilename)) { - return emptyEntry; - } - - try { - const json = await readFileText(cacheFilename); - const obj = JSON.parse(json); - - if (areEntriesEqual(emptyEntry, obj)) { - return {...emptyEntry, ...obj}; - } else { - // If the entries aren't equal then there's something wrong with the cache entry - await this.handleDeleted(cacheFilename); - return emptyEntry; - } - } catch (err) { - // TODO add some heuristic to only catch json and cache permission errors - return emptyEntry; - } - } - - async update( - path: AbsoluteFilePath, - partialEntryCallback: - | Partial - | ((entry: CacheEntry) => Partial), - ) { - const currEntry = await this.get(path); - const partialEntry: Partial = - typeof partialEntryCallback === 'function' - ? partialEntryCallback(currEntry) - : partialEntryCallback; - - const entry: CacheEntry = { - ...currEntry, - ...partialEntry, - }; - - // TODO should batch these and write during idle time - const cacheFilename = this.getCacheFilename(path); - this.loadedEntries.set(path, entry); - - if (this.disabled) { - return; - } - - await createDirectory( - cacheFilename.getParent(), - { - recursive: true, - }, - ); - await writeFile(cacheFilename, JSON.stringify(entry, null, ' ')); - } + constructor(master: Master) { + this.master = master; + this.loadedEntries = new AbsoluteFilePathMap(); + this.disabled = process.env.ROME_CACHE === '0'; + this.cachePath = master.userConfig.cachePath; + } + + disabled: boolean; + loadedEntries: AbsoluteFilePathMap; + master: Master; + cachePath: AbsoluteFilePath; + + async init() { + this.master.memoryFs.deletedFileEvent.subscribe((filename) => { + return this.master.cache.handleDeleted(filename); + }); + + const {memoryFs} = this.master; + await createDirectory(this.cachePath, {recursive: true}); + await memoryFs.watch(this.cachePath, DEFAULT_PROJECT_CONFIG); + } + + async createEmptyEntry(path: AbsoluteFilePath): Promise { + const {projectManager, memoryFs} = this.master; + + const project: ProjectDefinition = await projectManager.assertProject(path); + + const configHashes = [...project.meta.configHashes]; + const pkg = this.master.memoryFs.getOwnedManifest(path); + if (pkg !== undefined) { + configHashes.push(pkg.hash); + } + + const entry: CacheEntry = { + version: VERSION, + projectDir: project.folder.join(), + configHash: configHashes.join(';'), + mtime: memoryFs.getMtime(path), + compile: {}, + analyzeDependencies: undefined, + moduleSignature: undefined, + lint: {}, + }; + + return entry; + } + + getCacheFilename(path: AbsoluteFilePath): AbsoluteFilePath { + const uid = this.master.projectManager.getUid(path); + return this.cachePath.append(uid); + } + + async handleDeleted(path: AbsoluteFilePath) { + // Handle the file not existing + const cacheFilename = this.getCacheFilename(path); + await unlink(cacheFilename); + this.loadedEntries.delete(path); + } + + async get(path: AbsoluteFilePath): Promise { + const emptyEntry = await this.createEmptyEntry(path); + + // If we have a loaded memory entry, make sure it's valid compared to the default entry (file changes etc) + let loaded = this.loadedEntries.get(path); + if (loaded !== undefined && areEntriesEqual(loaded, emptyEntry)) { + return loaded; + } + + if (this.disabled) { + return emptyEntry; + } + + const cacheFilename = this.getCacheFilename(path); + const entry = await this.checkPossibleDiskCacheEntry( + cacheFilename, + emptyEntry, + ); + this.loadedEntries.set(path, entry); + return entry; + } + + async checkPossibleDiskCacheEntry( + cacheFilename: AbsoluteFilePath, + emptyEntry: CacheEntry, + ): Promise { + const {memoryFs} = this.master; + + if (!memoryFs.exists(cacheFilename)) { + return emptyEntry; + } + + try { + const json = await readFileText(cacheFilename); + const obj = JSON.parse(json); + + if (areEntriesEqual(emptyEntry, obj)) { + return {...emptyEntry, ...obj}; + } else { + // If the entries aren't equal then there's something wrong with the cache entry + await this.handleDeleted(cacheFilename); + return emptyEntry; + } + } catch (err) { + // TODO add some heuristic to only catch json and cache permission errors + return emptyEntry; + } + } + + async update( + path: AbsoluteFilePath, + partialEntryCallback: + | Partial + | ((entry: CacheEntry) => Partial), + ) { + const currEntry = await this.get(path); + const partialEntry: Partial = + typeof partialEntryCallback === 'function' + ? partialEntryCallback(currEntry) + : partialEntryCallback; + + const entry: CacheEntry = { + ...currEntry, + ...partialEntry, + }; + + // TODO should batch these and write during idle time + const cacheFilename = this.getCacheFilename(path); + this.loadedEntries.set(path, entry); + + if (this.disabled) { + return; + } + + await createDirectory( + cacheFilename.getParent(), + { + recursive: true, + }, + ); + await writeFile(cacheFilename, stringifyJSON(entry)); + } } diff --git a/packages/@romejs/core/master/Master.ts b/packages/@romejs/core/master/Master.ts index 7129728bd21..02ad7c0c724 100644 --- a/packages/@romejs/core/master/Master.ts +++ b/packages/@romejs/core/master/Master.ts @@ -6,32 +6,32 @@ */ import { - MasterBridge, - MasterQueryRequest, - MasterQueryResponse, + MasterBridge, + MasterQueryRequest, + MasterQueryResponse, } from '@romejs/core'; import { - DiagnosticOrigin, - Diagnostics, - DiagnosticsProcessor, - INTERNAL_ERROR_LOG_ADVICE, - deriveDiagnosticFromError, - descriptions, - getDiagnosticsFromError, + DiagnosticOrigin, + Diagnostics, + DiagnosticsProcessor, + INTERNAL_ERROR_LOG_ADVICE, + deriveDiagnosticFromError, + descriptions, + getDiagnosticsFromError, } from '@romejs/diagnostics'; import {MasterCommand, masterCommands} from './commands'; import { - DiagnosticsFileReader, - DiagnosticsPrinter, - printDiagnostics, - readDiagnosticsFileLocal, + DiagnosticsFileReader, + DiagnosticsPrinter, + printDiagnostics, + readDiagnosticsFileLocal, } from '@romejs/cli-diagnostics'; import {ConsumePath, consume} from '@romejs/consume'; import {Event, EventSubscription} from '@romejs/events'; import MasterRequest, { - EMPTY_SUCCESS_RESPONSE, - MasterRequestCancelled, - MasterRequestInvalid, + EMPTY_SUCCESS_RESPONSE, + MasterRequestCancelled, + MasterRequestInvalid, } from './MasterRequest'; import ProjectManager from './project/ProjectManager'; import WorkerManager from './WorkerManager'; @@ -43,22 +43,22 @@ import Cache from './Cache'; import {Reporter, ReporterStream} from '@romejs/cli-reporter'; import {Profiler} from '@romejs/v8'; import { - PartialMasterQueryRequest, - ProfilingStartData, + PartialMasterQueryRequest, + ProfilingStartData, } from '../common/bridges/MasterBridge'; import { - ClientFlags, - ClientRequestFlags, - DEFAULT_CLIENT_REQUEST_FLAGS, + ClientFlags, + ClientRequestFlags, + DEFAULT_CLIENT_REQUEST_FLAGS, } from '../common/types/client'; import {VERSION} from '../common/constants'; import {escapeMarkup} from '@romejs/string-markup'; import setupGlobalErrorHandlers from '../common/utils/setupGlobalErrorHandlers'; import {UserConfig, loadUserConfig} from '../common/userConfig'; import { - AbsoluteFilePath, - createAbsoluteFilePath, - createUnknownFilePath, + AbsoluteFilePath, + createAbsoluteFilePath, + createUnknownFilePath, } from '@romejs/path'; import {Dict} from '@romejs/typescript-helpers'; import LSPServer from './lsp/LSPServer'; @@ -70,927 +70,921 @@ import {toKebabCase} from '@romejs/string-utils'; const STDOUT_MAX_CHUNK_LENGTH = 100_000; export type MasterClient = { - id: number; - reporter: Reporter; - bridge: MasterBridge; - flags: ClientFlags; - version: string; - requestsInFlight: Set; + id: number; + reporter: Reporter; + bridge: MasterBridge; + flags: ClientFlags; + version: string; + requestsInFlight: Set; }; export type MasterOptions = { - dedicated: boolean; - globalErrorHandlers: boolean; + dedicated: boolean; + globalErrorHandlers: boolean; }; export type MasterUnfinishedMarker = { - // Text label to be associated with the timeline entry - label: string; + // Text label to be associated with the timeline entry + label: string; - // Start time in milliseconds - start: number; + // Start time in milliseconds + start: number; - // Process / Thread that the events are associated with - rowId: string; + // Process / Thread that the events are associated with + rowId: string; - // - facet: string; + // + facet: string; }; export type MasterMarker = MasterUnfinishedMarker & { - // End time in milliseconds - end: number; + // End time in milliseconds + end: number; }; const disallowedFlagsWhenReviewing: Array = ['watch']; async function validateRequestFlags( - req: MasterRequest, - masterCommand: MasterCommand>, + req: MasterRequest, + masterCommand: MasterCommand>, ) { - const {requestFlags} = req.query; - - // Commands need to explicitly allow these flags - validateAllowedRequestFlag(req, 'watch', masterCommand); - validateAllowedRequestFlag(req, 'review', masterCommand); - - // Don't allow review in combination with other flags - if (requestFlags.review) { - for (const key of disallowedFlagsWhenReviewing) { - if (requestFlags[key]) { - throw req.throwDiagnosticFlagError({ - description: descriptions.FLAGS.DISALLOWED_REVIEW_FLAG(key), - target: {type: 'flag', key}, - }); - } - } - } + const {requestFlags} = req.query; + + // Commands need to explicitly allow these flags + validateAllowedRequestFlag(req, 'watch', masterCommand); + validateAllowedRequestFlag(req, 'review', masterCommand); + + // Don't allow review in combination with other flags + if (requestFlags.review) { + for (const key of disallowedFlagsWhenReviewing) { + if (requestFlags[key]) { + throw req.throwDiagnosticFlagError({ + description: descriptions.FLAGS.DISALLOWED_REVIEW_FLAG(key), + target: {type: 'flag', key}, + }); + } + } + } } function validateAllowedRequestFlag( - req: MasterRequest, - flagKey: NonNullable>['allowRequestFlags']>[number], - masterCommand: MasterCommand>, + req: MasterRequest, + flagKey: NonNullable>['allowRequestFlags']>[number], + masterCommand: MasterCommand>, ) { - const allowRequestFlags = masterCommand.allowRequestFlags || []; - if (req.query.requestFlags[flagKey] && !allowRequestFlags.includes(flagKey)) { - throw req.throwDiagnosticFlagError({ - description: descriptions.FLAGS.DISALLOWED_REQUEST_FLAG(flagKey), - target: {type: 'flag', key: flagKey}, - }); - } + const allowRequestFlags = masterCommand.allowRequestFlags || []; + if (req.query.requestFlags[flagKey] && !allowRequestFlags.includes(flagKey)) { + throw req.throwDiagnosticFlagError({ + description: descriptions.FLAGS.DISALLOWED_REQUEST_FLAG(flagKey), + target: {type: 'flag', key: flagKey}, + }); + } } export default class Master { - constructor(opts: MasterOptions) { - this.onFatalErrorBound = this.onFatalError.bind(this); - - this.profiling = undefined; - this.options = opts; - - this.userConfig = loadUserConfig(); - - this.fileChangeEvent = new Event({ - name: 'Master.fileChange', - onError: this.onFatalErrorBound, - }); - - this.clientStartEvent = new Event({ - name: 'Master.clientStart', - onError: this.onFatalErrorBound, - }); - - this.requestStartEvent = new Event({ - name: 'Master.requestStart', - onError: this.onFatalErrorBound, - }); - - this.logEvent = new Event({ - name: 'Master.log', - onError: this.onFatalErrorBound, - }); - - this.endEvent = new Event({ - name: 'Master.end', - onError: this.onFatalErrorBound, - serial: true, - }); - - this.logger = new Logger( - 'master', - () => { - return ( - this.logEvent.hasSubscribers() || - this.connectedClientsListeningForLogs.size > 0 - ); - }, - { - streams: [ - { - type: 'all', - format: 'none', - columns: 0, - unicode: true, - write: (chunk) => { - this.emitMasterLog(chunk); - }, - }, - ], - markupOptions: { - humanizeFilename: (filename) => { - const path = createUnknownFilePath(filename); - if (path.isAbsolute()) { - const remote = this.projectManager.getRemoteFromLocalPath( - path.assertAbsolute(), - ); - if (remote !== undefined) { - return remote.join(); - } - } - return undefined; - }, - normalizeFilename: (filename: string): string => { - const path = this.projectManager.getFilePathFromUid(filename); - if (path === undefined) { - return filename; - } else { - return path.join(); - } - }, - }, - }, - ); - - this.connectedReporters = new MasterReporter(this); - - this.connectedClientsListeningForLogs = new Set(); - this.connectedLSPServers = new Set(); - this.connectedClients = new Set(); - - this.virtualModules = new VirtualModules(this); - this.memoryFs = new MemoryFileSystem(this); - this.projectManager = new ProjectManager(this); - this.workerManager = new WorkerManager(this); - this.fileAllocator = new FileAllocator(this); - this.resolver = new Resolver(this); - this.cache = new Cache(this); - - this.memoryFs.deletedFileEvent.subscribe((path) => { - return this.handleFileDelete(path); - }); - - this.memoryFs.changedFileEvent.subscribe(({path}) => { - return this.handleFileChange(path); - }); - - this.warnedCacheClients = new WeakSet(); - - this.clientIdCounter = 0; - - this.requestRunningCounter = 0; - this.terminateWhenIdle = false; - } - - userConfig: UserConfig; - - requestStartEvent: Event; - clientStartEvent: Event; - fileChangeEvent: Event; - logEvent: Event; - endEvent: Event; - - onFatalErrorBound: (err: Error) => void; - - requestRunningCounter: number; - terminateWhenIdle: boolean; - - clientIdCounter: number; - - profiling: undefined | ProfilingStartData; - options: MasterOptions; - - warnedCacheClients: WeakSet; - memoryFs: MemoryFileSystem; - virtualModules: VirtualModules; - resolver: Resolver; - projectManager: ProjectManager; - workerManager: WorkerManager; - fileAllocator: FileAllocator; - cache: Cache; - connectedReporters: MasterReporter; - logger: Logger; - - connectedClients: Set; - connectedLSPServers: Set; - connectedClientsListeningForLogs: Set; - - emitMasterLog(chunk: string) { - this.logEvent.send(chunk); - - for (const {bridge} of this.connectedClientsListeningForLogs) { - bridge.log.send({chunk, origin: 'master'}); - } - } - - onFatalError(err: Error) { - const message = `Fatal error occurred: ${escapeMarkup( - err.stack || err.message, - )}`; - this.logger.error(message); - this.connectedReporters.error(message); - process.exit(); - } - - // rome-ignore lint/noExplicitAny - wrapFatal) => any>(callback: T): T { - return (((...args: Array): any => { - try { - const res = callback(...args); - if (res instanceof Promise) { - res.catch(this.onFatalErrorBound); - } - return res; - } catch (err) { - throw this.onFatalError(err); - } - }) as T); - } - - async handleDisconnectedDiagnostics(diagnostics: Diagnostics) { - this.connectedReporters.error( - 'Generated diagnostics without a current request', - ); - - printDiagnostics({ - diagnostics, - suppressions: [], - printerOptions: { - processor: this.createDiagnosticsProcessor(), - reporter: this.connectedReporters, - readFile: this.readDiagnosticsPrinterFile.bind(this), - }, - }); - } - - readDiagnosticsPrinterFile( - path: AbsoluteFilePath, - ): ReturnType { - const remoteToLocal = this.projectManager.remoteToLocalPath.get(path); - - if (remoteToLocal === undefined) { - return readDiagnosticsFileLocal(path); - } else { - return readDiagnosticsFileLocal(remoteToLocal); - } - } - - createDiagnosticsProcessor( - opts: DiagnosticsProcessorOptions = {}, - ): DiagnosticsProcessor { - return new DiagnosticsProcessor({ - markupOptions: this.logger.markupOptions, - ...opts, - }); - } - - createDisconnectedDiagnosticsProcessor( - origins: Array, - ): DiagnosticsProcessor { - return this.createDiagnosticsProcessor({ - onDiagnostics: (diagnostics: Diagnostics) => { - this.handleDisconnectedDiagnostics(diagnostics); - }, - origins: [ - ...origins, - { - category: 'master', - message: 'Created disconnected diagnostics collector', - }, - ], - }); - } - - maybeSetupGlobalErrorHandlers() { - if (!this.options.globalErrorHandlers) { - return; - } - - const teardown = setupGlobalErrorHandlers((err) => { - this.onFatalError(err); - }); - - this.endEvent.subscribe(() => { - teardown(); - }); - } - - async init() { - this.maybeSetupGlobalErrorHandlers(); - this.memoryFs.init(); - await this.projectManager.init(); - this.fileAllocator.init(); - this.resolver.init(); - await this.cache.init(); - await this.virtualModules.init(); - await this.workerManager.init(); - } - - async end() { - // Cancel all queries in flight - for (const client of this.connectedClients) { - for (const req of client.requestsInFlight) { - req.cancel(); - } - } - - // We should remove everything that has an external dependency like a socket or process - await this.endEvent.callOptional(); - this.workerManager.end(); - this.memoryFs.unwatchAll(); - } - - async attachToBridge(bridge: MasterBridge) { - let profiler: undefined | Profiler; - - // If we aren't a dedicated process then we should only expect a single connection - // and when that ends. End the Master. - if (this.options.dedicated === false) { - bridge.endEvent.subscribe(() => { - this.end(); - }); - } - - bridge.profilingStart.subscribe(async (data) => { - if (profiler !== undefined) { - throw new Error('Expected no profiler to be running'); - } - profiler = new Profiler(); - await profiler.startProfiling(data.samplingInterval); - this.profiling = data; - for (const {bridge} of this.workerManager.getExternalWorkers()) { - await bridge.profilingStart.call(data); - } - }); - - bridge.profilingStop.subscribe(async () => { - if (profiler === undefined) { - throw new Error('Expected profiler to be running'); - } - const masterProfile = await profiler.stopProfiling(); - profiler = undefined; - this.profiling = undefined; - return masterProfile; - }); - - bridge.profilingGetWorkers.subscribe(async () => { - const ids: Array = []; - for (const {id} of this.workerManager.getExternalWorkers()) { - ids.push(id); - } - return ids; - }); - - bridge.profilingStopWorker.subscribe(async (id) => { - const worker = this.workerManager.getWorkerAssert(id); - return await worker.bridge.profilingStop.call(); - }); - - // When enableWorkerLogs is called we setup subscriptions to the worker logs - // Logs are never transported from workers to the master unless there is a subscription - let subscribedWorkers = false; - bridge.enableWorkerLogs.subscribe(() => { - // enableWorkerLogs could be called twice in the case of `--logs --rage`. We'll only want to setup the subscriptions once - if (subscribedWorkers) { - return; - } else { - subscribedWorkers = true; - } - - function onLog(chunk: string) { - bridge.log.call({origin: 'worker', chunk}); - } - - // Add on existing workers if there are any - for (const worker of this.workerManager.getWorkers()) { - bridge.attachEndSubscriptionRemoval(worker.bridge.log.subscribe(onLog)); - } - - // Listen for logs for any workers that start later - this.workerManager.workerStartEvent.subscribe((worker) => { - bridge.attachEndSubscriptionRemoval(worker.log.subscribe(onLog)); - }); - }); - - await bridge.handshake(); - - const client = await this.createClient(bridge); - - if (client.version !== VERSION) { - // TODO this wont ever actually be printed? - client.reporter.error( - `Client version ${client.version} does not match server version ${VERSION}. Goodbye lol.`, - ); - client.bridge.end(); - return; - } - - bridge.query.subscribe(async (request) => { - return await this.handleRequest(client, request); - }); - - bridge.cancelQuery.subscribe(async (token) => { - for (const req of client.requestsInFlight) { - if (req.query.cancelToken === token) { - req.cancel(); - } - } - }); - - await this.clientStartEvent.callOptional(client); - } - - async createClient(bridge: MasterBridge): Promise { - const { - flags: rawFlags, - useRemoteReporter, - hasClearScreen, - columns, - unicode, - format, - version, - } = await bridge.getClientInfo.call(); - - // Turn the cwd back into a AbsoluteFilePath - const flags: ClientFlags = { - ...rawFlags, - cwd: createAbsoluteFilePath(rawFlags.cwd), - }; - - const outStream: ReporterStream = { - type: 'out', - columns, - format, - unicode, - write(chunk: string) { - if (flags.silent === true) { - return; - } - - // Split up stdout chunks - if (chunk.length < STDOUT_MAX_CHUNK_LENGTH) { - bridge.stdout.send(chunk); - } else { - while (chunk.length > 0) { - const subChunk = chunk.slice(0, STDOUT_MAX_CHUNK_LENGTH); - chunk = chunk.slice(STDOUT_MAX_CHUNK_LENGTH); - bridge.stdout.send(subChunk); - } - } - }, - }; - - const errStream: ReporterStream = { - ...outStream, - type: 'error', - write(chunk: string) { - bridge.stderr.send(chunk); - }, - }; - - bridge.setColumns.subscribe((columns) => { - reporter.setStreamColumns([outStream, errStream], columns); - }); - - // Initialize the reporter - const reporter = new Reporter({ - hasClearScreen, - wrapperFactory: this.wrapFatal.bind(this), - streams: [outStream, errStream], - verbose: flags.verbose, - markupOptions: { - cwd: flags.cwd, - ...this.logger.markupOptions, - }, - useRemoteProgressBars: useRemoteReporter, - }); - - reporter.sendRemoteClientMessage.subscribe((msg) => { - bridge.reporterRemoteServerMessage.send(msg); - }); - - bridge.reporterRemoteClientMessage.subscribe((msg) => { - reporter.receivedRemoteServerMessage(msg); - }); - - // Add reporter to connected set, important logs may be output to these - if (!flags.silent) { - this.connectedReporters.addStream(outStream); - } - this.connectedReporters.addStream(errStream); - - const client: MasterClient = { - id: this.clientIdCounter++, - bridge, - reporter, - flags, - version, - requestsInFlight: new Set(), - }; - - this.connectedClients.add(client); - - bridge.updatedListenersEvent.subscribe((listeners) => { - if (listeners.has('log')) { - this.connectedClientsListeningForLogs.add(client); - } else { - this.connectedClientsListeningForLogs.delete(client); - } - }); - - bridge.endEvent.subscribe(() => { - // Cancel any requests still in flight - for (const req of client.requestsInFlight) { - req.cancel(); - } - - this.connectedClients.delete(client); - this.connectedClientsListeningForLogs.delete(client); - this.connectedReporters.removeStream(errStream); - this.connectedReporters.removeStream(outStream); - }); - - return client; - } - - async handleFileDelete(path: AbsoluteFilePath) { - this.logger.info(`[Master] File delete:`, path.join()); - this.fileChangeEvent.send(path); - } - - async handleFileChange(path: AbsoluteFilePath) { - this.logger.info(`[Master] File change:`, path.join()); - this.fileChangeEvent.send(path); - } - - async handleRequestStart(req: MasterRequest) { - this.logger.info(`[Master] Handling CLI request:`, req.query); - - // Hook used by the web server to track and collect master requests - await this.requestStartEvent.callOptional(req); - - // Track the amount of running queries for terminateWhenIdle - this.requestRunningCounter++; - - // Sometimes we'll want to terminate the process once all queries have finished - if (req.query.terminateWhenIdle) { - this.terminateWhenIdle = true; - } - } - - handleRequestEnd(req: MasterRequest) { - this.requestRunningCounter--; - this.logger.info(`[Master] Replying to CLI request:`, req.query); - - // If we're waiting to terminate ourselves when idle, then do so when there's no running requests - if (this.terminateWhenIdle && this.requestRunningCounter === 0) { - this.end(); - } - } - - async handleRequest( - client: MasterClient, - partialQuery: PartialMasterQueryRequest, - ): Promise { - const requestFlags: ClientRequestFlags = { - ...DEFAULT_CLIENT_REQUEST_FLAGS, - ...partialQuery.requestFlags, - }; - - const query: MasterQueryRequest = { - commandName: partialQuery.commandName, - args: partialQuery.args === undefined ? [] : partialQuery.args, - noData: partialQuery.noData === true, - requestFlags, - silent: partialQuery.silent === true || requestFlags.benchmark, - terminateWhenIdle: partialQuery.terminateWhenIdle === true, - commandFlags: partialQuery.commandFlags === undefined - ? {} - : partialQuery.commandFlags, - cancelToken: partialQuery.cancelToken, - }; - - const {bridge} = client; - - // Create a promise for the client dying so we can race it later - let bridgeEndEvent: undefined | EventSubscription; - const bridgeEndPromise: Promise = new Promise((resolve, reject) => { - bridgeEndEvent = bridge.endEvent.subscribe((err) => { - reject(err); - }); - }); - if (bridgeEndEvent === undefined) { - throw new Error('Expected bridgeEndEvent to have been initialized'); - } - - // Support a silent option on requests so they don't write output - let reporter = client.reporter; - if (query.silent) { - reporter = reporter.fork({ - streams: [], - }); - } - - const req = new MasterRequest({ - client, - query, - master: this, - }); - - await req.init(); - - try { - let res: undefined | MasterQueryResponse = await this.dispatchRequest( - req, - bridgeEndPromise, - [], - ); - - res = req.teardown(res); - - if (res === undefined) { - throw new Error( - 'teardown should have returned a normalized MasterQueryResponse', - ); - } - - return res; - } catch (err) { - req.teardown(undefined); - throw err; - } finally { - // We no longer care if the client dies - bridgeEndEvent.unsubscribe(); - } - } - - async dispatchBenchmarkRequest( - req: MasterRequest, - bridgeEndPromise: Promise, - ): Promise { - const {client} = req; - const {reporter} = client; - const {benchmarkIterations} = req.query.requestFlags; - - // Warmup - const warmupStart = Date.now(); - const result = await this.dispatchRequest( - req, - bridgeEndPromise, - ['benchmark'], - ); - const warmupTook = Date.now() - warmupStart; - - // Benchmark - const progress = client.reporter.progress({title: 'Running benchmark'}); - progress.setTotal(benchmarkIterations); - const benchmarkStart = Date.now(); - for (let i = 0; i < benchmarkIterations; i++) { - await this.dispatchRequest(req, bridgeEndPromise, ['benchmark']); - progress.tick(); - } - progress.end(); - const benchmarkTook = Date.now() - benchmarkStart; - - reporter.section( - 'Benchmark results', - () => { - reporter.info( - 'Request artifacts may have been cached after the first run, artificially decreasing subsequent run time', - ); - reporter.heading('Query'); - reporter.inspect(req.query); - reporter.heading('Stats'); - reporter.list([ - `Warmup took ${warmupTook}`, - `${benchmarkIterations} runs`, - `${benchmarkTook} total`, - `${benchmarkTook / benchmarkIterations} per run`, - ]); - }, - ); - - return result; - } - - async dispatchRequest( - req: MasterRequest, - bridgeEndPromise: Promise, - origins: Array, - ): Promise { - const {query, reporter, bridge} = req; - const {requestFlags} = query; - - if (requestFlags.benchmark && !origins.includes('benchmark')) { - return this.dispatchBenchmarkRequest(req, bridgeEndPromise); - } - - try { - const defaultCommandFlags: Dict = {}; - - // A type-safe wrapper for retrieving command flags - // TODO perhaps present this as JSON or something if this isn't a request from the CLI? - const flagsConsumer = consume({ - filePath: createUnknownFilePath('argv'), - parent: undefined, - value: query.commandFlags, - onDefinition(def) { - // objectPath should only have a depth of 1 - defaultCommandFlags[def.objectPath[0]] = def.default; - }, - objectPath: [], - context: { - category: 'flags/invalid', - getOriginalValue: () => { - return undefined; - }, - normalizeKey: (key) => { - return toKebabCase(key); - }, - getDiagnosticPointer: (keys: ConsumePath) => { - return req.getDiagnosticPointerFromFlags({ - type: 'flag', - key: String(keys[0]), - target: 'value', - }); - }, - }, - }); - - // An array of promises that we'll race, the only promise that will ever resolve will be the command one - let promises: Array | undefined> = [bridgeEndPromise]; - - // Get command - const masterCommand: undefined | MasterCommand> = masterCommands.get( - query.commandName, - ); - if (masterCommand) { - // Warn about disabled disk caching - if ( - process.env.ROME_CACHE === '0' && - !this.warnedCacheClients.has(bridge) - ) { - reporter.warn( - 'Disk caching has been disabled due to the ROME_CACHE=0 environment variable', - ); - this.warnedCacheClients.add(bridge); - } - - await validateRequestFlags(req, masterCommand); - - let commandFlags; - if (masterCommand.defineFlags !== undefined) { - commandFlags = masterCommand.defineFlags(flagsConsumer); - } - - req.setNormalizedCommandFlags({ - flags: commandFlags, - defaultFlags: defaultCommandFlags, - }); - - // @ts-ignore - const commandPromise = masterCommand.callback(req, commandFlags); - promises.push(commandPromise); - - await Promise.race(promises); - - // Only the command promise should have won the race with a resolve - const data = await commandPromise; - return { - ...EMPTY_SUCCESS_RESPONSE, - hasData: data !== undefined, - data, - }; - } else { - throw new Error(`Unknown command ${String(query.commandName)}`); - } - } catch (err) { - let diagnostics: undefined | Diagnostics = this.handleRequestError( - req, - err, - ); - - if (diagnostics === undefined) { - return { - type: 'ERROR', - fatal: false, - handled: true, - name: err.name, - message: err.message, - stack: err.stack, - }; - } else if (err instanceof MasterRequestCancelled) { - return { - type: 'CANCELLED', - }; - } else if (err instanceof MasterRequestInvalid) { - return { - type: 'INVALID_REQUEST', - diagnostics, - showHelp: err.showHelp, - }; - } else { - return { - type: 'DIAGNOSTICS', - diagnostics, - }; - } - } - } - - handleRequestError(req: MasterRequest, rawErr: Error): undefined | Diagnostics { - let err = rawErr; - - // If we can derive diagnostics from the error then create a diagnostics printer - const diagnostics = getDiagnosticsFromError(err); - if (diagnostics !== undefined) { - const printer = req.createDiagnosticsPrinter( - req.createDiagnosticsProcessor({ - origins: [ - { - category: 'internal', - message: 'Derived diagnostics from thrown error', - }, - ], - }), - ); - printer.processor.addDiagnostics(diagnostics); - err = printer; - } - - // Print it! - if (err instanceof DiagnosticsPrinter) { - const printer = err; - - // Only print when the bridge is alive and we aren't in review mode - // When we're in review mode we don't expect to show any diagnostics because they'll be intercepted in the client command - // We will always print invalid request errors - let shouldPrint = true; - if (req.query.requestFlags.review) { - shouldPrint = false; - } - if (rawErr instanceof MasterRequestInvalid) { - shouldPrint = true; - } - if (!req.bridge.alive) { - shouldPrint = false; - } - - if (shouldPrint) { - printer.print(); - - // Don't output the footer if this is a notifier for an invalid request as it will be followed by a help screen - if (!(rawErr instanceof MasterRequestInvalid)) { - printer.footer(); - } - } - - return printer.getDiagnostics(); - } - - if (!req.bridge.alive) { - return undefined; - } - - const printer = req.createDiagnosticsPrinter( - req.createDiagnosticsProcessor({ - origins: [ - { - category: 'internal', - message: 'Error captured and converted into a diagnostic', - }, - ], - }), - ); - const errorDiag = deriveDiagnosticFromError( - err, - { - description: { - category: 'internalError/request', - }, - }, - ); - printer.processor.addDiagnostic({ - ...errorDiag, - description: { - ...errorDiag.description, - advice: [...errorDiag.description.advice, INTERNAL_ERROR_LOG_ADVICE], - }, - }); - printer.print(); - - // We could probably return printer.getDiagnostics() but we just want to print to the console - - // We will still want to send the `error` property - return undefined; - } + constructor(opts: MasterOptions) { + this.onFatalErrorBound = this.onFatalError.bind(this); + + this.profiling = undefined; + this.options = opts; + + this.userConfig = loadUserConfig(); + + this.fileChangeEvent = new Event({ + name: 'Master.fileChange', + onError: this.onFatalErrorBound, + }); + + this.clientStartEvent = new Event({ + name: 'Master.clientStart', + onError: this.onFatalErrorBound, + }); + + this.requestStartEvent = new Event({ + name: 'Master.requestStart', + onError: this.onFatalErrorBound, + }); + + this.logEvent = new Event({ + name: 'Master.log', + onError: this.onFatalErrorBound, + }); + + this.endEvent = new Event({ + name: 'Master.end', + onError: this.onFatalErrorBound, + serial: true, + }); + + this.logger = new Logger( + 'master', + () => { + return ( + this.logEvent.hasSubscribers() || + this.connectedClientsListeningForLogs.size > 0 + ); + }, + { + streams: [ + { + type: 'all', + format: 'none', + columns: 0, + unicode: true, + write: (chunk) => { + this.emitMasterLog(chunk); + }, + }, + ], + markupOptions: { + humanizeFilename: (filename) => { + const path = createUnknownFilePath(filename); + if (path.isAbsolute()) { + const remote = this.projectManager.getRemoteFromLocalPath( + path.assertAbsolute(), + ); + if (remote !== undefined) { + return remote.join(); + } + } + return undefined; + }, + normalizeFilename: (filename: string): string => { + const path = this.projectManager.getFilePathFromUid(filename); + if (path === undefined) { + return filename; + } else { + return path.join(); + } + }, + }, + }, + ); + + this.connectedReporters = new MasterReporter(this); + + this.connectedClientsListeningForLogs = new Set(); + this.connectedLSPServers = new Set(); + this.connectedClients = new Set(); + + this.virtualModules = new VirtualModules(this); + this.memoryFs = new MemoryFileSystem(this); + this.projectManager = new ProjectManager(this); + this.workerManager = new WorkerManager(this); + this.fileAllocator = new FileAllocator(this); + this.resolver = new Resolver(this); + this.cache = new Cache(this); + + this.memoryFs.deletedFileEvent.subscribe((path) => { + return this.handleFileDelete(path); + }); + + this.memoryFs.changedFileEvent.subscribe(({path}) => { + return this.handleFileChange(path); + }); + + this.warnedCacheClients = new WeakSet(); + + this.clientIdCounter = 0; + + this.requestRunningCounter = 0; + this.terminateWhenIdle = false; + } + + userConfig: UserConfig; + + requestStartEvent: Event; + clientStartEvent: Event; + fileChangeEvent: Event; + logEvent: Event; + endEvent: Event; + + onFatalErrorBound: (err: Error) => void; + + requestRunningCounter: number; + terminateWhenIdle: boolean; + + clientIdCounter: number; + + profiling: undefined | ProfilingStartData; + options: MasterOptions; + + warnedCacheClients: WeakSet; + memoryFs: MemoryFileSystem; + virtualModules: VirtualModules; + resolver: Resolver; + projectManager: ProjectManager; + workerManager: WorkerManager; + fileAllocator: FileAllocator; + cache: Cache; + connectedReporters: MasterReporter; + logger: Logger; + + connectedClients: Set; + connectedLSPServers: Set; + connectedClientsListeningForLogs: Set; + + emitMasterLog(chunk: string) { + this.logEvent.send(chunk); + + for (const {bridge} of this.connectedClientsListeningForLogs) { + bridge.log.send({chunk, origin: 'master'}); + } + } + + onFatalError(err: Error) { + const message = `Fatal error occurred: ${escapeMarkup( + err.stack || err.message, + )}`; + this.logger.error(message); + this.connectedReporters.error(message); + process.exit(); + } + + // rome-ignore lint/noExplicitAny + wrapFatal) => any>(callback: T): T { + return (((...args: Array): any => { + try { + const res = callback(...args); + if (res instanceof Promise) { + res.catch(this.onFatalErrorBound); + } + return res; + } catch (err) { + throw this.onFatalError(err); + } + }) as T); + } + + async handleDisconnectedDiagnostics(diagnostics: Diagnostics) { + this.connectedReporters.error( + 'Generated diagnostics without a current request', + ); + + printDiagnostics({ + diagnostics, + suppressions: [], + printerOptions: { + processor: this.createDiagnosticsProcessor(), + reporter: this.connectedReporters, + readFile: this.readDiagnosticsPrinterFile.bind(this), + }, + }); + } + + readDiagnosticsPrinterFile( + path: AbsoluteFilePath, + ): ReturnType { + const remoteToLocal = this.projectManager.remoteToLocalPath.get(path); + + if (remoteToLocal === undefined) { + return readDiagnosticsFileLocal(path); + } else { + return readDiagnosticsFileLocal(remoteToLocal); + } + } + + createDiagnosticsProcessor( + opts: DiagnosticsProcessorOptions = {}, + ): DiagnosticsProcessor { + return new DiagnosticsProcessor({ + markupOptions: this.logger.markupOptions, + ...opts, + }); + } + + createDisconnectedDiagnosticsProcessor( + origins: Array, + ): DiagnosticsProcessor { + return this.createDiagnosticsProcessor({ + onDiagnostics: (diagnostics: Diagnostics) => { + this.handleDisconnectedDiagnostics(diagnostics); + }, + origins: [ + ...origins, + { + category: 'master', + message: 'Created disconnected diagnostics collector', + }, + ], + }); + } + + maybeSetupGlobalErrorHandlers() { + if (!this.options.globalErrorHandlers) { + return; + } + + const teardown = setupGlobalErrorHandlers((err) => { + this.onFatalError(err); + }); + + this.endEvent.subscribe(() => { + teardown(); + }); + } + + async init() { + this.maybeSetupGlobalErrorHandlers(); + this.memoryFs.init(); + await this.projectManager.init(); + this.fileAllocator.init(); + this.resolver.init(); + await this.cache.init(); + await this.virtualModules.init(); + await this.workerManager.init(); + } + + async end() { + // Cancel all queries in flight + for (const client of this.connectedClients) { + for (const req of client.requestsInFlight) { + req.cancel(); + } + } + + // We should remove everything that has an external dependency like a socket or process + await this.endEvent.callOptional(); + this.workerManager.end(); + this.memoryFs.unwatchAll(); + } + + async attachToBridge(bridge: MasterBridge) { + let profiler: undefined | Profiler; + + // If we aren't a dedicated process then we should only expect a single connection + // and when that ends. End the Master. + if (this.options.dedicated === false) { + bridge.endEvent.subscribe(() => { + this.end(); + }); + } + + bridge.profilingStart.subscribe(async (data) => { + if (profiler !== undefined) { + throw new Error('Expected no profiler to be running'); + } + profiler = new Profiler(); + await profiler.startProfiling(data.samplingInterval); + this.profiling = data; + for (const {bridge} of this.workerManager.getExternalWorkers()) { + await bridge.profilingStart.call(data); + } + }); + + bridge.profilingStop.subscribe(async () => { + if (profiler === undefined) { + throw new Error('Expected profiler to be running'); + } + const masterProfile = await profiler.stopProfiling(); + profiler = undefined; + this.profiling = undefined; + return masterProfile; + }); + + bridge.profilingGetWorkers.subscribe(async () => { + const ids: Array = []; + for (const {id} of this.workerManager.getExternalWorkers()) { + ids.push(id); + } + return ids; + }); + + bridge.profilingStopWorker.subscribe(async (id) => { + const worker = this.workerManager.getWorkerAssert(id); + return await worker.bridge.profilingStop.call(); + }); + + // When enableWorkerLogs is called we setup subscriptions to the worker logs + // Logs are never transported from workers to the master unless there is a subscription + let subscribedWorkers = false; + bridge.enableWorkerLogs.subscribe(() => { + // enableWorkerLogs could be called twice in the case of `--logs --rage`. We'll only want to setup the subscriptions once + if (subscribedWorkers) { + return; + } else { + subscribedWorkers = true; + } + + function onLog(chunk: string) { + bridge.log.call({origin: 'worker', chunk}); + } + + // Add on existing workers if there are any + for (const worker of this.workerManager.getWorkers()) { + bridge.attachEndSubscriptionRemoval(worker.bridge.log.subscribe(onLog)); + } + + // Listen for logs for any workers that start later + this.workerManager.workerStartEvent.subscribe((worker) => { + bridge.attachEndSubscriptionRemoval(worker.log.subscribe(onLog)); + }); + }); + + await bridge.handshake(); + + const client = await this.createClient(bridge); + + if (client.version !== VERSION) { + // TODO this wont ever actually be printed? + client.reporter.error( + `Client version ${client.version} does not match server version ${VERSION}. Goodbye lol.`, + ); + client.bridge.end(); + return; + } + + bridge.query.subscribe(async (request) => { + return await this.handleRequest(client, request); + }); + + bridge.cancelQuery.subscribe(async (token) => { + for (const req of client.requestsInFlight) { + if (req.query.cancelToken === token) { + req.cancel(); + } + } + }); + + await this.clientStartEvent.callOptional(client); + } + + async createClient(bridge: MasterBridge): Promise { + const { + flags: rawFlags, + useRemoteReporter, + hasClearScreen, + columns, + unicode, + format, + version, + } = await bridge.getClientInfo.call(); + + // Turn the cwd back into a AbsoluteFilePath + const flags: ClientFlags = { + ...rawFlags, + cwd: createAbsoluteFilePath(rawFlags.cwd), + }; + + const outStream: ReporterStream = { + type: 'out', + columns, + format, + unicode, + write(chunk: string) { + if (flags.silent === true) { + return; + } + + // Split up stdout chunks + if (chunk.length < STDOUT_MAX_CHUNK_LENGTH) { + bridge.stdout.send(chunk); + } else { + while (chunk.length > 0) { + const subChunk = chunk.slice(0, STDOUT_MAX_CHUNK_LENGTH); + chunk = chunk.slice(STDOUT_MAX_CHUNK_LENGTH); + bridge.stdout.send(subChunk); + } + } + }, + }; + + const errStream: ReporterStream = { + ...outStream, + type: 'error', + write(chunk: string) { + bridge.stderr.send(chunk); + }, + }; + + bridge.setColumns.subscribe((columns) => { + reporter.setStreamColumns([outStream, errStream], columns); + }); + + // Initialize the reporter + const reporter = new Reporter({ + hasClearScreen, + wrapperFactory: this.wrapFatal.bind(this), + streams: [outStream, errStream], + verbose: flags.verbose, + markupOptions: { + cwd: flags.cwd, + ...this.logger.markupOptions, + }, + useRemoteProgressBars: useRemoteReporter, + }); + + reporter.sendRemoteClientMessage.subscribe((msg) => { + bridge.reporterRemoteServerMessage.send(msg); + }); + + bridge.reporterRemoteClientMessage.subscribe((msg) => { + reporter.receivedRemoteServerMessage(msg); + }); + + // Add reporter to connected set, important logs may be output to these + if (!flags.silent) { + this.connectedReporters.addStream(outStream); + } + this.connectedReporters.addStream(errStream); + + const client: MasterClient = { + id: this.clientIdCounter++, + bridge, + reporter, + flags, + version, + requestsInFlight: new Set(), + }; + + this.connectedClients.add(client); + + bridge.updatedListenersEvent.subscribe((listeners) => { + if (listeners.has('log')) { + this.connectedClientsListeningForLogs.add(client); + } else { + this.connectedClientsListeningForLogs.delete(client); + } + }); + + bridge.endEvent.subscribe(() => { + // Cancel any requests still in flight + for (const req of client.requestsInFlight) { + req.cancel(); + } + + this.connectedClients.delete(client); + this.connectedClientsListeningForLogs.delete(client); + this.connectedReporters.removeStream(errStream); + this.connectedReporters.removeStream(outStream); + }); + + return client; + } + + async handleFileDelete(path: AbsoluteFilePath) { + this.logger.info(`[Master] File delete:`, path.join()); + this.fileChangeEvent.send(path); + } + + async handleFileChange(path: AbsoluteFilePath) { + this.logger.info(`[Master] File change:`, path.join()); + this.fileChangeEvent.send(path); + } + + async handleRequestStart(req: MasterRequest) { + this.logger.info(`[Master] Handling CLI request:`, req.query); + + // Hook used by the web server to track and collect master requests + await this.requestStartEvent.callOptional(req); + + // Track the amount of running queries for terminateWhenIdle + this.requestRunningCounter++; + + // Sometimes we'll want to terminate the process once all queries have finished + if (req.query.terminateWhenIdle) { + this.terminateWhenIdle = true; + } + } + + handleRequestEnd(req: MasterRequest) { + this.requestRunningCounter--; + this.logger.info(`[Master] Replying to CLI request:`, req.query); + + // If we're waiting to terminate ourselves when idle, then do so when there's no running requests + if (this.terminateWhenIdle && this.requestRunningCounter === 0) { + this.end(); + } + } + + async handleRequest( + client: MasterClient, + partialQuery: PartialMasterQueryRequest, + ): Promise { + const requestFlags: ClientRequestFlags = { + ...DEFAULT_CLIENT_REQUEST_FLAGS, + ...partialQuery.requestFlags, + }; + + const query: MasterQueryRequest = { + commandName: partialQuery.commandName, + args: partialQuery.args === undefined ? [] : partialQuery.args, + noData: partialQuery.noData === true, + requestFlags, + silent: partialQuery.silent === true || requestFlags.benchmark, + terminateWhenIdle: partialQuery.terminateWhenIdle === true, + commandFlags: partialQuery.commandFlags === undefined + ? {} + : partialQuery.commandFlags, + cancelToken: partialQuery.cancelToken, + }; + + const {bridge} = client; + + // Create a promise for the client dying so we can race it later + let bridgeEndEvent: undefined | EventSubscription; + const bridgeEndPromise: Promise = new Promise((resolve, reject) => { + bridgeEndEvent = bridge.endEvent.subscribe((err) => { + reject(err); + }); + }); + if (bridgeEndEvent === undefined) { + throw new Error('Expected bridgeEndEvent to have been initialized'); + } + + // Support a silent option on requests so they don't write output + let reporter = client.reporter; + if (query.silent) { + reporter = reporter.fork({ + streams: [], + }); + } + + const req = new MasterRequest({ + client, + query, + master: this, + }); + + await req.init(); + + try { + let res: undefined | MasterQueryResponse = await this.dispatchRequest( + req, + bridgeEndPromise, + [], + ); + + res = req.teardown(res); + + if (res === undefined) { + throw new Error( + 'teardown should have returned a normalized MasterQueryResponse', + ); + } + + return res; + } catch (err) { + req.teardown(undefined); + throw err; + } finally { + // We no longer care if the client dies + bridgeEndEvent.unsubscribe(); + } + } + + async dispatchBenchmarkRequest( + req: MasterRequest, + bridgeEndPromise: Promise, + ): Promise { + const {client} = req; + const {reporter} = client; + const {benchmarkIterations} = req.query.requestFlags; + + // Warmup + const warmupStart = Date.now(); + const result = await this.dispatchRequest( + req, + bridgeEndPromise, + ['benchmark'], + ); + const warmupTook = Date.now() - warmupStart; + + // Benchmark + const progress = client.reporter.progress({title: 'Running benchmark'}); + progress.setTotal(benchmarkIterations); + const benchmarkStart = Date.now(); + for (let i = 0; i < benchmarkIterations; i++) { + await this.dispatchRequest(req, bridgeEndPromise, ['benchmark']); + progress.tick(); + } + progress.end(); + const benchmarkTook = Date.now() - benchmarkStart; + + reporter.section( + 'Benchmark results', + () => { + reporter.info( + 'Request artifacts may have been cached after the first run, artificially decreasing subsequent run time', + ); + reporter.heading('Query'); + reporter.inspect(req.query); + reporter.heading('Stats'); + reporter.list([ + `Warmup took ${warmupTook}`, + `${benchmarkIterations} runs`, + `${benchmarkTook} total`, + `${benchmarkTook / benchmarkIterations} per run`, + ]); + }, + ); + + return result; + } + + async dispatchRequest( + req: MasterRequest, + bridgeEndPromise: Promise, + origins: Array, + ): Promise { + const {query, reporter, bridge} = req; + const {requestFlags} = query; + + if (requestFlags.benchmark && !origins.includes('benchmark')) { + return this.dispatchBenchmarkRequest(req, bridgeEndPromise); + } + + try { + const defaultCommandFlags: Dict = {}; + + // A type-safe wrapper for retrieving command flags + // TODO perhaps present this as JSON or something if this isn't a request from the CLI? + const flagsConsumer = consume({ + filePath: createUnknownFilePath('argv'), + parent: undefined, + value: query.commandFlags, + onDefinition(def) { + // objectPath should only have a depth of 1 + defaultCommandFlags[def.objectPath[0]] = def.default; + }, + objectPath: [], + context: { + category: 'flags/invalid', + getOriginalValue: () => { + return undefined; + }, + normalizeKey: (key) => { + return toKebabCase(key); + }, + getDiagnosticPointer: (keys: ConsumePath) => { + return req.getDiagnosticPointerFromFlags({ + type: 'flag', + key: String(keys[0]), + target: 'value', + }); + }, + }, + }); + + // An array of promises that we'll race, the only promise that will ever resolve will be the command one + let promises: Array | undefined> = [bridgeEndPromise]; + + // Get command + const masterCommand: undefined | MasterCommand> = masterCommands.get( + query.commandName, + ); + if (masterCommand) { + // Warn about disabled disk caching + if (process.env.ROME_CACHE === '0' && !this.warnedCacheClients.has(bridge)) { + reporter.warn( + 'Disk caching has been disabled due to the ROME_CACHE=0 environment variable', + ); + this.warnedCacheClients.add(bridge); + } + + await validateRequestFlags(req, masterCommand); + + let commandFlags; + if (masterCommand.defineFlags !== undefined) { + commandFlags = masterCommand.defineFlags(flagsConsumer); + } + + req.setNormalizedCommandFlags({ + flags: commandFlags, + defaultFlags: defaultCommandFlags, + }); + + // @ts-ignore + const commandPromise = masterCommand.callback(req, commandFlags); + promises.push(commandPromise); + + await Promise.race(promises); + + // Only the command promise should have won the race with a resolve + const data = await commandPromise; + return { + ...EMPTY_SUCCESS_RESPONSE, + hasData: data !== undefined, + data, + }; + } else { + throw new Error(`Unknown command ${String(query.commandName)}`); + } + } catch (err) { + let diagnostics: undefined | Diagnostics = this.handleRequestError(req, err); + + if (diagnostics === undefined) { + return { + type: 'ERROR', + fatal: false, + handled: true, + name: err.name, + message: err.message, + stack: err.stack, + }; + } else if (err instanceof MasterRequestCancelled) { + return { + type: 'CANCELLED', + }; + } else if (err instanceof MasterRequestInvalid) { + return { + type: 'INVALID_REQUEST', + diagnostics, + showHelp: err.showHelp, + }; + } else { + return { + type: 'DIAGNOSTICS', + diagnostics, + }; + } + } + } + + handleRequestError(req: MasterRequest, rawErr: Error): undefined | Diagnostics { + let err = rawErr; + + // If we can derive diagnostics from the error then create a diagnostics printer + const diagnostics = getDiagnosticsFromError(err); + if (diagnostics !== undefined) { + const printer = req.createDiagnosticsPrinter( + req.createDiagnosticsProcessor({ + origins: [ + { + category: 'internal', + message: 'Derived diagnostics from thrown error', + }, + ], + }), + ); + printer.processor.addDiagnostics(diagnostics); + err = printer; + } + + // Print it! + if (err instanceof DiagnosticsPrinter) { + const printer = err; + + // Only print when the bridge is alive and we aren't in review mode + // When we're in review mode we don't expect to show any diagnostics because they'll be intercepted in the client command + // We will always print invalid request errors + let shouldPrint = true; + if (req.query.requestFlags.review) { + shouldPrint = false; + } + if (rawErr instanceof MasterRequestInvalid) { + shouldPrint = true; + } + if (!req.bridge.alive) { + shouldPrint = false; + } + + if (shouldPrint) { + printer.print(); + + // Don't output the footer if this is a notifier for an invalid request as it will be followed by a help screen + if (!(rawErr instanceof MasterRequestInvalid)) { + printer.footer(); + } + } + + return printer.getDiagnostics(); + } + + if (!req.bridge.alive) { + return undefined; + } + + const printer = req.createDiagnosticsPrinter( + req.createDiagnosticsProcessor({ + origins: [ + { + category: 'internal', + message: 'Error captured and converted into a diagnostic', + }, + ], + }), + ); + const errorDiag = deriveDiagnosticFromError( + err, + { + description: { + category: 'internalError/request', + }, + }, + ); + printer.processor.addDiagnostic({ + ...errorDiag, + description: { + ...errorDiag.description, + advice: [...errorDiag.description.advice, INTERNAL_ERROR_LOG_ADVICE], + }, + }); + printer.print(); + + // We could probably return printer.getDiagnostics() but we just want to print to the console + + // We will still want to send the `error` property + return undefined; + } } diff --git a/packages/@romejs/core/master/MasterReporter.ts b/packages/@romejs/core/master/MasterReporter.ts index d71c1e8f94a..955db4b191a 100644 --- a/packages/@romejs/core/master/MasterReporter.ts +++ b/packages/@romejs/core/master/MasterReporter.ts @@ -6,31 +6,31 @@ */ import { - Reporter, - ReporterProgress, - ReporterProgressOptions, - mergeProgresses, + Reporter, + ReporterProgress, + ReporterProgressOptions, + mergeProgresses, } from '@romejs/cli-reporter'; import Master from './Master'; export default class MasterReporter extends Reporter { - constructor(master: Master) { - super({ - wrapperFactory: master.wrapFatal.bind(master), - }); - this.master = master; - } + constructor(master: Master) { + super({ + wrapperFactory: master.wrapFatal.bind(master), + }); + this.master = master; + } - master: Master; + master: Master; - // This is so all progress bars are also shown on an LSP client, alongside connected CLIs - progress(opts?: ReporterProgressOptions): ReporterProgress { - const progresses: Array = [this.progressLocal(opts)]; + // This is so all progress bars are also shown on an LSP client, alongside connected CLIs + progress(opts?: ReporterProgressOptions): ReporterProgress { + const progresses: Array = [this.progressLocal(opts)]; - for (const server of this.master.connectedLSPServers) { - progresses.push(server.createProgress(opts)); - } + for (const server of this.master.connectedLSPServers) { + progresses.push(server.createProgress(opts)); + } - return mergeProgresses(progresses); - } + return mergeProgresses(progresses); + } } diff --git a/packages/@romejs/core/master/MasterRequest.ts b/packages/@romejs/core/master/MasterRequest.ts index 38370abaa76..d7958e5d092 100644 --- a/packages/@romejs/core/master/MasterRequest.ts +++ b/packages/@romejs/core/master/MasterRequest.ts @@ -6,74 +6,74 @@ */ import { - DEFAULT_CLIENT_FLAGS, - DEFAULT_CLIENT_REQUEST_FLAGS, + DEFAULT_CLIENT_FLAGS, + DEFAULT_CLIENT_REQUEST_FLAGS, } from '../common/types/client'; import {JSONFileReference} from '../common/types/files'; import { - Diagnostic, - DiagnosticAdvice, - DiagnosticCategory, - DiagnosticDescription, - DiagnosticLocation, - Diagnostics, - DiagnosticsError, - DiagnosticsProcessor, - createSingleDiagnosticError, - deriveDiagnosticFromError, - descriptions, - getDiagnosticsFromError, + Diagnostic, + DiagnosticAdvice, + DiagnosticCategory, + DiagnosticDescription, + DiagnosticLocation, + Diagnostics, + DiagnosticsError, + DiagnosticsProcessor, + createSingleDiagnosticError, + deriveDiagnosticFromError, + descriptions, + getDiagnosticsFromError, } from '@romejs/diagnostics'; import { - DiagnosticsPrinter, - DiagnosticsPrinterFlags, + DiagnosticsPrinter, + DiagnosticsPrinterFlags, } from '@romejs/cli-diagnostics'; import { - ProjectConfigCategoriesWithIgnore, - ProjectDefinition, + ProjectConfigCategoriesWithIgnore, + ProjectDefinition, } from '@romejs/project'; import {ResolverOptions, ResolverQueryResponseFound} from './fs/Resolver'; import {BundlerConfig} from '../common/types/bundler'; import MasterBridge, { - MasterQueryRequest, - MasterQueryResponse, - MasterQueryResponseSuccess, + MasterQueryRequest, + MasterQueryResponse, + MasterQueryResponseSuccess, } from '../common/bridges/MasterBridge'; import Master, { - MasterClient, - MasterMarker, - MasterUnfinishedMarker, + MasterClient, + MasterMarker, + MasterUnfinishedMarker, } from './Master'; import {Reporter} from '@romejs/cli-reporter'; import { - Event, - EventSubscription, - mergeEventSubscriptions, + Event, + EventSubscription, + mergeEventSubscriptions, } from '@romejs/events'; import { - FlagValue, - SerializeCLITarget, - serializeCLIFlags, + FlagValue, + SerializeCLITarget, + serializeCLIFlags, } from '@romejs/cli-flags'; import {Program} from '@romejs/js-ast'; import {TransformStageName} from '@romejs/js-compiler'; import WorkerBridge, { - PrefetchedModuleSignatures, - WorkerAnalyzeDependencyResult, - WorkerCompileResult, - WorkerCompilerOptions, - WorkerFormatResult, - WorkerLintOptions, - WorkerLintResult, - WorkerParseOptions, + PrefetchedModuleSignatures, + WorkerAnalyzeDependencyResult, + WorkerCompileResult, + WorkerCompilerOptions, + WorkerFormatResult, + WorkerLintOptions, + WorkerLintResult, + WorkerParseOptions, } from '../common/bridges/WorkerBridge'; import {ModuleSignature} from '@romejs/js-analysis'; import { - AbsoluteFilePath, - AbsoluteFilePathSet, - UnknownFilePath, - createAbsoluteFilePath, - createUnknownFilePath, + AbsoluteFilePath, + AbsoluteFilePathSet, + UnknownFilePath, + createAbsoluteFilePath, + createUnknownFilePath, } from '@romejs/path'; import crypto = require('crypto'); import {Dict, RequiredProps} from '@romejs/typescript-helpers'; @@ -86,1023 +86,1019 @@ import {VCSClient} from '@romejs/vcs'; import {InlineSnapshotUpdates} from '../test-worker/SnapshotManager'; type MasterRequestOptions = { - master: Master; - client: MasterClient; - query: MasterQueryRequest; + master: Master; + client: MasterClient; + query: MasterQueryRequest; }; let requestIdCounter = 0; type NormalizedCommandFlags = { - flags: undefined | Dict; - defaultFlags: Dict; + flags: undefined | Dict; + defaultFlags: Dict; }; type ResolvedArg = { - path: AbsoluteFilePath; - location: DiagnosticLocation; - project: ProjectDefinition; + path: AbsoluteFilePath; + location: DiagnosticLocation; + project: ProjectDefinition; }; type ResolvedArgs = Array; export const EMPTY_SUCCESS_RESPONSE: MasterQueryResponseSuccess = { - type: 'SUCCESS', - hasData: false, - data: undefined, - markers: [], + type: 'SUCCESS', + hasData: false, + data: undefined, + markers: [], }; type GetFilesTryAlternateArg = ( - path: UnknownFilePath, + path: UnknownFilePath, ) => undefined | UnknownFilePath; export type MasterRequestGetFilesOptions = Omit< - MemoryFSGlobOptions, - 'getProjectIgnore' + MemoryFSGlobOptions, + 'getProjectIgnore' > & { - tryAlternateArg?: GetFilesTryAlternateArg; - ignoreArgumentMisses?: boolean; - ignoreProjectIgnore?: boolean; - disabledDiagnosticCategory?: DiagnosticCategory; - advice?: DiagnosticAdvice; - configCategory?: ProjectConfigCategoriesWithIgnore; - verb?: string; - noun?: string; - args?: Array; + tryAlternateArg?: GetFilesTryAlternateArg; + ignoreArgumentMisses?: boolean; + ignoreProjectIgnore?: boolean; + disabledDiagnosticCategory?: DiagnosticCategory; + advice?: DiagnosticAdvice; + configCategory?: ProjectConfigCategoriesWithIgnore; + verb?: string; + noun?: string; + args?: Array; }; export type MasterRequestGetFilesResult = { - projects: Set; - paths: AbsoluteFilePathSet; + projects: Set; + paths: AbsoluteFilePathSet; }; export class MasterRequestInvalid extends DiagnosticsError { - constructor(message: string, diagnostics: Diagnostics, showHelp: boolean) { - super(message, diagnostics); - this.showHelp = showHelp; - } + constructor(message: string, diagnostics: Diagnostics, showHelp: boolean) { + super(message, diagnostics); + this.showHelp = showHelp; + } - showHelp: boolean; + showHelp: boolean; } function hash(val: JSONObject): string { - return val === undefined || Object.keys(val).length === 0 - ? 'none' - : crypto.createHash('sha256').update(JSON.stringify(val)).digest('hex'); + return val === undefined || Object.keys(val).length === 0 + ? 'none' + : crypto.createHash('sha256').update(JSON.stringify(val)).digest('hex'); } export class MasterRequestCancelled extends Error { - constructor() { - super( - 'MasterRequest has been cancelled. This error is meant to be seen by Master', - ); - } + constructor() { + super( + 'MasterRequest has been cancelled. This error is meant to be seen by Master', + ); + } } export default class MasterRequest { - constructor(opts: MasterRequestOptions) { - this.query = opts.query; - this.master = opts.master; - this.bridge = opts.client.bridge; - this.reporter = opts.client.reporter; - this.cancelled = false; - this.toredown = false; - this.markerEvent = new Event({ - name: 'MasterRequest.marker', - onError: this.master.onFatalErrorBound, - }); - this.endEvent = new Event({ - name: 'MasterRequest.teardown', - onError: this.master.onFatalErrorBound, - serial: true, - }); - this.client = opts.client; - this.id = requestIdCounter++; - this.markers = []; - this.start = Date.now(); - this.normalizedCommandFlags = { - flags: {}, - defaultFlags: {}, - }; - this.client.requestsInFlight.add(this); - } - - start: number; - client: MasterClient; - query: MasterQueryRequest; - master: Master; - bridge: MasterBridge; - reporter: Reporter; - id: number; - markerEvent: Event; - endEvent: Event; - normalizedCommandFlags: NormalizedCommandFlags; - markers: Array; - cancelled: boolean; - toredown: boolean; - - async init() { - if (this.query.requestFlags.collectMarkers) { - this.markerEvent.subscribe((marker) => { - this.markers.push(marker); - }); - } - - await this.master.handleRequestStart(this); - } - - checkCancelled() { - if (this.cancelled) { - throw new MasterRequestCancelled(); - } - } - - cancel() { - this.cancelled = true; - this.teardown({ - type: 'CANCELLED', - }); - } - - teardown( - res: undefined | MasterQueryResponse, - ): undefined | MasterQueryResponse { - if (this.toredown) { - return; - } - - this.toredown = true; - this.client.requestsInFlight.delete(this); - - // Output timing information - if (this.query.requestFlags.timing) { - const end = Date.now(); - this.reporter.info( - `Request took ${String(end - this.start)}`, - ); - } - - if (res !== undefined) { - // If the query asked for no data then strip all diagnostics and data values - if (this.query.noData) { - if (res.type === 'SUCCESS') { - res = { - ...EMPTY_SUCCESS_RESPONSE, - hasData: res.data !== undefined, - }; - } else if (res.type === 'DIAGNOSTICS') { - res = { - type: 'DIAGNOSTICS', - diagnostics: [], - }; - } else if (res.type === 'INVALID_REQUEST') { - res = { - type: 'INVALID_REQUEST', - diagnostics: [], - showHelp: res.showHelp, - }; - } - } - - // Add on markers - if (res.type === 'SUCCESS') { - res = { - ...res, - markers: this.markers, - }; - } - } - - this.reporter.teardown(); - this.endEvent.send(res); - this.master.handleRequestEnd(this); - return res; - } - - setNormalizedCommandFlags(normalized: NormalizedCommandFlags) { - this.normalizedCommandFlags = normalized; - } - - async assertClientCwdProject(): Promise { - const location = this.getDiagnosticPointerForClientCwd(); - return this.master.projectManager.assertProject( - this.client.flags.cwd, - location, - ); - } - - async getVCSClient(): Promise { - return this.master.projectManager.getVCSClient( - await this.assertClientCwdProject(), - ); - } - - async maybeGetVCSClient(): Promise { - return this.master.projectManager.maybeGetVCSClient( - await this.assertClientCwdProject(), - ); - } - - createDiagnosticsProcessor( - opts: DiagnosticsProcessorOptions = {}, - ): DiagnosticsProcessor { - return new DiagnosticsProcessor({ - markupOptions: this.reporter.markupOptions, - ...opts, - }); - } - - createDiagnosticsPrinter( - processor: DiagnosticsProcessor = this.createDiagnosticsProcessor(), - ): DiagnosticsPrinter { - processor.unshiftOrigin({ - category: 'master', - message: `${this.query.commandName} command was dispatched`, - }); - - return new DiagnosticsPrinter({ - processor, - reporter: this.reporter, - cwd: this.client.flags.cwd, - flags: this.getDiagnosticsPrinterFlags(), - readFile: this.master.readDiagnosticsPrinterFile.bind(this.master), - }); - } - - getDiagnosticsPrinterFlags(): DiagnosticsPrinterFlags { - const {requestFlags} = this.query; - return { - grep: requestFlags.grep, - inverseGrep: requestFlags.inverseGrep, - showAllDiagnostics: requestFlags.showAllDiagnostics, - verboseDiagnostics: requestFlags.verboseDiagnostics, - maxDiagnostics: requestFlags.maxDiagnostics, - fieri: requestFlags.fieri, - }; - } - - expectArgumentLength(min: number, max: number = min) { - const {args} = this.query; - let message; - - let excessive = false; - - if (min === max) { - if (args.length !== min) { - if (min === 0) { - message = `Expected no arguments`; - } else { - message = `Expected exactly ${min} arguments`; - } - } - } else { - if (args.length < min) { - message = `Expected at least ${min} arguments`; - } - - if (args.length > max) { - excessive = true; - message = `Expected no more than ${min} arguments`; - } - } - - if (message !== undefined) { - this.throwDiagnosticFlagError({ - target: { - type: 'arg-range', - from: min, - to: max, - }, - description: descriptions.FLAGS.INCORRECT_ARG_COUNT(excessive, message), - }); - } - } - - throwDiagnosticFlagError( - { - description, - target = {type: 'none'}, - showHelp = true, - }: { - description: RequiredProps, 'message'>; - target?: SerializeCLITarget; - showHelp?: boolean; - }, - ) { - const location = this.getDiagnosticPointerFromFlags(target); - - let {category} = description; - if (category === undefined) { - category = - target.type === 'arg' || target.type === 'arg-range' - ? 'args/invalid' - : 'flags/invalid'; - } - - const diag: Diagnostic = { - description: { - advice: [], - ...description, - category, - }, - location, - }; - - throw new MasterRequestInvalid(description.message.value, [diag], showHelp); - } - - getDiagnosticPointerForClientCwd(): DiagnosticLocation { - const cwd = this.client.flags.cwd.join(); - return { - sourceText: cwd, - start: { - index: ob1Number0, - line: ob1Number1, - column: ob1Number0, - }, - end: { - index: ob1Coerce0(cwd.length), - line: ob1Number1, - column: ob1Coerce0(cwd.length), - }, - filename: 'cwd', - }; - } - - getDiagnosticPointerFromFlags(target: SerializeCLITarget): DiagnosticLocation { - const {query} = this; - - const rawFlags = { - ...this.client.flags, - ...this.query.requestFlags, - ...this.normalizedCommandFlags.flags, - }; - const flags: Dict = { - ...rawFlags, - cwd: rawFlags.cwd.join(), - }; - - const rawDefaultFlags = { - ...DEFAULT_CLIENT_FLAGS, - ...DEFAULT_CLIENT_REQUEST_FLAGS, - ...this.normalizedCommandFlags.defaultFlags, - clientName: this.client.flags.clientName, - }; - const defaultFlags: Dict = { - ...rawDefaultFlags, - cwd: rawDefaultFlags.cwd.join(), - }; - - return serializeCLIFlags( - { - programName: 'rome', - commandName: query.commandName, - flags, - args: query.args, - defaultFlags, - incorrectCaseFlags: new Set(), - shorthandFlags: new Set(), - }, - target, - ); - } - - getResolverOptionsFromFlags(): RequiredProps { - const {requestFlags} = this.query; - return { - origin: this.client.flags.cwd, - platform: requestFlags.resolverPlatform, - scale: requestFlags.resolverScale, - mocks: requestFlags.resolverMocks, - }; - } - - getBundlerConfigFromFlags( - resolverOpts?: Partial, - ): BundlerConfig { - return { - inlineSourceMap: false, - cwd: this.client.flags.cwd, - resolver: { - ...this.getResolverOptionsFromFlags(), - ...resolverOpts, - }, - }; - } - - async resolveFilesFromArgs( - overrideArgs?: Array, - tryAlternateArg?: GetFilesTryAlternateArg, - ): Promise<{ - projects: Set; - resolvedArgs: ResolvedArgs; - }> { - this.checkCancelled(); - - const projects: Set = new Set(); - const rawArgs = overrideArgs === undefined ? this.query.args : overrideArgs; - const resolvedArgs: ResolvedArgs = []; - const {cwd} = this.client.flags; - - // If args was explicitly provided then don't assume empty args is the project root - if (rawArgs.length === 0 && overrideArgs === undefined) { - const location = this.getDiagnosticPointerForClientCwd(); - const project = await this.assertClientCwdProject(); - resolvedArgs.push({ - path: project.folder, - location, - project, - }); - projects.add(project); - } else { - for (let i = 0; i < rawArgs.length; i++) { - const arg = rawArgs[i]; - - const location = this.getDiagnosticPointerFromFlags({ - type: 'arg', - key: i, - }); - - let source = createUnknownFilePath(arg); - let resolved: undefined | ResolverQueryResponseFound; - - if (tryAlternateArg !== undefined) { - const alternateSource = tryAlternateArg(source); - if (alternateSource !== undefined) { - const resolvedAlternate = await this.master.resolver.resolveEntry({ - origin: cwd, - source: alternateSource, - requestedType: 'folder', - }); - if (resolvedAlternate.type === 'FOUND') { - resolved = resolvedAlternate; - } - } - } - - if (resolved === undefined) { - resolved = await this.master.resolver.resolveEntryAssert( - { - origin: cwd, - source, - requestedType: 'folder', - }, - { - location, - }, - ); - } - - const project = this.master.projectManager.assertProjectExisting( - resolved.path, - ); - projects.add(project); - - resolvedArgs.push({ - project, - path: resolved.path, - location, - }); - } - } - - return { - resolvedArgs, - projects, - }; - } - - async watchFilesFromArgs( - opts: MasterRequestGetFilesOptions, - callback: ( - result: MasterRequestGetFilesResult, - initial: boolean, - ) => Promise, - ): Promise { - this.checkCancelled(); - - // Everything needs to be relative to this - const {resolvedArgs} = await this.resolveFilesFromArgs( - opts.args, - opts.tryAlternateArg, - ); - - const initial = await this.getFilesFromArgs(opts); - await callback(initial, true); - - let pendingEvictPaths: AbsoluteFilePathSet = new AbsoluteFilePathSet(); - let pendingEvictProjects: Set = new Set(); - let timeout: undefined | NodeJS.Timeout; - let changesWhileRunningCallback = false; - let runningCallback = false; - - async function flush() { - if (pendingEvictPaths.size === 0) { - return; - } - - timeout = undefined; - - const result: MasterRequestGetFilesResult = { - paths: pendingEvictPaths, - projects: pendingEvictProjects, - }; - pendingEvictPaths = new AbsoluteFilePathSet(); - pendingEvictProjects = new Set(); - - runningCallback = true; - await callback(result, false); - runningCallback = false; - - if (changesWhileRunningCallback) { - changesWhileRunningCallback = false; - flush(); - } - } - - const onChange = (path: AbsoluteFilePath) => { - let matches = false; - for (const arg of resolvedArgs) { - if (arg.path.equal(path) || path.isRelativeTo(arg.path)) { - matches = true; - break; - } - } - if (!matches) { - return; - } - - const project = this.master.projectManager.findProjectExisting(path); - if (project !== undefined) { - pendingEvictProjects.add(project); - } - - pendingEvictPaths.add(path); - - // Buffer up evicted paths - if (runningCallback) { - changesWhileRunningCallback = true; - } else if (timeout === undefined) { - timeout = setTimeout(flush, 100); - } - }; - - // Subscribe to evictions and file changes. This can cause double emits but we dedupe them with AbsoluteFilePathSet. An updated buffer dispatches a fileChangeEvent but NOT an evictEvent. An evictEvent is dispatched for all files in a project when the project config is changed but does NOT dispatch evictEvent. - const evictSubscription = this.master.fileAllocator.evictEvent.subscribe( - onChange, - ); - const fileChangeEvent = this.master.fileChangeEvent.subscribe(onChange); - - this.endEvent.subscribe(() => { - evictSubscription.unsubscribe(); - fileChangeEvent.unsubscribe(); - }); - - return mergeEventSubscriptions([evictSubscription, fileChangeEvent]); - } - - async getFilesFromArgs( - opts: MasterRequestGetFilesOptions = {}, - ): Promise { - this.checkCancelled(); - - const {master} = this; - const {configCategory, ignoreProjectIgnore} = opts; - const {projects, resolvedArgs} = await this.resolveFilesFromArgs( - opts.args, - opts.tryAlternateArg, - ); - - const extendedGlobOpts: MemoryFSGlobOptions = {...opts}; - - if (configCategory !== undefined) { - extendedGlobOpts.getProjectIgnore = (project) => - ignoreProjectIgnore ? [] : project.config[configCategory].ignore - ; - } - - // Resolved arguments that resulted in no files - const noArgMatches: Set = new Set(); - - // Match files - const paths: AbsoluteFilePathSet = new AbsoluteFilePathSet(); - for (const arg of resolvedArgs) { - const matches = master.memoryFs.glob(arg.path, extendedGlobOpts); - - if (matches.size === 0) { - if (!opts.ignoreArgumentMisses) { - noArgMatches.add(arg); - } - } else { - for (const path of matches) { - paths.add(path); - } - } - } - - if (noArgMatches.size > 0) { - const diagnostics: Diagnostics = []; - - for (const {path, project, location} of noArgMatches) { - let category: DiagnosticCategory = 'args/fileNotFound'; - - let advice: DiagnosticAdvice = [...(opts.advice || [])]; - - // Hint if all files were ignored - if (configCategory !== undefined && !ignoreProjectIgnore) { - const {paths: withoutIgnore} = await this.getFilesFromArgs({ - ...opts, - ignoreProjectIgnore: true, - }); - - // Remove paths that we already successfully found - for (const path of paths) { - withoutIgnore.delete(path); - } - - if (withoutIgnore.size > 0) { - advice.push({ - type: 'log', - category: 'info', - text: 'The following files were ignored', - }); - - advice.push({ - type: 'list', - list: Array.from( - withoutIgnore, - (path) => ``, - ), - truncate: true, - }); - - const ignoreSource = master.projectManager.findProjectConfigConsumer( - project, - (consumer) => - consumer.has(configCategory) && - consumer.get(configCategory).get('ignore') - , - ); - - if (ignoreSource.value !== undefined) { - const ignorePointer = ignoreSource.value.getDiagnosticLocation( - 'value', - ); - - advice.push({ - type: 'log', - category: 'info', - text: 'Ignore patterns were defined here', - }); - - advice.push({ - type: 'frame', - location: ignorePointer, - }); - } - } - } - - diagnostics.push({ - location: { - ...location, - marker: ``, - }, - description: { - ...descriptions.FLAGS.NO_FILES_FOUND(opts.noun), - category, - advice, - }, - }); - } - - throw new DiagnosticsError( - 'MasterRequest.getFilesFromArgs: Some arguments did not resolve to any files', - diagnostics, - ); - } - - return {paths, projects}; - } - - normalizeCompileResult(res: WorkerCompileResult): WorkerCompileResult { - const {projectManager} = this.master; - - // Turn all the cacheDependencies entries from 'absolute paths to UIDs - return { - ...res, - cacheDependencies: res.cacheDependencies.map((filename) => { - return projectManager.getFileReference(createAbsoluteFilePath(filename)).uid; - }), - }; - } - - startMarker( - opts: Omit, - ): MasterUnfinishedMarker { - this.master.logger.info('Started marker %s', opts.label); - return { - ...opts, - start: Date.now(), - }; - } - - endMarker(startMarker: MasterUnfinishedMarker): MasterMarker { - const endMarker: MasterMarker = { - ...startMarker, - end: Date.now(), - }; - this.master.logger.info('Finished marker %s', startMarker.label); - this.markerEvent.send(endMarker); - return endMarker; - } - - async wrapRequestDiagnostic( - method: string, - path: AbsoluteFilePath, - factory: (bridge: WorkerBridge, ref: JSONFileReference) => Promise, - ): Promise { - const {master} = this; - const owner = await master.fileAllocator.getOrAssignOwner(path); - const ref = master.projectManager.getTransportFileReference(path); - - const marker = this.startMarker({ - label: `${method}: ${ref.uid}`, - facet: method, - rowId: `worker ${owner.id}`, - }); - - try { - const res: T = await factory(owner.bridge, ref); - this.endMarker(marker); - return res; - } catch (err) { - let diagnostics = getDiagnosticsFromError(err); - - if (diagnostics === undefined) { - const diag = deriveDiagnosticFromError( - err, - { - description: { - category: 'internalError/request', - }, - }, - ); - - throw createSingleDiagnosticError({ - ...diag, - description: { - ...diag.description, - advice: [ - ...diag.description.advice, - { - type: 'log', - category: 'info', - text: markup`Error occurred while requesting ${method} for `, - }, - ], - }, - }); - } else { - // We don't want to tamper with these - throw err; - } - } - } - - async requestWorkerUpdateBuffer( - path: AbsoluteFilePath, - content: string, - ): Promise { - this.checkCancelled(); - - await this.wrapRequestDiagnostic( - 'updateBuffer', - path, - (bridge, file) => bridge.updateBuffer.call({file, content}), - ); - this.master.fileChangeEvent.send(path); - } - - async requestWorkerParse( - path: AbsoluteFilePath, - opts: WorkerParseOptions, - ): Promise { - this.checkCancelled(); - - return this.wrapRequestDiagnostic( - 'parse', - path, - (bridge, file) => bridge.parseJS.call({file, options: opts}), - ); - } - - async requestWorkerUpdateInlineSnapshots( - path: AbsoluteFilePath, - updates: InlineSnapshotUpdates, - parseOptions: WorkerParseOptions, - ): Promise { - this.checkCancelled(); - - return this.wrapRequestDiagnostic( - 'updateInlineSnapshots', - path, - (bridge, file) => - bridge.updateInlineSnapshots.call({file, updates, parseOptions}) - , - ); - } - - async requestWorkerLint( - path: AbsoluteFilePath, - optionsWithoutModSigs: Omit, - ): Promise { - this.checkCancelled(); - - const {cache} = this.master; - const cacheEntry = await cache.get(path); - - const cacheKey = hash(optionsWithoutModSigs); - const cached = cacheEntry.lint[cacheKey]; - if (cached !== undefined) { - return cached; - } - - const prefetchedModuleSignatures = await this.maybePrefetchModuleSignatures( - path, - ); - - const options: WorkerLintOptions = { - ...optionsWithoutModSigs, - prefetchedModuleSignatures, - }; - - const res = await this.wrapRequestDiagnostic( - 'lint', - path, - (bridge, file) => bridge.lint.call({file, options, parseOptions: {}}), - ); - - await cache.update( - path, - (cacheEntry) => ({ - lint: { - ...cacheEntry.lint, - [cacheKey]: res, - }, - }), - ); - - return res; - } - - async requestWorkerFormat( - path: AbsoluteFilePath, - parseOptions: WorkerParseOptions, - ): Promise { - this.checkCancelled(); - - return await this.wrapRequestDiagnostic( - 'format', - path, - (bridge, file) => bridge.format.call({file, parseOptions}), - ); - } - - async requestWorkerCompile( - path: AbsoluteFilePath, - stage: TransformStageName, - options: WorkerCompilerOptions, - parseOptions: WorkerParseOptions, - ): Promise { - this.checkCancelled(); - - const {cache} = this.master; - - // Create a cache key comprised of the stage and hash of the options - const cacheKey = `${stage}:${hash(options)}`; - - // Check cache for this stage and options - const cacheEntry = await cache.get(path); - const cached = cacheEntry.compile[cacheKey]; - if (cached !== undefined) { - // TODO check cacheDependencies - return cached; - } - - const compileRes = await this.wrapRequestDiagnostic( - 'compile', - path, - (bridge, file) => { - // We allow options to be passed in as undefined so we can compute an easy cache key - if (options === undefined) { - options = {}; - } - - return bridge.compileJS.call({file, stage, options, parseOptions}); - }, - ); - - const res = this.normalizeCompileResult({ - ...compileRes, - cached: false, - }); - - // There's a race condition here between the file being opened and then rewritten - await cache.update( - path, - (cacheEntry) => ({ - compile: { - ...cacheEntry.compile, - [cacheKey]: { - ...res, - cached: true, - }, - }, - }), - ); - - return res; - } - - async requestWorkerAnalyzeDependencies( - path: AbsoluteFilePath, - parseOptions: WorkerParseOptions, - ): Promise { - this.checkCancelled(); - - const {cache} = this.master; - - const cacheEntry = await cache.get(path); - if (cacheEntry.analyzeDependencies !== undefined) { - return cacheEntry.analyzeDependencies; - } - - const res = await this.wrapRequestDiagnostic( - 'analyzeDependencies', - path, - (bridge, file) => bridge.analyzeDependencies.call({file, parseOptions}), - ); - await cache.update( - path, - { - analyzeDependencies: { - ...res, - cached: true, - }, - }, - ); - - return { - ...res, - cached: false, - }; - } - - async requestWorkerModuleSignature( - path: AbsoluteFilePath, - parseOptions: WorkerParseOptions, - ): Promise { - this.checkCancelled(); - - const {cache} = this.master; - - const cacheEntry = await cache.get(path); - if (cacheEntry.moduleSignature !== undefined) { - return cacheEntry.moduleSignature; - } - - const res = await this.wrapRequestDiagnostic( - 'moduleSignature', - path, - (bridge, file) => bridge.moduleSignatureJS.call({file, parseOptions}), - ); - await cache.update( - path, - { - moduleSignature: res, - }, - ); - return res; - } - - async maybePrefetchModuleSignatures( - path: AbsoluteFilePath, - ): Promise { - this.checkCancelled(); - - const {projectManager} = this.master; - - const prefetchedModuleSignatures: PrefetchedModuleSignatures = {}; - const project = await projectManager.assertProject(path); - if (project.config.typeCheck.enabled === false) { - return prefetchedModuleSignatures; - } - - // get the owner of this file - - /*const rootOwner = await fileAllocator.getOrAssignOwner(filename); + constructor(opts: MasterRequestOptions) { + this.query = opts.query; + this.master = opts.master; + this.bridge = opts.client.bridge; + this.reporter = opts.client.reporter; + this.cancelled = false; + this.toredown = false; + this.markerEvent = new Event({ + name: 'MasterRequest.marker', + onError: this.master.onFatalErrorBound, + }); + this.endEvent = new Event({ + name: 'MasterRequest.teardown', + onError: this.master.onFatalErrorBound, + serial: true, + }); + this.client = opts.client; + this.id = requestIdCounter++; + this.markers = []; + this.start = Date.now(); + this.normalizedCommandFlags = { + flags: {}, + defaultFlags: {}, + }; + this.client.requestsInFlight.add(this); + } + + start: number; + client: MasterClient; + query: MasterQueryRequest; + master: Master; + bridge: MasterBridge; + reporter: Reporter; + id: number; + markerEvent: Event; + endEvent: Event; + normalizedCommandFlags: NormalizedCommandFlags; + markers: Array; + cancelled: boolean; + toredown: boolean; + + async init() { + if (this.query.requestFlags.collectMarkers) { + this.markerEvent.subscribe((marker) => { + this.markers.push(marker); + }); + } + + await this.master.handleRequestStart(this); + } + + checkCancelled() { + if (this.cancelled) { + throw new MasterRequestCancelled(); + } + } + + cancel() { + this.cancelled = true; + this.teardown({ + type: 'CANCELLED', + }); + } + + teardown(res: undefined | MasterQueryResponse): undefined | MasterQueryResponse { + if (this.toredown) { + return; + } + + this.toredown = true; + this.client.requestsInFlight.delete(this); + + // Output timing information + if (this.query.requestFlags.timing) { + const end = Date.now(); + this.reporter.info( + `Request took ${String(end - this.start)}`, + ); + } + + if (res !== undefined) { + // If the query asked for no data then strip all diagnostics and data values + if (this.query.noData) { + if (res.type === 'SUCCESS') { + res = { + ...EMPTY_SUCCESS_RESPONSE, + hasData: res.data !== undefined, + }; + } else if (res.type === 'DIAGNOSTICS') { + res = { + type: 'DIAGNOSTICS', + diagnostics: [], + }; + } else if (res.type === 'INVALID_REQUEST') { + res = { + type: 'INVALID_REQUEST', + diagnostics: [], + showHelp: res.showHelp, + }; + } + } + + // Add on markers + if (res.type === 'SUCCESS') { + res = { + ...res, + markers: this.markers, + }; + } + } + + this.reporter.teardown(); + this.endEvent.send(res); + this.master.handleRequestEnd(this); + return res; + } + + setNormalizedCommandFlags(normalized: NormalizedCommandFlags) { + this.normalizedCommandFlags = normalized; + } + + async assertClientCwdProject(): Promise { + const location = this.getDiagnosticPointerForClientCwd(); + return this.master.projectManager.assertProject( + this.client.flags.cwd, + location, + ); + } + + async getVCSClient(): Promise { + return this.master.projectManager.getVCSClient( + await this.assertClientCwdProject(), + ); + } + + async maybeGetVCSClient(): Promise { + return this.master.projectManager.maybeGetVCSClient( + await this.assertClientCwdProject(), + ); + } + + createDiagnosticsProcessor( + opts: DiagnosticsProcessorOptions = {}, + ): DiagnosticsProcessor { + return new DiagnosticsProcessor({ + markupOptions: this.reporter.markupOptions, + ...opts, + }); + } + + createDiagnosticsPrinter( + processor: DiagnosticsProcessor = this.createDiagnosticsProcessor(), + ): DiagnosticsPrinter { + processor.unshiftOrigin({ + category: 'master', + message: `${this.query.commandName} command was dispatched`, + }); + + return new DiagnosticsPrinter({ + processor, + reporter: this.reporter, + cwd: this.client.flags.cwd, + flags: this.getDiagnosticsPrinterFlags(), + readFile: this.master.readDiagnosticsPrinterFile.bind(this.master), + }); + } + + getDiagnosticsPrinterFlags(): DiagnosticsPrinterFlags { + const {requestFlags} = this.query; + return { + grep: requestFlags.grep, + inverseGrep: requestFlags.inverseGrep, + showAllDiagnostics: requestFlags.showAllDiagnostics, + verboseDiagnostics: requestFlags.verboseDiagnostics, + maxDiagnostics: requestFlags.maxDiagnostics, + fieri: requestFlags.fieri, + }; + } + + expectArgumentLength(min: number, max: number = min) { + const {args} = this.query; + let message; + + let excessive = false; + + if (min === max) { + if (args.length !== min) { + if (min === 0) { + message = `Expected no arguments`; + } else { + message = `Expected exactly ${min} arguments`; + } + } + } else { + if (args.length < min) { + message = `Expected at least ${min} arguments`; + } + + if (args.length > max) { + excessive = true; + message = `Expected no more than ${min} arguments`; + } + } + + if (message !== undefined) { + this.throwDiagnosticFlagError({ + target: { + type: 'arg-range', + from: min, + to: max, + }, + description: descriptions.FLAGS.INCORRECT_ARG_COUNT(excessive, message), + }); + } + } + + throwDiagnosticFlagError( + { + description, + target = {type: 'none'}, + showHelp = true, + }: { + description: RequiredProps, 'message'>; + target?: SerializeCLITarget; + showHelp?: boolean; + }, + ) { + const location = this.getDiagnosticPointerFromFlags(target); + + let {category} = description; + if (category === undefined) { + category = + target.type === 'arg' || target.type === 'arg-range' + ? 'args/invalid' + : 'flags/invalid'; + } + + const diag: Diagnostic = { + description: { + advice: [], + ...description, + category, + }, + location, + }; + + throw new MasterRequestInvalid(description.message.value, [diag], showHelp); + } + + getDiagnosticPointerForClientCwd(): DiagnosticLocation { + const cwd = this.client.flags.cwd.join(); + return { + sourceText: cwd, + start: { + index: ob1Number0, + line: ob1Number1, + column: ob1Number0, + }, + end: { + index: ob1Coerce0(cwd.length), + line: ob1Number1, + column: ob1Coerce0(cwd.length), + }, + filename: 'cwd', + }; + } + + getDiagnosticPointerFromFlags(target: SerializeCLITarget): DiagnosticLocation { + const {query} = this; + + const rawFlags = { + ...this.client.flags, + ...this.query.requestFlags, + ...this.normalizedCommandFlags.flags, + }; + const flags: Dict = { + ...rawFlags, + cwd: rawFlags.cwd.join(), + }; + + const rawDefaultFlags = { + ...DEFAULT_CLIENT_FLAGS, + ...DEFAULT_CLIENT_REQUEST_FLAGS, + ...this.normalizedCommandFlags.defaultFlags, + clientName: this.client.flags.clientName, + }; + const defaultFlags: Dict = { + ...rawDefaultFlags, + cwd: rawDefaultFlags.cwd.join(), + }; + + return serializeCLIFlags( + { + programName: 'rome', + commandName: query.commandName, + flags, + args: query.args, + defaultFlags, + incorrectCaseFlags: new Set(), + shorthandFlags: new Set(), + }, + target, + ); + } + + getResolverOptionsFromFlags(): RequiredProps { + const {requestFlags} = this.query; + return { + origin: this.client.flags.cwd, + platform: requestFlags.resolverPlatform, + scale: requestFlags.resolverScale, + mocks: requestFlags.resolverMocks, + }; + } + + getBundlerConfigFromFlags( + resolverOpts?: Partial, + ): BundlerConfig { + return { + inlineSourceMap: false, + cwd: this.client.flags.cwd, + resolver: { + ...this.getResolverOptionsFromFlags(), + ...resolverOpts, + }, + }; + } + + async resolveFilesFromArgs( + overrideArgs?: Array, + tryAlternateArg?: GetFilesTryAlternateArg, + ): Promise<{ + projects: Set; + resolvedArgs: ResolvedArgs; + }> { + this.checkCancelled(); + + const projects: Set = new Set(); + const rawArgs = overrideArgs === undefined ? this.query.args : overrideArgs; + const resolvedArgs: ResolvedArgs = []; + const {cwd} = this.client.flags; + + // If args was explicitly provided then don't assume empty args is the project root + if (rawArgs.length === 0 && overrideArgs === undefined) { + const location = this.getDiagnosticPointerForClientCwd(); + const project = await this.assertClientCwdProject(); + resolvedArgs.push({ + path: project.folder, + location, + project, + }); + projects.add(project); + } else { + for (let i = 0; i < rawArgs.length; i++) { + const arg = rawArgs[i]; + + const location = this.getDiagnosticPointerFromFlags({ + type: 'arg', + key: i, + }); + + let source = createUnknownFilePath(arg); + let resolved: undefined | ResolverQueryResponseFound; + + if (tryAlternateArg !== undefined) { + const alternateSource = tryAlternateArg(source); + if (alternateSource !== undefined) { + const resolvedAlternate = await this.master.resolver.resolveEntry({ + origin: cwd, + source: alternateSource, + requestedType: 'folder', + }); + if (resolvedAlternate.type === 'FOUND') { + resolved = resolvedAlternate; + } + } + } + + if (resolved === undefined) { + resolved = await this.master.resolver.resolveEntryAssert( + { + origin: cwd, + source, + requestedType: 'folder', + }, + { + location, + }, + ); + } + + const project = this.master.projectManager.assertProjectExisting( + resolved.path, + ); + projects.add(project); + + resolvedArgs.push({ + project, + path: resolved.path, + location, + }); + } + } + + return { + resolvedArgs, + projects, + }; + } + + async watchFilesFromArgs( + opts: MasterRequestGetFilesOptions, + callback: ( + result: MasterRequestGetFilesResult, + initial: boolean, + ) => Promise, + ): Promise { + this.checkCancelled(); + + // Everything needs to be relative to this + const {resolvedArgs} = await this.resolveFilesFromArgs( + opts.args, + opts.tryAlternateArg, + ); + + const initial = await this.getFilesFromArgs(opts); + await callback(initial, true); + + let pendingEvictPaths: AbsoluteFilePathSet = new AbsoluteFilePathSet(); + let pendingEvictProjects: Set = new Set(); + let timeout: undefined | NodeJS.Timeout; + let changesWhileRunningCallback = false; + let runningCallback = false; + + async function flush() { + if (pendingEvictPaths.size === 0) { + return; + } + + timeout = undefined; + + const result: MasterRequestGetFilesResult = { + paths: pendingEvictPaths, + projects: pendingEvictProjects, + }; + pendingEvictPaths = new AbsoluteFilePathSet(); + pendingEvictProjects = new Set(); + + runningCallback = true; + await callback(result, false); + runningCallback = false; + + if (changesWhileRunningCallback) { + changesWhileRunningCallback = false; + flush(); + } + } + + const onChange = (path: AbsoluteFilePath) => { + let matches = false; + for (const arg of resolvedArgs) { + if (arg.path.equal(path) || path.isRelativeTo(arg.path)) { + matches = true; + break; + } + } + if (!matches) { + return; + } + + const project = this.master.projectManager.findProjectExisting(path); + if (project !== undefined) { + pendingEvictProjects.add(project); + } + + pendingEvictPaths.add(path); + + // Buffer up evicted paths + if (runningCallback) { + changesWhileRunningCallback = true; + } else if (timeout === undefined) { + timeout = setTimeout(flush, 100); + } + }; + + // Subscribe to evictions and file changes. This can cause double emits but we dedupe them with AbsoluteFilePathSet. An updated buffer dispatches a fileChangeEvent but NOT an evictEvent. An evictEvent is dispatched for all files in a project when the project config is changed but does NOT dispatch evictEvent. + const evictSubscription = this.master.fileAllocator.evictEvent.subscribe( + onChange, + ); + const fileChangeEvent = this.master.fileChangeEvent.subscribe(onChange); + + this.endEvent.subscribe(() => { + evictSubscription.unsubscribe(); + fileChangeEvent.unsubscribe(); + }); + + return mergeEventSubscriptions([evictSubscription, fileChangeEvent]); + } + + async getFilesFromArgs( + opts: MasterRequestGetFilesOptions = {}, + ): Promise { + this.checkCancelled(); + + const {master} = this; + const {configCategory, ignoreProjectIgnore} = opts; + const {projects, resolvedArgs} = await this.resolveFilesFromArgs( + opts.args, + opts.tryAlternateArg, + ); + + const extendedGlobOpts: MemoryFSGlobOptions = {...opts}; + + if (configCategory !== undefined) { + extendedGlobOpts.getProjectIgnore = (project) => + ignoreProjectIgnore ? [] : project.config[configCategory].ignore + ; + } + + // Resolved arguments that resulted in no files + const noArgMatches: Set = new Set(); + + // Match files + const paths: AbsoluteFilePathSet = new AbsoluteFilePathSet(); + for (const arg of resolvedArgs) { + const matches = master.memoryFs.glob(arg.path, extendedGlobOpts); + + if (matches.size === 0) { + if (!opts.ignoreArgumentMisses) { + noArgMatches.add(arg); + } + } else { + for (const path of matches) { + paths.add(path); + } + } + } + + if (noArgMatches.size > 0) { + const diagnostics: Diagnostics = []; + + for (const {path, project, location} of noArgMatches) { + let category: DiagnosticCategory = 'args/fileNotFound'; + + let advice: DiagnosticAdvice = [...(opts.advice || [])]; + + // Hint if all files were ignored + if (configCategory !== undefined && !ignoreProjectIgnore) { + const {paths: withoutIgnore} = await this.getFilesFromArgs({ + ...opts, + ignoreProjectIgnore: true, + }); + + // Remove paths that we already successfully found + for (const path of paths) { + withoutIgnore.delete(path); + } + + if (withoutIgnore.size > 0) { + advice.push({ + type: 'log', + category: 'info', + text: 'The following files were ignored', + }); + + advice.push({ + type: 'list', + list: Array.from( + withoutIgnore, + (path) => ``, + ), + truncate: true, + }); + + const ignoreSource = master.projectManager.findProjectConfigConsumer( + project, + (consumer) => + consumer.has(configCategory) && + consumer.get(configCategory).get('ignore') + , + ); + + if (ignoreSource.value !== undefined) { + const ignorePointer = ignoreSource.value.getDiagnosticLocation('value'); + + advice.push({ + type: 'log', + category: 'info', + text: 'Ignore patterns were defined here', + }); + + advice.push({ + type: 'frame', + location: ignorePointer, + }); + } + } + } + + diagnostics.push({ + location: { + ...location, + marker: ``, + }, + description: { + ...descriptions.FLAGS.NO_FILES_FOUND(opts.noun), + category, + advice, + }, + }); + } + + throw new DiagnosticsError( + 'MasterRequest.getFilesFromArgs: Some arguments did not resolve to any files', + diagnostics, + ); + } + + return {paths, projects}; + } + + normalizeCompileResult(res: WorkerCompileResult): WorkerCompileResult { + const {projectManager} = this.master; + + // Turn all the cacheDependencies entries from 'absolute paths to UIDs + return { + ...res, + cacheDependencies: res.cacheDependencies.map((filename) => { + return projectManager.getFileReference(createAbsoluteFilePath(filename)).uid; + }), + }; + } + + startMarker( + opts: Omit, + ): MasterUnfinishedMarker { + this.master.logger.info('Started marker %s', opts.label); + return { + ...opts, + start: Date.now(), + }; + } + + endMarker(startMarker: MasterUnfinishedMarker): MasterMarker { + const endMarker: MasterMarker = { + ...startMarker, + end: Date.now(), + }; + this.master.logger.info('Finished marker %s', startMarker.label); + this.markerEvent.send(endMarker); + return endMarker; + } + + async wrapRequestDiagnostic( + method: string, + path: AbsoluteFilePath, + factory: (bridge: WorkerBridge, ref: JSONFileReference) => Promise, + ): Promise { + const {master} = this; + const owner = await master.fileAllocator.getOrAssignOwner(path); + const ref = master.projectManager.getTransportFileReference(path); + + const marker = this.startMarker({ + label: `${method}: ${ref.uid}`, + facet: method, + rowId: `worker ${owner.id}`, + }); + + try { + const res: T = await factory(owner.bridge, ref); + this.endMarker(marker); + return res; + } catch (err) { + let diagnostics = getDiagnosticsFromError(err); + + if (diagnostics === undefined) { + const diag = deriveDiagnosticFromError( + err, + { + description: { + category: 'internalError/request', + }, + }, + ); + + throw createSingleDiagnosticError({ + ...diag, + description: { + ...diag.description, + advice: [ + ...diag.description.advice, + { + type: 'log', + category: 'info', + text: markup`Error occurred while requesting ${method} for `, + }, + ], + }, + }); + } else { + // We don't want to tamper with these + throw err; + } + } + } + + async requestWorkerUpdateBuffer( + path: AbsoluteFilePath, + content: string, + ): Promise { + this.checkCancelled(); + + await this.wrapRequestDiagnostic( + 'updateBuffer', + path, + (bridge, file) => bridge.updateBuffer.call({file, content}), + ); + this.master.fileChangeEvent.send(path); + } + + async requestWorkerParse( + path: AbsoluteFilePath, + opts: WorkerParseOptions, + ): Promise { + this.checkCancelled(); + + return this.wrapRequestDiagnostic( + 'parse', + path, + (bridge, file) => bridge.parseJS.call({file, options: opts}), + ); + } + + async requestWorkerUpdateInlineSnapshots( + path: AbsoluteFilePath, + updates: InlineSnapshotUpdates, + parseOptions: WorkerParseOptions, + ): Promise { + this.checkCancelled(); + + return this.wrapRequestDiagnostic( + 'updateInlineSnapshots', + path, + (bridge, file) => + bridge.updateInlineSnapshots.call({file, updates, parseOptions}) + , + ); + } + + async requestWorkerLint( + path: AbsoluteFilePath, + optionsWithoutModSigs: Omit, + ): Promise { + this.checkCancelled(); + + const {cache} = this.master; + const cacheEntry = await cache.get(path); + + const cacheKey = hash(optionsWithoutModSigs); + const cached = cacheEntry.lint[cacheKey]; + if (cached !== undefined) { + return cached; + } + + const prefetchedModuleSignatures = await this.maybePrefetchModuleSignatures( + path, + ); + + const options: WorkerLintOptions = { + ...optionsWithoutModSigs, + prefetchedModuleSignatures, + }; + + const res = await this.wrapRequestDiagnostic( + 'lint', + path, + (bridge, file) => bridge.lint.call({file, options, parseOptions: {}}), + ); + + await cache.update( + path, + (cacheEntry) => ({ + lint: { + ...cacheEntry.lint, + [cacheKey]: res, + }, + }), + ); + + return res; + } + + async requestWorkerFormat( + path: AbsoluteFilePath, + parseOptions: WorkerParseOptions, + ): Promise { + this.checkCancelled(); + + return await this.wrapRequestDiagnostic( + 'format', + path, + (bridge, file) => bridge.format.call({file, parseOptions}), + ); + } + + async requestWorkerCompile( + path: AbsoluteFilePath, + stage: TransformStageName, + options: WorkerCompilerOptions, + parseOptions: WorkerParseOptions, + ): Promise { + this.checkCancelled(); + + const {cache} = this.master; + + // Create a cache key comprised of the stage and hash of the options + const cacheKey = `${stage}:${hash(options)}`; + + // Check cache for this stage and options + const cacheEntry = await cache.get(path); + const cached = cacheEntry.compile[cacheKey]; + if (cached !== undefined) { + // TODO check cacheDependencies + return cached; + } + + const compileRes = await this.wrapRequestDiagnostic( + 'compile', + path, + (bridge, file) => { + // We allow options to be passed in as undefined so we can compute an easy cache key + if (options === undefined) { + options = {}; + } + + return bridge.compileJS.call({file, stage, options, parseOptions}); + }, + ); + + const res = this.normalizeCompileResult({ + ...compileRes, + cached: false, + }); + + // There's a race condition here between the file being opened and then rewritten + await cache.update( + path, + (cacheEntry) => ({ + compile: { + ...cacheEntry.compile, + [cacheKey]: { + ...res, + cached: true, + }, + }, + }), + ); + + return res; + } + + async requestWorkerAnalyzeDependencies( + path: AbsoluteFilePath, + parseOptions: WorkerParseOptions, + ): Promise { + this.checkCancelled(); + + const {cache} = this.master; + + const cacheEntry = await cache.get(path); + if (cacheEntry.analyzeDependencies !== undefined) { + return cacheEntry.analyzeDependencies; + } + + const res = await this.wrapRequestDiagnostic( + 'analyzeDependencies', + path, + (bridge, file) => bridge.analyzeDependencies.call({file, parseOptions}), + ); + await cache.update( + path, + { + analyzeDependencies: { + ...res, + cached: true, + }, + }, + ); + + return { + ...res, + cached: false, + }; + } + + async requestWorkerModuleSignature( + path: AbsoluteFilePath, + parseOptions: WorkerParseOptions, + ): Promise { + this.checkCancelled(); + + const {cache} = this.master; + + const cacheEntry = await cache.get(path); + if (cacheEntry.moduleSignature !== undefined) { + return cacheEntry.moduleSignature; + } + + const res = await this.wrapRequestDiagnostic( + 'moduleSignature', + path, + (bridge, file) => bridge.moduleSignatureJS.call({file, parseOptions}), + ); + await cache.update( + path, + { + moduleSignature: res, + }, + ); + return res; + } + + async maybePrefetchModuleSignatures( + path: AbsoluteFilePath, + ): Promise { + this.checkCancelled(); + + const {projectManager} = this.master; + + const prefetchedModuleSignatures: PrefetchedModuleSignatures = {}; + const project = await projectManager.assertProject(path); + if (project.config.typeCheck.enabled === false) { + return prefetchedModuleSignatures; + } + + // get the owner of this file + + /*const rootOwner = await fileAllocator.getOrAssignOwner(filename); const rootOwnerId = workerManager.getIdFromBridge(rootOwner); // absolute filenames to redupe export graphs @@ -1168,6 +1164,6 @@ export default class MasterRequest { graph, }; }*/ - return prefetchedModuleSignatures; - } + return prefetchedModuleSignatures; + } } diff --git a/packages/@romejs/core/master/WorkerManager.ts b/packages/@romejs/core/master/WorkerManager.ts index 2f381452a6c..16c8e102658 100644 --- a/packages/@romejs/core/master/WorkerManager.ts +++ b/packages/@romejs/core/master/WorkerManager.ts @@ -9,357 +9,357 @@ import {ProjectDefinition} from '@romejs/project'; import {Stats} from './fs/MemoryFileSystem'; import fork from '../common/utils/fork'; import { - MAX_MASTER_BYTES_BEFORE_WORKERS, - MAX_WORKER_BYTES_BEFORE_ADD, + MAX_MASTER_BYTES_BEFORE_WORKERS, + MAX_WORKER_BYTES_BEFORE_ADD, } from '../common/constants'; import {MAX_WORKER_COUNT, Master, Worker, WorkerBridge} from '@romejs/core'; import Locker from '../common/utils/Locker'; import { - Event, - createBridgeFromChildProcess, - createBridgeFromLocal, + Event, + createBridgeFromChildProcess, + createBridgeFromLocal, } from '@romejs/events'; import child = require('child_process'); import {AbsoluteFilePath} from '@romejs/path'; export type WorkerContainer = { - id: number; - fileCount: number; - byteCount: number; - bridge: WorkerBridge; - process: undefined | child.ChildProcess; - // Whether we've completed a handshake with the worker and it's ready to receive requests - ready: boolean; - // Whether we should assign files to this worker - ghost: boolean; + id: number; + fileCount: number; + byteCount: number; + bridge: WorkerBridge; + process: undefined | child.ChildProcess; + // Whether we've completed a handshake with the worker and it's ready to receive requests + ready: boolean; + // Whether we should assign files to this worker + ghost: boolean; }; export default class WorkerManager { - constructor(master: Master) { - this.master = master; - - this.workerStartEvent = new Event({ - name: 'WorkerManager.workerStart', - onError: master.onFatalErrorBound, - }); - this.selfWorker = true; - this.locker = new Locker(); - this.workers = new Map(); - this.idCounter = 0; - } - - master: Master; - locker: Locker; - - selfWorker: boolean; - workerStartEvent: Event; - - workers: Map; - - // We use an idCounter rather than using workers.size due to race conditions - - // If we use workers.size to generate the next id, then by the time we insert it - - // into the map between async operations, it could already be filled! - idCounter: number; - - getNextWorkerId(): number { - return this.idCounter++; - } - - getWorkerAssert(id: number): WorkerContainer { - const worker = this.workers.get(id); - if (worker === undefined) { - throw new Error('Expected worker'); - } - return worker; - } - - getWorkers(): Array { - return Array.from(this.workers.values()); - } - - // Get worker count, excluding ghost workers - getWorkerCount(): number { - let count = 0; - for (const worker of this.workers.values()) { - if (worker.ghost === false) { - count++; - } - } - return count; - } - - // Get all the workers that live in external processes - getExternalWorkers(): Array { - return this.getWorkers().filter((worker) => worker.process !== undefined); - } - - end() { - // Shutdown all workers, no need to clean up any internal data structures since they will never be used - for (const {bridge} of this.workers.values()) { - bridge.end(); - } - } - - getLowestByteCountWorker(): WorkerContainer { - // Find the worker with the lowest byteCount value - let smallestWorker; - let byteCount; - for (const worker of this.workers.values()) { - if ( - !worker.ghost && - (byteCount === undefined || byteCount > worker.byteCount) - ) { - smallestWorker = worker; - byteCount = worker.byteCount; - } - } - - if (smallestWorker === undefined) { - // This shouldn't be possible - throw new Error('No worker found'); - } else { - return smallestWorker; - } - } - - async init(): Promise { - // Create the worker - const bridge = createBridgeFromLocal(WorkerBridge, {}); - const worker = new Worker({ - bridge, - globalErrorHandlers: false, - }); - - // We make an assumption elsewhere in the code that this is always the first worker - - // Let's use an invariant here for completeness - const id = this.getNextWorkerId(); - if (id !== 0) { - throw new Error('Expected master worker id to be 0'); - } - - const container: WorkerContainer = { - id: 0, - fileCount: 0, - byteCount: 0, - process: undefined, - bridge, - ghost: false, - ready: false, - }; - this.workers.set(0, container); - await worker.init(); - - await Promise.all([this.workerHandshake(container), bridge.handshake()]); - - this.workerStartEvent.send(bridge); - } - - async replaceOwnWorker() { - const lock = this.locker.getNewLock(0); - - try { - const masterWorker = this.getWorkerAssert(0); - this.master.logger.info( - `[WorkerManager] Spawning first worker outside of master after exceeding ${MAX_MASTER_BYTES_BEFORE_WORKERS} bytes`, - ); - this.selfWorker = false; - - // Spawn a new worker - const newWorker = await this.spawnWorker(this.getNextWorkerId(), true); - - // End the old worker, will automatically cleanup - masterWorker.bridge.end(); - - // Swap the workers - - // We perform this as a single atomic operation rather than doing it in spawnWorker so we have predictable worker retrieval - this.workers.set( - 0, - { - id: 0, - fileCount: masterWorker.fileCount, - byteCount: masterWorker.byteCount, - bridge: newWorker.bridge, - process: newWorker.process, - ghost: false, - ready: true, - }, - ); - this.workers.delete(newWorker.id); - } finally { - lock.release(); - } - } - - onNewProject(newProject: ProjectDefinition) { - this.master.projectManager.notifyWorkersOfProjects( - this.getWorkers(), - [newProject], - ); - } - - async workerHandshake(worker: WorkerContainer) { - const {bridge} = worker; - await bridge.handshake({timeout: 3_000}); - await this.master.projectManager.notifyWorkersOfProjects([worker]); - worker.ready = true; - } - - async spawnWorker( - workerId: number, - isGhost: boolean = false, - ): Promise { - const lock = this.locker.getNewLock(workerId); - try { - return await this._spawnWorker(workerId, isGhost); - } finally { - lock.release(); - } - } - - async _spawnWorker( - workerId: number, - isGhost: boolean, - ): Promise { - const start = Date.now(); - - const process = fork('worker'); - - const bridge = createBridgeFromChildProcess( - WorkerBridge, - process, - { - type: 'client', - onSendMessage: (data) => { - this.master.logger.info( - `[WorkerManager] Sending worker request to %s:`, - workerId, - data, - ); - }, - }, - ); - - const worker: WorkerContainer = { - id: workerId, - fileCount: 0, - byteCount: 0, - process, - bridge, - ghost: isGhost, - ready: false, - }; - this.workers.set(workerId, worker); - - process.once( - 'error', - (err) => { - // The process could not be spawned, or - // The process could not be killed, or - // Sending a message to the child process failed. - this.master.onFatalError(err); - process.kill(); - }, - ); - - process.once( - 'exit', - () => { - //bridge.end(`Worker ${String(workerId)} died`); - this.master.onFatalError(new Error(`Worker ${String(workerId)} died`)); - }, - ); - - await this.workerHandshake(worker); - - // If a worker is spawned while we're profiling then make sure it's profiling too - if (this.master.profiling !== undefined) { - await bridge.profilingStart.call(this.master.profiling); - } - - this.workerStartEvent.send(bridge); - - this.master.logger.info( - `[WorkerManager] Worker %s started after %sms`, - workerId, - Date.now() - start, - ); - - return worker; - } - - own(workerId: number, stats: Stats) { - const worker = this.getWorkerAssert(workerId); - worker.byteCount += stats.size; - worker.fileCount++; - } - - disown(workerId: number, stats: Stats) { - const worker = this.getWorkerAssert(workerId); - worker.byteCount -= stats.size; - worker.fileCount--; - } - - async getNextWorker(path: AbsoluteFilePath): Promise { - const {logger, memoryFs, fileAllocator} = this.master; - - // Get stats first - let stats = memoryFs.getFileStats(path); - if (stats === undefined) { - // Give memoryFs a chance to finish initializing if it's in a pending project - await this.master.memoryFs.waitIfInitializingWatch(path); - - stats = memoryFs.getFileStats(path); - if (stats === undefined) { - console.error(Array.from(memoryFs.files.keys(), (path) => path.join())); - throw new Error(`The file ${path.join()} doesn't exist`); - } - } - - // Verify that this file doesn't exceed any size limit - fileAllocator.verifySize(path, stats); - - // Lock in case we're in the process of swapping the master worker with a dedicated worker - await this.locker.waitLock(0); - - // If the worker is running in the master process and we've exceed our byte limit - - // then start up a dedicated worker process - if (this.selfWorker) { - const worker = this.getWorkerAssert(0); - if (worker.byteCount > MAX_MASTER_BYTES_BEFORE_WORKERS) { - await this.replaceOwnWorker(); - } - } - - // Find the worker with the lowest owned byte size - const smallestWorker = this.getLowestByteCountWorker(); - let workerId = smallestWorker.id; - - // When the smallest worker exceeds the max worker byte limit and we're still under - - // our max worker limit, then let's start a new one - if ( - smallestWorker.byteCount > MAX_WORKER_BYTES_BEFORE_ADD && - this.getWorkerCount() < MAX_WORKER_COUNT - ) { - logger.info( - `[WorkerManager] Spawning a new worker as we've exceeded ${MAX_WORKER_BYTES_BEFORE_ADD} bytes across each worker`, - ); - workerId = this.getNextWorkerId(); - await this.spawnWorker(workerId); - } - - // Register size of file - this.own(workerId, stats); - - // Just in case we've chosen a worker that's still spawning - await this.locker.waitLock(workerId); - - return this.getWorkerAssert(workerId); - } + constructor(master: Master) { + this.master = master; + + this.workerStartEvent = new Event({ + name: 'WorkerManager.workerStart', + onError: master.onFatalErrorBound, + }); + this.selfWorker = true; + this.locker = new Locker(); + this.workers = new Map(); + this.idCounter = 0; + } + + master: Master; + locker: Locker; + + selfWorker: boolean; + workerStartEvent: Event; + + workers: Map; + + // We use an idCounter rather than using workers.size due to race conditions + + // If we use workers.size to generate the next id, then by the time we insert it + + // into the map between async operations, it could already be filled! + idCounter: number; + + getNextWorkerId(): number { + return this.idCounter++; + } + + getWorkerAssert(id: number): WorkerContainer { + const worker = this.workers.get(id); + if (worker === undefined) { + throw new Error('Expected worker'); + } + return worker; + } + + getWorkers(): Array { + return Array.from(this.workers.values()); + } + + // Get worker count, excluding ghost workers + getWorkerCount(): number { + let count = 0; + for (const worker of this.workers.values()) { + if (worker.ghost === false) { + count++; + } + } + return count; + } + + // Get all the workers that live in external processes + getExternalWorkers(): Array { + return this.getWorkers().filter((worker) => worker.process !== undefined); + } + + end() { + // Shutdown all workers, no need to clean up any internal data structures since they will never be used + for (const {bridge} of this.workers.values()) { + bridge.end(); + } + } + + getLowestByteCountWorker(): WorkerContainer { + // Find the worker with the lowest byteCount value + let smallestWorker; + let byteCount; + for (const worker of this.workers.values()) { + if ( + !worker.ghost && + (byteCount === undefined || byteCount > worker.byteCount) + ) { + smallestWorker = worker; + byteCount = worker.byteCount; + } + } + + if (smallestWorker === undefined) { + // This shouldn't be possible + throw new Error('No worker found'); + } else { + return smallestWorker; + } + } + + async init(): Promise { + // Create the worker + const bridge = createBridgeFromLocal(WorkerBridge, {}); + const worker = new Worker({ + bridge, + globalErrorHandlers: false, + }); + + // We make an assumption elsewhere in the code that this is always the first worker + + // Let's use an invariant here for completeness + const id = this.getNextWorkerId(); + if (id !== 0) { + throw new Error('Expected master worker id to be 0'); + } + + const container: WorkerContainer = { + id: 0, + fileCount: 0, + byteCount: 0, + process: undefined, + bridge, + ghost: false, + ready: false, + }; + this.workers.set(0, container); + await worker.init(); + + await Promise.all([this.workerHandshake(container), bridge.handshake()]); + + this.workerStartEvent.send(bridge); + } + + async replaceOwnWorker() { + const lock = this.locker.getNewLock(0); + + try { + const masterWorker = this.getWorkerAssert(0); + this.master.logger.info( + `[WorkerManager] Spawning first worker outside of master after exceeding ${MAX_MASTER_BYTES_BEFORE_WORKERS} bytes`, + ); + this.selfWorker = false; + + // Spawn a new worker + const newWorker = await this.spawnWorker(this.getNextWorkerId(), true); + + // End the old worker, will automatically cleanup + masterWorker.bridge.end(); + + // Swap the workers + + // We perform this as a single atomic operation rather than doing it in spawnWorker so we have predictable worker retrieval + this.workers.set( + 0, + { + id: 0, + fileCount: masterWorker.fileCount, + byteCount: masterWorker.byteCount, + bridge: newWorker.bridge, + process: newWorker.process, + ghost: false, + ready: true, + }, + ); + this.workers.delete(newWorker.id); + } finally { + lock.release(); + } + } + + onNewProject(newProject: ProjectDefinition) { + this.master.projectManager.notifyWorkersOfProjects( + this.getWorkers(), + [newProject], + ); + } + + async workerHandshake(worker: WorkerContainer) { + const {bridge} = worker; + await bridge.handshake({timeout: 3_000}); + await this.master.projectManager.notifyWorkersOfProjects([worker]); + worker.ready = true; + } + + async spawnWorker( + workerId: number, + isGhost: boolean = false, + ): Promise { + const lock = this.locker.getNewLock(workerId); + try { + return await this._spawnWorker(workerId, isGhost); + } finally { + lock.release(); + } + } + + async _spawnWorker( + workerId: number, + isGhost: boolean, + ): Promise { + const start = Date.now(); + + const process = fork('worker'); + + const bridge = createBridgeFromChildProcess( + WorkerBridge, + process, + { + type: 'client', + onSendMessage: (data) => { + this.master.logger.info( + `[WorkerManager] Sending worker request to %s:`, + workerId, + data, + ); + }, + }, + ); + + const worker: WorkerContainer = { + id: workerId, + fileCount: 0, + byteCount: 0, + process, + bridge, + ghost: isGhost, + ready: false, + }; + this.workers.set(workerId, worker); + + process.once( + 'error', + (err) => { + // The process could not be spawned, or + // The process could not be killed, or + // Sending a message to the child process failed. + this.master.onFatalError(err); + process.kill(); + }, + ); + + process.once( + 'exit', + () => { + //bridge.end(`Worker ${String(workerId)} died`); + this.master.onFatalError(new Error(`Worker ${String(workerId)} died`)); + }, + ); + + await this.workerHandshake(worker); + + // If a worker is spawned while we're profiling then make sure it's profiling too + if (this.master.profiling !== undefined) { + await bridge.profilingStart.call(this.master.profiling); + } + + this.workerStartEvent.send(bridge); + + this.master.logger.info( + `[WorkerManager] Worker %s started after %sms`, + workerId, + Date.now() - start, + ); + + return worker; + } + + own(workerId: number, stats: Stats) { + const worker = this.getWorkerAssert(workerId); + worker.byteCount += stats.size; + worker.fileCount++; + } + + disown(workerId: number, stats: Stats) { + const worker = this.getWorkerAssert(workerId); + worker.byteCount -= stats.size; + worker.fileCount--; + } + + async getNextWorker(path: AbsoluteFilePath): Promise { + const {logger, memoryFs, fileAllocator} = this.master; + + // Get stats first + let stats = memoryFs.getFileStats(path); + if (stats === undefined) { + // Give memoryFs a chance to finish initializing if it's in a pending project + await this.master.memoryFs.waitIfInitializingWatch(path); + + stats = memoryFs.getFileStats(path); + if (stats === undefined) { + console.error(Array.from(memoryFs.files.keys(), (path) => path.join())); + throw new Error(`The file ${path.join()} doesn't exist`); + } + } + + // Verify that this file doesn't exceed any size limit + fileAllocator.verifySize(path, stats); + + // Lock in case we're in the process of swapping the master worker with a dedicated worker + await this.locker.waitLock(0); + + // If the worker is running in the master process and we've exceed our byte limit + + // then start up a dedicated worker process + if (this.selfWorker) { + const worker = this.getWorkerAssert(0); + if (worker.byteCount > MAX_MASTER_BYTES_BEFORE_WORKERS) { + await this.replaceOwnWorker(); + } + } + + // Find the worker with the lowest owned byte size + const smallestWorker = this.getLowestByteCountWorker(); + let workerId = smallestWorker.id; + + // When the smallest worker exceeds the max worker byte limit and we're still under + + // our max worker limit, then let's start a new one + if ( + smallestWorker.byteCount > MAX_WORKER_BYTES_BEFORE_ADD && + this.getWorkerCount() < MAX_WORKER_COUNT + ) { + logger.info( + `[WorkerManager] Spawning a new worker as we've exceeded ${MAX_WORKER_BYTES_BEFORE_ADD} bytes across each worker`, + ); + workerId = this.getNextWorkerId(); + await this.spawnWorker(workerId); + } + + // Register size of file + this.own(workerId, stats); + + // Just in case we've chosen a worker that's still spawning + await this.locker.waitLock(workerId); + + return this.getWorkerAssert(workerId); + } } diff --git a/packages/@romejs/core/master/WorkerQueue.ts b/packages/@romejs/core/master/WorkerQueue.ts index cc4837ebb4b..73f29c7bd68 100644 --- a/packages/@romejs/core/master/WorkerQueue.ts +++ b/packages/@romejs/core/master/WorkerQueue.ts @@ -12,113 +12,111 @@ import {AbsoluteFilePath} from '@romejs/path'; type Queue = Array<[AbsoluteFilePath, M]>; type WorkerQueueItem = { - running: boolean; - queue: Queue; + running: boolean; + queue: Queue; }; type Callback = ( - path: AbsoluteFilePath, - metadata: M, + path: AbsoluteFilePath, + metadata: M, ) => undefined | Promise; export default class WorkerQueue { - constructor(master: Master, maxPer: number = 2) { - this.master = master; - this.callbacks = []; - this.runningWorkers = []; - this.workers = new Map(); - this.open = true; - this.maxPer = maxPer; - } - - master: Master; - maxPer: number; - runningWorkers: Array>; - callbacks: Array>; - workers: Map>; - open: boolean; - - async pushQueue(path: AbsoluteFilePath, metadata: M) { - if (!this.open) { - throw new Error('WorkerQueue has already closed'); - } - - if (this.callbacks.length === 0) { - throw new Error('No callbacks attached to queue'); - } - - const workerContainer = await this.master.fileAllocator.getOrAssignOwner( - path, - ); - - // Populate the worker queue for this item - let worker = this.workers.get(workerContainer); - if (worker === undefined) { - worker = { - running: false, - queue: [], - }; - this.workers.set(workerContainer, worker); - } - worker.queue.push([path, metadata]); - - // Start this worker if it isn't already - if (worker.running === false) { - const promise = this.processWorker(worker); - // Add a `catch` so that we aren't considered an unhandled promise if it rejects before a handler is attached - promise.catch(() => {}); - this.runningWorkers.push(promise); - } - } - - addCallback(callback: Callback) { - this.callbacks.push(callback); - } - - async processWorker(worker: WorkerQueueItem) { - worker.running = true; - - const {queue} = worker; - - const next = async () => { - const item = queue.shift(); - if (item === undefined) { - // Exhausted queue - return; - } - - const [filename, metadata] = item; - for (const callback of this.callbacks) { - await callback(filename, metadata); - } - await next(); - }; - - while (queue.length > 0) { - // "threads" - const threads = []; - for (let i = 0; i < this.maxPer; i++) { - threads.push(next()); - } - await Promise.all(threads); - } - - worker.running = false; - } - - async spin() { - while ( - // Keep consuming all the promises until we're exhausted - this.runningWorkers.length > - 0 - ) { - const {runningWorkers} = this; - this.runningWorkers = []; - await Promise.all(runningWorkers); - } - - // Ensure we never receive anymore queue items - - this.open = false; - } + constructor(master: Master, maxPer: number = 2) { + this.master = master; + this.callbacks = []; + this.runningWorkers = []; + this.workers = new Map(); + this.open = true; + this.maxPer = maxPer; + } + + master: Master; + maxPer: number; + runningWorkers: Array>; + callbacks: Array>; + workers: Map>; + open: boolean; + + async pushQueue(path: AbsoluteFilePath, metadata: M) { + if (!this.open) { + throw new Error('WorkerQueue has already closed'); + } + + if (this.callbacks.length === 0) { + throw new Error('No callbacks attached to queue'); + } + + const workerContainer = await this.master.fileAllocator.getOrAssignOwner(path); + + // Populate the worker queue for this item + let worker = this.workers.get(workerContainer); + if (worker === undefined) { + worker = { + running: false, + queue: [], + }; + this.workers.set(workerContainer, worker); + } + worker.queue.push([path, metadata]); + + // Start this worker if it isn't already + if (worker.running === false) { + const promise = this.processWorker(worker); + // Add a `catch` so that we aren't considered an unhandled promise if it rejects before a handler is attached + promise.catch(() => {}); + this.runningWorkers.push(promise); + } + } + + addCallback(callback: Callback) { + this.callbacks.push(callback); + } + + async processWorker(worker: WorkerQueueItem) { + worker.running = true; + + const {queue} = worker; + + const next = async () => { + const item = queue.shift(); + if (item === undefined) { + // Exhausted queue + return; + } + + const [filename, metadata] = item; + for (const callback of this.callbacks) { + await callback(filename, metadata); + } + await next(); + }; + + while (queue.length > 0) { + // "threads" + const threads = []; + for (let i = 0; i < this.maxPer; i++) { + threads.push(next()); + } + await Promise.all(threads); + } + + worker.running = false; + } + + async spin() { + while ( + // Keep consuming all the promises until we're exhausted + this.runningWorkers.length > + 0 + ) { + const {runningWorkers} = this; + this.runningWorkers = []; + await Promise.all(runningWorkers); + } + + // Ensure we never receive anymore queue items + + this.open = false; + } } diff --git a/packages/@romejs/core/master/bundler/BundleRequest.ts b/packages/@romejs/core/master/bundler/BundleRequest.ts index 520da12601a..5c38525b70f 100644 --- a/packages/@romejs/core/master/bundler/BundleRequest.ts +++ b/packages/@romejs/core/master/bundler/BundleRequest.ts @@ -10,14 +10,14 @@ import DependencyNode from '../dependencies/DependencyNode'; import {Mappings, SourceMapGenerator} from '@romejs/codec-source-map'; import {BundleRequestResult, BundlerMode} from '../../common/types/bundler'; import { - WorkerBundleCompileOptions, - WorkerCompileResult, + WorkerBundleCompileOptions, + WorkerCompileResult, } from '../../common/bridges/WorkerBridge'; import {DependencyOrder} from '../dependencies/DependencyOrderer'; import { - BundleCompileResolvedImports, - CompileResult, - getPrefixedBundleNamespace, + BundleCompileResolvedImports, + CompileResult, + getPrefixedBundleNamespace, } from '@romejs/js-compiler'; import {DiagnosticsProcessor, descriptions} from '@romejs/diagnostics'; @@ -31,268 +31,268 @@ import {Reporter} from '@romejs/cli-reporter'; import WorkerQueue from '../WorkerQueue'; export type BundleOptions = { - prefix?: string; - interpreter?: string; - deferredSourceMaps?: boolean; + prefix?: string; + interpreter?: string; + deferredSourceMaps?: boolean; }; export default class BundleRequest { - constructor( - { - bundler, - reporter, - mode, - resolvedEntry, - options, - }: { - bundler: Bundler; - reporter: Reporter; - mode: BundlerMode; - resolvedEntry: AbsoluteFilePath; - options: BundleOptions; - }, - ) { - this.options = options; - this.reporter = reporter; - this.bundler = bundler; - this.cached = true; - this.mode = mode; - - this.resolvedEntry = resolvedEntry; - this.resolvedEntryUid = bundler.master.projectManager.getUid(resolvedEntry); - - this.diagnostics = bundler.request.createDiagnosticsProcessor({ - origins: [ - { - category: 'bundler', - message: `Requested bundle for `, - }, - ], - }); - this.diagnostics.addAllowedUnusedSuppressionPrefix('lint'); - - this.compiles = new Map(); - this.assets = new Map(); - - this.sourceMap = new SourceMapGenerator({ - file: resolvedEntry.getBasename(), - }); - } - - options: BundleOptions; - cached: boolean; - reporter: Reporter; - bundler: Bundler; - resolvedEntry: AbsoluteFilePath; - resolvedEntryUid: string; - diagnostics: DiagnosticsProcessor; - assets: Map; - compiles: Map; - sourceMap: SourceMapGenerator; - mode: BundlerMode; - - async stepAnalyze(): Promise { - const {graph} = this.bundler; - const {reporter} = this; - - const analyzeProgress = reporter.progress({ - name: `bundler:analyze:${this.resolvedEntryUid}`, - title: 'Analyzing', - }); - this.diagnostics.setThrowAfter(100); - try { - await graph.seed({ - paths: [this.resolvedEntry], - diagnosticsProcessor: this.diagnostics, - analyzeProgress, - validate: true, - }); - } finally { - analyzeProgress.end(); - } - - return this.bundler.graph.getNode(this.resolvedEntry).getDependencyOrder(); - } - - async stepCompile(paths: Array) { - const {master} = this.bundler; - const {reporter} = this; - this.diagnostics.setThrowAfter(undefined); - - const compilingSpinner = reporter.progress({ - name: `bundler:compile:${this.resolvedEntryUid}`, - title: 'Compiling', - }); - compilingSpinner.setTotal(paths.length); - - const queue: WorkerQueue = new WorkerQueue(master); - - queue.addCallback(async (path) => { - const progressText = ``; - compilingSpinner.pushText(progressText); - await this.compileJS(path); - compilingSpinner.tick(); - compilingSpinner.popText(progressText); - }); - - for (const path of paths) { - await queue.pushQueue(path); - } - - await queue.spin(); - compilingSpinner.end(); - } - - async compileJS(path: AbsoluteFilePath): Promise { - const {graph} = this.bundler; - - const source = path.join(); - const mod = graph.getNode(path); - - // Build a map of relative module sources to module id - const relativeSourcesToModuleId: Dict = {}; - for (const [relative, absolute] of mod.relativeToAbsolutePath) { - const moduleId = graph.getNode(absolute).uid; - relativeSourcesToModuleId[relative] = moduleId; - } - - // Diagnostics would have already been added during the initial DependencyGraph.seed - // We're doing the work of resolving everything again, maybe we should cache it? - const resolvedImports: BundleCompileResolvedImports = mod.resolveImports().resolved; - - let assetPath: undefined | string; - if (mod.handler?.isAsset) { - const buffer = await readFile(mod.path); - - // Asset path in the form of: BASENAME-SHA1HASH.EXTENSIONS - const hash = crypto.createHash('sha1').update(buffer).digest('hex'); - const basename = mod.path.getExtensionlessBasename(); - const exts = mod.path.getExtensions(); - - assetPath = `${basename}-${hash}${exts}`; - this.assets.set(assetPath, buffer); - } - - const opts: WorkerBundleCompileOptions = { - mode: this.mode, - moduleAll: mod.all, - moduleId: mod.uid, - relativeSourcesToModuleId, - resolvedImports, - assetPath, - }; - - const lock = await this.bundler.compileLocker.getLock(source); - - const res: WorkerCompileResult = await this.bundler.request.requestWorkerCompile( - path, - 'compileForBundle', - { - bundle: opts, - }, - {}, - ); - - lock.release(); - - if (!res.cached) { - this.cached = false; - } - - this.diagnostics.addSuppressions(res.suppressions); - this.diagnostics.addDiagnostics(res.diagnostics); - - this.compiles.set(source, res); - return res; - } - - stepCombine( - order: DependencyOrder, - forceSourceMaps: boolean, - ): BundleRequestResult { - const {files} = order; - const {inlineSourceMap} = this.bundler.config; - const {graph} = this.bundler; - const {resolvedEntry, mode, sourceMap} = this; - - // We allow deferring the generation of source maps. We don't do this by default as it's slower than generating them upfront - // which is what most callers need. But for things like tests, we want to lazily compute the source map only when diagnostics - // are present. - let deferredSourceMaps = - !forceSourceMaps && this.options.deferredSourceMaps === true; - if (deferredSourceMaps) { - sourceMap.addMaterializer(() => { - this.stepCombine(order, true); - }); - } - - let content: string = ''; - let lineOffset: number = 0; - - function push(str: string) { - str += '\n'; - content += str; - if (!deferredSourceMaps) { - for (let cha of str) { - if (cha === '\n') { - lineOffset++; - } - } - } - } - - function addMappings( - filename: string, - sourceContent: string, - mappings: Mappings, - ) { - if (deferredSourceMaps) { - return; - } - - sourceMap.setSourceContent(filename, sourceContent); - for (const mapping of mappings) { - sourceMap.addMapping({ - ...mapping, - generated: { - ...mapping.generated, - line: ob1Add(lineOffset, mapping.generated.line), - }, - }); - } - } - - const {interpreter} = this.options; - if (interpreter !== undefined) { - push(`#!${interpreter}\n`); - } - - // add on bootstrap - if (order.firstTopAwaitLocations.length > 0) { - if (mode === 'legacy') { - for (const {loc, mtime} of order.firstTopAwaitLocations) { - this.diagnostics.addDiagnostic({ - description: descriptions.BUNDLER.TOP_LEVEL_AWAIT_IN_LEGACY, - location: { - ...loc, - mtime, - }, - }); - } - } - - push(`(async function(global) {`); - } else { - push(`(function(global) {`); - } - - if (mode === 'modern') { - push(` 'use strict';`); - } - - // TODO prelude - - /* + constructor( + { + bundler, + reporter, + mode, + resolvedEntry, + options, + }: { + bundler: Bundler; + reporter: Reporter; + mode: BundlerMode; + resolvedEntry: AbsoluteFilePath; + options: BundleOptions; + }, + ) { + this.options = options; + this.reporter = reporter; + this.bundler = bundler; + this.cached = true; + this.mode = mode; + + this.resolvedEntry = resolvedEntry; + this.resolvedEntryUid = bundler.master.projectManager.getUid(resolvedEntry); + + this.diagnostics = bundler.request.createDiagnosticsProcessor({ + origins: [ + { + category: 'bundler', + message: `Requested bundle for `, + }, + ], + }); + this.diagnostics.addAllowedUnusedSuppressionPrefix('lint'); + + this.compiles = new Map(); + this.assets = new Map(); + + this.sourceMap = new SourceMapGenerator({ + file: resolvedEntry.getBasename(), + }); + } + + options: BundleOptions; + cached: boolean; + reporter: Reporter; + bundler: Bundler; + resolvedEntry: AbsoluteFilePath; + resolvedEntryUid: string; + diagnostics: DiagnosticsProcessor; + assets: Map; + compiles: Map; + sourceMap: SourceMapGenerator; + mode: BundlerMode; + + async stepAnalyze(): Promise { + const {graph} = this.bundler; + const {reporter} = this; + + const analyzeProgress = reporter.progress({ + name: `bundler:analyze:${this.resolvedEntryUid}`, + title: 'Analyzing', + }); + this.diagnostics.setThrowAfter(100); + try { + await graph.seed({ + paths: [this.resolvedEntry], + diagnosticsProcessor: this.diagnostics, + analyzeProgress, + validate: true, + }); + } finally { + analyzeProgress.end(); + } + + return this.bundler.graph.getNode(this.resolvedEntry).getDependencyOrder(); + } + + async stepCompile(paths: Array) { + const {master} = this.bundler; + const {reporter} = this; + this.diagnostics.setThrowAfter(undefined); + + const compilingSpinner = reporter.progress({ + name: `bundler:compile:${this.resolvedEntryUid}`, + title: 'Compiling', + }); + compilingSpinner.setTotal(paths.length); + + const queue: WorkerQueue = new WorkerQueue(master); + + queue.addCallback(async (path) => { + const progressText = ``; + compilingSpinner.pushText(progressText); + await this.compileJS(path); + compilingSpinner.tick(); + compilingSpinner.popText(progressText); + }); + + for (const path of paths) { + await queue.pushQueue(path); + } + + await queue.spin(); + compilingSpinner.end(); + } + + async compileJS(path: AbsoluteFilePath): Promise { + const {graph} = this.bundler; + + const source = path.join(); + const mod = graph.getNode(path); + + // Build a map of relative module sources to module id + const relativeSourcesToModuleId: Dict = {}; + for (const [relative, absolute] of mod.relativeToAbsolutePath) { + const moduleId = graph.getNode(absolute).uid; + relativeSourcesToModuleId[relative] = moduleId; + } + + // Diagnostics would have already been added during the initial DependencyGraph.seed + // We're doing the work of resolving everything again, maybe we should cache it? + const resolvedImports: BundleCompileResolvedImports = mod.resolveImports().resolved; + + let assetPath: undefined | string; + if (mod.handler?.isAsset) { + const buffer = await readFile(mod.path); + + // Asset path in the form of: BASENAME-SHA1HASH.EXTENSIONS + const hash = crypto.createHash('sha1').update(buffer).digest('hex'); + const basename = mod.path.getExtensionlessBasename(); + const exts = mod.path.getExtensions(); + + assetPath = `${basename}-${hash}${exts}`; + this.assets.set(assetPath, buffer); + } + + const opts: WorkerBundleCompileOptions = { + mode: this.mode, + moduleAll: mod.all, + moduleId: mod.uid, + relativeSourcesToModuleId, + resolvedImports, + assetPath, + }; + + const lock = await this.bundler.compileLocker.getLock(source); + + const res: WorkerCompileResult = await this.bundler.request.requestWorkerCompile( + path, + 'compileForBundle', + { + bundle: opts, + }, + {}, + ); + + lock.release(); + + if (!res.cached) { + this.cached = false; + } + + this.diagnostics.addSuppressions(res.suppressions); + this.diagnostics.addDiagnostics(res.diagnostics); + + this.compiles.set(source, res); + return res; + } + + stepCombine( + order: DependencyOrder, + forceSourceMaps: boolean, + ): BundleRequestResult { + const {files} = order; + const {inlineSourceMap} = this.bundler.config; + const {graph} = this.bundler; + const {resolvedEntry, mode, sourceMap} = this; + + // We allow deferring the generation of source maps. We don't do this by default as it's slower than generating them upfront + // which is what most callers need. But for things like tests, we want to lazily compute the source map only when diagnostics + // are present. + let deferredSourceMaps = + !forceSourceMaps && this.options.deferredSourceMaps === true; + if (deferredSourceMaps) { + sourceMap.addMaterializer(() => { + this.stepCombine(order, true); + }); + } + + let content: string = ''; + let lineOffset: number = 0; + + function push(str: string) { + str += '\n'; + content += str; + if (!deferredSourceMaps) { + for (let cha of str) { + if (cha === '\n') { + lineOffset++; + } + } + } + } + + function addMappings( + filename: string, + sourceContent: string, + mappings: Mappings, + ) { + if (deferredSourceMaps) { + return; + } + + sourceMap.setSourceContent(filename, sourceContent); + for (const mapping of mappings) { + sourceMap.addMapping({ + ...mapping, + generated: { + ...mapping.generated, + line: ob1Add(lineOffset, mapping.generated.line), + }, + }); + } + } + + const {interpreter} = this.options; + if (interpreter !== undefined) { + push(`#!${interpreter}\n`); + } + + // add on bootstrap + if (order.firstTopAwaitLocations.length > 0) { + if (mode === 'legacy') { + for (const {loc, mtime} of order.firstTopAwaitLocations) { + this.diagnostics.addDiagnostic({ + description: descriptions.BUNDLER.TOP_LEVEL_AWAIT_IN_LEGACY, + location: { + ...loc, + mtime, + }, + }); + } + } + + push(`(async function(global) {`); + } else { + push(`(function(global) {`); + } + + if (mode === 'modern') { + push(` 'use strict';`); + } + + // TODO prelude + + /* const path = createAbsoluteFilePath(loc); const res = await this.bundler.request.requestWorkerCompile( path, @@ -307,100 +307,100 @@ export default class BundleRequest { push(res.code); push('})();'); */ - const declaredCJS: Set = new Set(); - function declareCJS(module: DependencyNode) { - if (mode !== 'modern' || module.type !== 'cjs' || declaredCJS.has(module)) { - return; - } - - declaredCJS.add(module); - - push(` var ${getPrefixedBundleNamespace(module.uid)} = {};`); - } - - // Add on files - for (const source of files) { - const module = graph.getNode(source); - - for (const path of module.getAbsoluteDependencies()) { - declareCJS(graph.getNode(path)); - } - - const compileResult = this.compiles.get(source.join()); - if (compileResult === undefined) { - continue; - throw new Error('Expected compile result'); - } - - // Only do this in modern mode, the module id will already be in the wrapper otherwise - if (mode === 'modern') { - push(` // ${module.uid}`); - } - - declareCJS(module); - - addMappings(module.uid, compileResult.sourceText, compileResult.mappings); - push(compileResult.compiledCode); - push(''); - } - - // push on initial entry require - const entryModule = graph.getNode(resolvedEntry); - if (mode === 'modern') { - push(` return ${getPrefixedBundleNamespace(entryModule.uid)};`); - } else { - push(` return Rome.requireNamespace("${entryModule.uid}");`); - } - - // push footer - push( - "})(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this);", - ); - - // - if (inlineSourceMap === true) { - const sourceMapComment = sourceMap.toComment(); - content += sourceMapComment; - } else { - content += `//# sourceMappingURL=${this.sourceMap.file}.map`; - } - - return { - diagnostics: this.diagnostics.getDiagnostics(), - content, - sourceMap: this.sourceMap, - cached: this.cached, - assets: this.assets, - }; - } - - shouldAbort(): boolean { - return this.diagnostics.hasDiagnostics(); - } - - abort(): BundleRequestResult { - return { - sourceMap: this.sourceMap, - content: '', - diagnostics: this.diagnostics.getDiagnostics(), - cached: false, - assets: this.assets, - }; - } - - async bundle(): Promise { - const order = await this.stepAnalyze(); - if (this.shouldAbort()) { - return this.abort(); - } - - // Compile - await this.stepCompile(order.files); - if (this.shouldAbort()) { - return this.abort(); - } - - // Combine - return await this.stepCombine(order, false); - } + const declaredCJS: Set = new Set(); + function declareCJS(module: DependencyNode) { + if (mode !== 'modern' || module.type !== 'cjs' || declaredCJS.has(module)) { + return; + } + + declaredCJS.add(module); + + push(` var ${getPrefixedBundleNamespace(module.uid)} = {};`); + } + + // Add on files + for (const source of files) { + const module = graph.getNode(source); + + for (const path of module.getAbsoluteDependencies()) { + declareCJS(graph.getNode(path)); + } + + const compileResult = this.compiles.get(source.join()); + if (compileResult === undefined) { + continue; + throw new Error('Expected compile result'); + } + + // Only do this in modern mode, the module id will already be in the wrapper otherwise + if (mode === 'modern') { + push(` // ${module.uid}`); + } + + declareCJS(module); + + addMappings(module.uid, compileResult.sourceText, compileResult.mappings); + push(compileResult.compiledCode); + push(''); + } + + // push on initial entry require + const entryModule = graph.getNode(resolvedEntry); + if (mode === 'modern') { + push(` return ${getPrefixedBundleNamespace(entryModule.uid)};`); + } else { + push(` return Rome.requireNamespace("${entryModule.uid}");`); + } + + // push footer + push( + "})(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this);", + ); + + // + if (inlineSourceMap === true) { + const sourceMapComment = sourceMap.toComment(); + content += sourceMapComment; + } else { + content += `//# sourceMappingURL=${this.sourceMap.file}.map`; + } + + return { + diagnostics: this.diagnostics.getDiagnostics(), + content, + sourceMap: this.sourceMap, + cached: this.cached, + assets: this.assets, + }; + } + + shouldAbort(): boolean { + return this.diagnostics.hasDiagnostics(); + } + + abort(): BundleRequestResult { + return { + sourceMap: this.sourceMap, + content: '', + diagnostics: this.diagnostics.getDiagnostics(), + cached: false, + assets: this.assets, + }; + } + + async bundle(): Promise { + const order = await this.stepAnalyze(); + if (this.shouldAbort()) { + return this.abort(); + } + + // Compile + await this.stepCompile(order.files); + if (this.shouldAbort()) { + return this.abort(); + } + + // Combine + return await this.stepCombine(order, false); + } } diff --git a/packages/@romejs/core/master/bundler/Bundler.ts b/packages/@romejs/core/master/bundler/Bundler.ts index a620bfd6deb..9eb5511a089 100644 --- a/packages/@romejs/core/master/bundler/Bundler.ts +++ b/packages/@romejs/core/master/bundler/Bundler.ts @@ -8,19 +8,19 @@ import {Master, MasterRequest} from '@romejs/core'; import {Reporter} from '@romejs/cli-reporter'; import { - BundleResult, - BundleResultBundle, - BundlerConfig, - BundlerFiles, - BundlerMode, + BundleResult, + BundleResultBundle, + BundlerConfig, + BundlerFiles, + BundlerMode, } from '../../common/types/bundler'; import DependencyGraph from '../dependencies/DependencyGraph'; import BundleRequest, {BundleOptions} from './BundleRequest'; import {AbsoluteFilePath, createUnknownFilePath} from '@romejs/path'; import { - JSONManifest, - ManifestDefinition, - convertManifestToJSON, + JSONManifest, + ManifestDefinition, + convertManifestToJSON, } from '@romejs/codec-js-manifest'; import {WorkerCompileResult} from '../../common/bridges/WorkerBridge'; import {Dict} from '@romejs/typescript-helpers'; @@ -28,399 +28,396 @@ import {readFile} from '@romejs/fs'; import {flipPathPatterns} from '@romejs/path-match'; import {markup} from '@romejs/string-markup'; import Locker from '@romejs/core/common/utils/Locker'; +import {stringifyJSON} from '@romejs/codec-json'; export type BundlerEntryResoluton = { - manifestDef: undefined | ManifestDefinition; - resolvedEntry: AbsoluteFilePath; + manifestDef: undefined | ManifestDefinition; + resolvedEntry: AbsoluteFilePath; }; export default class Bundler { - constructor(req: MasterRequest, config: BundlerConfig) { - this.config = config; - this.master = req.master; - this.reporter = req.reporter; - this.request = req; - - this.entries = []; - - this.compileLocker = new Locker(); - this.graph = new DependencyGraph(req, config.resolver); - } - - compileLocker: Locker; - graph: DependencyGraph; - master: Master; - request: MasterRequest; - reporter: Reporter; - entries: Array; - config: BundlerConfig; - - static createFromMasterRequest(req: MasterRequest): Bundler { - return new Bundler(req, req.getBundlerConfigFromFlags()); - } - - async getResolvedEntry( - unresolvedEntry: string, - ): Promise { - const {cwd} = this.config; - - const res = await this.master.resolver.resolveEntryAssert({ - ...this.config.resolver, - origin: cwd, - source: createUnknownFilePath(unresolvedEntry), - }); - - const {master} = this; - const resolvedEntry = res.path; - - // Now do the same resolver request but with a package - const manifestRootResolved = master.resolver.resolveLocal({ - ...this.config.resolver, - origin: cwd, - requestedType: 'package', - source: createUnknownFilePath(unresolvedEntry), - }); - const manifestRoot: undefined | AbsoluteFilePath = - manifestRootResolved.type === 'FOUND' - ? manifestRootResolved.path - : undefined; - let manifestDef; - if (manifestRoot !== undefined) { - const def = master.memoryFs.getManifestDefinition(manifestRoot); - if (def !== undefined) { - manifestDef = def; - } - } - - return {manifestDef, resolvedEntry}; - } - - createBundleRequest( - resolvedEntry: AbsoluteFilePath, - options: BundleOptions, - reporter: Reporter, - ): BundleRequest { - const project = this.master.projectManager.assertProjectExisting( - resolvedEntry, - ); - const mode: BundlerMode = project.config.bundler.mode; - - this.entries.push(resolvedEntry); - return new BundleRequest({ - bundler: this, - mode, - resolvedEntry, - options, - reporter, - }); - } - - async compile(path: AbsoluteFilePath): Promise { - const bundleRequest = this.createBundleRequest(path, {}, this.reporter); - await bundleRequest.stepAnalyze(); - bundleRequest.diagnostics.maybeThrowDiagnosticsError(); - return await bundleRequest.compileJS(path); - } - - // This will take multiple entry points and do some magic to make them more efficient to build in parallel - async bundleMultiple( - entries: Array, - options: BundleOptions = {}, - ): Promise> { - // Clone so we can mess with it - entries = [...entries]; - - // Seed the dependency graph with all the entries at the same time - const processor = this.request.createDiagnosticsProcessor({ - origins: [ - { - category: 'Bundler', - message: 'Analyzing dependencies for bundleMultiple', - }, - ], - }); - const entryUids = entries.map((entry) => - this.master.projectManager.getUid(entry) - ); - const analyzeProgress = this.reporter.progress({ - name: `bundler:analyze:${entryUids.join(',')}`, - title: 'Analyzing', - }); - processor.setThrowAfter(100); - await this.graph.seed({ - paths: entries, - diagnosticsProcessor: processor, - analyzeProgress, - validate: false, - }); - analyzeProgress.end(); - processor.maybeThrowDiagnosticsError(); - - // Now actually bundle them - const map: Map = new Map(); - - const progress = this.reporter.progress({title: 'Bundling'}); - progress.setTotal(entries.length); - - const silentReporter = this.reporter.fork({ - streams: [], - }); - - const promises: Set> = new Set(); - - // Could maybe do some of this in parallel? - while (entries.length > 0) { - const entry = entries.shift()!; - - const promise = (async () => { - const text = markup``; - progress.pushText(text); - map.set(entry, await this.bundle(entry, options, silentReporter)); - progress.popText(text); - progress.tick(); - })(); - promise.then(() => { - promises.delete(promise); - }); - promises.add(promise); - - if (promises.size > 5) { - await Promise.race(Array.from(promises)); - } - } - - await Promise.all(Array.from(promises)); - - progress.end(); - - return map; - } - - async bundleManifest({resolvedEntry, manifestDef}: BundlerEntryResoluton) { - let bundles: Array = []; - const files: BundlerFiles = new Map(); - - const createBundle = async ( - resolvedSegment: AbsoluteFilePath, - options: BundleOptions, - ): Promise => { - const bundle = await this.bundle(resolvedSegment, options); - for (const [path, content] of bundle.files) { - files.set(path, content); - } - bundles = bundles.concat(bundle.bundles); - return bundle.entry; - }; - - const entryBundle = await createBundle(resolvedEntry, {}); - - // - const bundleBuddyStats = this.graph.getBundleBuddyStats(this.entries); - files.set( - 'bundlebuddy.json', - { - kind: 'stats', - content: () => JSON.stringify(bundleBuddyStats, null, ' '), - }, - ); - - // TODO ensure that __dirname is relative to the project root - if (manifestDef !== undefined) { - const newManifest = await this.deriveManifest( - manifestDef, - entryBundle, - createBundle, - (relative, buffer) => { - if (!files.has(relative)) { - files.set( - relative, - { - kind: 'file', - content: () => buffer, - }, - ); - } - }, - ); - - // If we have a `files` array then set it to all the newly added files - // This will have included files already there that we copied - if (newManifest.files !== undefined) { - newManifest.files = Array.from(files.keys()); - } - - // Add a package.json with updated values - files.set( - 'package.json', - { - kind: 'manifest', - content: () => JSON.stringify(newManifest, undefined, ' '), - }, - ); - } - - return { - files, - bundles, - entry: entryBundle, - }; - } - - async deriveManifest( - manifestDef: ManifestDefinition, - entryBundle: BundleResultBundle, - createBundle: ( - resolvedSegment: AbsoluteFilePath, - options: BundleOptions, - ) => Promise, - addFile: (relative: string, buffer: Buffer | string) => void, - ): Promise { - // TODO figure out some way to use bundleMultiple here - const manifest = manifestDef.manifest; - - const newManifest: JSONManifest = { - ...convertManifestToJSON(manifest), - main: entryBundle.js.path, - }; - - // TODO inherit some manifest properties from project configs - const project = this.master.projectManager.findProjectExisting( - manifestDef.folder, - ); - if (project !== undefined) { - if (newManifest.name === undefined) { - newManifest.name = project.config.name; - } - } - - // TODO remove dependencies fields, probably? - - // TODO Compile a index.d.ts - - // Copy manifest.files - if (manifest.files !== undefined) { - const paths = await this.master.memoryFs.glob( - manifestDef.folder, - { - overrideIgnore: flipPathPatterns(manifest.files), - }, - ); - - for (const path of paths) { - const relative = manifestDef.folder.relative(path).join(); - const buffer = await readFile(path); - addFile(relative, buffer); - } - } - - // Compile manifest.bin files - const bin = manifest.bin; - if (bin !== undefined) { - const newBin: Dict = {}; - newManifest.bin = newBin; - - const binConsumer = manifestDef.consumer.get('bin'); - const isBinShorthand = typeof binConsumer.asUnknown() === 'string'; - - for (const [binName, relative] of manifest.bin) { - const location = (isBinShorthand - ? binConsumer - : binConsumer.get(binName)).getDiagnosticLocation('inner-value'); - - const absolute = await this.master.resolver.resolveAssert( - { - ...this.config.resolver, - origin: manifestDef.folder, - source: createUnknownFilePath(relative).toExplicitRelative(), - }, - { - location, - }, - ); - - const res = await createBundle( - absolute.path, - { - prefix: `bin/${binName}`, - interpreter: '/usr/bin/env node', - }, - ); - newBin[binName] = res.js.path; - } - } - - // TODO `{type: "module"}` will always fail since we've produced CJS bundles - // rome-ignore lint/noDelete - delete newManifest.type; - - return newManifest; - } - - async bundle( - resolvedEntry: AbsoluteFilePath, - options: BundleOptions = {}, - reporter: Reporter = this.reporter, - ): Promise { - reporter.info( - markup`Bundling `, - ); - - const req = this.createBundleRequest(resolvedEntry, options, reporter); - const res = await req.bundle(); - - const processor = this.request.createDiagnosticsProcessor(); - processor.addDiagnostics(res.diagnostics); - processor.maybeThrowDiagnosticsError(); - - if (res.cached) { - reporter.warn('Bundle was built completely from cache'); - } - - const prefix = options.prefix === undefined ? '' : `${options.prefix}/`; - const jsPath = `${prefix}index.js`; - const mapPath = `${jsPath}.map`; - - const files: BundlerFiles = new Map(); - files.set( - jsPath, - { - kind: 'entry', - content: () => res.content, - }, - ); - - files.set( - mapPath, - { - kind: 'sourcemap', - content: () => res.sourceMap.toJSON(), - }, - ); - - for (const [relative, buffer] of res.assets) { - files.set( - relative, - { - kind: 'asset', - content: () => buffer, - }, - ); - } - - const bundle: BundleResultBundle = { - js: { - path: jsPath, - content: res.content, - }, - sourceMap: { - path: mapPath, - map: res.sourceMap, - }, - }; - return { - entry: bundle, - bundles: [bundle], - files, - }; - } + constructor(req: MasterRequest, config: BundlerConfig) { + this.config = config; + this.master = req.master; + this.reporter = req.reporter; + this.request = req; + + this.entries = []; + + this.compileLocker = new Locker(); + this.graph = new DependencyGraph(req, config.resolver); + } + + compileLocker: Locker; + graph: DependencyGraph; + master: Master; + request: MasterRequest; + reporter: Reporter; + entries: Array; + config: BundlerConfig; + + static createFromMasterRequest(req: MasterRequest): Bundler { + return new Bundler(req, req.getBundlerConfigFromFlags()); + } + + async getResolvedEntry(unresolvedEntry: string): Promise { + const {cwd} = this.config; + + const res = await this.master.resolver.resolveEntryAssert({ + ...this.config.resolver, + origin: cwd, + source: createUnknownFilePath(unresolvedEntry), + }); + + const {master} = this; + const resolvedEntry = res.path; + + // Now do the same resolver request but with a package + const manifestRootResolved = master.resolver.resolveLocal({ + ...this.config.resolver, + origin: cwd, + requestedType: 'package', + source: createUnknownFilePath(unresolvedEntry), + }); + const manifestRoot: undefined | AbsoluteFilePath = + manifestRootResolved.type === 'FOUND' ? manifestRootResolved.path : undefined; + let manifestDef; + if (manifestRoot !== undefined) { + const def = master.memoryFs.getManifestDefinition(manifestRoot); + if (def !== undefined) { + manifestDef = def; + } + } + + return {manifestDef, resolvedEntry}; + } + + createBundleRequest( + resolvedEntry: AbsoluteFilePath, + options: BundleOptions, + reporter: Reporter, + ): BundleRequest { + const project = this.master.projectManager.assertProjectExisting( + resolvedEntry, + ); + const mode: BundlerMode = project.config.bundler.mode; + + this.entries.push(resolvedEntry); + return new BundleRequest({ + bundler: this, + mode, + resolvedEntry, + options, + reporter, + }); + } + + async compile(path: AbsoluteFilePath): Promise { + const bundleRequest = this.createBundleRequest(path, {}, this.reporter); + await bundleRequest.stepAnalyze(); + bundleRequest.diagnostics.maybeThrowDiagnosticsError(); + return await bundleRequest.compileJS(path); + } + + // This will take multiple entry points and do some magic to make them more efficient to build in parallel + async bundleMultiple( + entries: Array, + options: BundleOptions = {}, + ): Promise> { + // Clone so we can mess with it + entries = [...entries]; + + // Seed the dependency graph with all the entries at the same time + const processor = this.request.createDiagnosticsProcessor({ + origins: [ + { + category: 'Bundler', + message: 'Analyzing dependencies for bundleMultiple', + }, + ], + }); + const entryUids = entries.map((entry) => + this.master.projectManager.getUid(entry) + ); + const analyzeProgress = this.reporter.progress({ + name: `bundler:analyze:${entryUids.join(',')}`, + title: 'Analyzing', + }); + processor.setThrowAfter(100); + await this.graph.seed({ + paths: entries, + diagnosticsProcessor: processor, + analyzeProgress, + validate: false, + }); + analyzeProgress.end(); + processor.maybeThrowDiagnosticsError(); + + // Now actually bundle them + const map: Map = new Map(); + + const progress = this.reporter.progress({title: 'Bundling'}); + progress.setTotal(entries.length); + + const silentReporter = this.reporter.fork({ + streams: [], + }); + + const promises: Set> = new Set(); + + // Could maybe do some of this in parallel? + while (entries.length > 0) { + const entry = entries.shift()!; + + const promise = (async () => { + const text = markup``; + progress.pushText(text); + map.set(entry, await this.bundle(entry, options, silentReporter)); + progress.popText(text); + progress.tick(); + })(); + promise.then(() => { + promises.delete(promise); + }); + promises.add(promise); + + if (promises.size > 5) { + await Promise.race(Array.from(promises)); + } + } + + await Promise.all(Array.from(promises)); + + progress.end(); + + return map; + } + + async bundleManifest({resolvedEntry, manifestDef}: BundlerEntryResoluton) { + let bundles: Array = []; + const files: BundlerFiles = new Map(); + + const createBundle = async ( + resolvedSegment: AbsoluteFilePath, + options: BundleOptions, + ): Promise => { + const bundle = await this.bundle(resolvedSegment, options); + for (const [path, content] of bundle.files) { + files.set(path, content); + } + bundles = bundles.concat(bundle.bundles); + return bundle.entry; + }; + + const entryBundle = await createBundle(resolvedEntry, {}); + + // + const bundleBuddyStats = this.graph.getBundleBuddyStats(this.entries); + files.set( + 'bundlebuddy.json', + { + kind: 'stats', + content: () => stringifyJSON(bundleBuddyStats), + }, + ); + + // TODO ensure that __dirname is relative to the project root + if (manifestDef !== undefined) { + const newManifest = await this.deriveManifest( + manifestDef, + entryBundle, + createBundle, + (relative, buffer) => { + if (!files.has(relative)) { + files.set( + relative, + { + kind: 'file', + content: () => buffer, + }, + ); + } + }, + ); + + // If we have a `files` array then set it to all the newly added files + // This will have included files already there that we copied + if (newManifest.files !== undefined) { + newManifest.files = Array.from(files.keys()); + } + + // Add a package.json with updated values + files.set( + 'package.json', + { + kind: 'manifest', + content: () => stringifyJSON(newManifest), + }, + ); + } + + return { + files, + bundles, + entry: entryBundle, + }; + } + + async deriveManifest( + manifestDef: ManifestDefinition, + entryBundle: BundleResultBundle, + createBundle: ( + resolvedSegment: AbsoluteFilePath, + options: BundleOptions, + ) => Promise, + addFile: (relative: string, buffer: Buffer | string) => void, + ): Promise { + // TODO figure out some way to use bundleMultiple here + const manifest = manifestDef.manifest; + + const newManifest: JSONManifest = { + ...convertManifestToJSON(manifest), + main: entryBundle.js.path, + }; + + // TODO inherit some manifest properties from project configs + const project = this.master.projectManager.findProjectExisting( + manifestDef.folder, + ); + if (project !== undefined) { + if (newManifest.name === undefined) { + newManifest.name = project.config.name; + } + } + + // TODO remove dependencies fields, probably? + + // TODO Compile a index.d.ts + + // Copy manifest.files + if (manifest.files !== undefined) { + const paths = await this.master.memoryFs.glob( + manifestDef.folder, + { + overrideIgnore: flipPathPatterns(manifest.files), + }, + ); + + for (const path of paths) { + const relative = manifestDef.folder.relative(path).join(); + const buffer = await readFile(path); + addFile(relative, buffer); + } + } + + // Compile manifest.bin files + const bin = manifest.bin; + if (bin !== undefined) { + const newBin: Dict = {}; + newManifest.bin = newBin; + + const binConsumer = manifestDef.consumer.get('bin'); + const isBinShorthand = typeof binConsumer.asUnknown() === 'string'; + + for (const [binName, relative] of manifest.bin) { + const location = (isBinShorthand ? binConsumer : binConsumer.get(binName)).getDiagnosticLocation( + 'inner-value', + ); + + const absolute = await this.master.resolver.resolveAssert( + { + ...this.config.resolver, + origin: manifestDef.folder, + source: createUnknownFilePath(relative).toExplicitRelative(), + }, + { + location, + }, + ); + + const res = await createBundle( + absolute.path, + { + prefix: `bin/${binName}`, + interpreter: '/usr/bin/env node', + }, + ); + newBin[binName] = res.js.path; + } + } + + // TODO `{type: "module"}` will always fail since we've produced CJS bundles + // rome-ignore lint/noDelete + delete newManifest.type; + + return newManifest; + } + + async bundle( + resolvedEntry: AbsoluteFilePath, + options: BundleOptions = {}, + reporter: Reporter = this.reporter, + ): Promise { + reporter.info( + markup`Bundling `, + ); + + const req = this.createBundleRequest(resolvedEntry, options, reporter); + const res = await req.bundle(); + + const processor = this.request.createDiagnosticsProcessor(); + processor.addDiagnostics(res.diagnostics); + processor.maybeThrowDiagnosticsError(); + + if (res.cached) { + reporter.warn('Bundle was built completely from cache'); + } + + const prefix = options.prefix === undefined ? '' : `${options.prefix}/`; + const jsPath = `${prefix}index.js`; + const mapPath = `${jsPath}.map`; + + const files: BundlerFiles = new Map(); + files.set( + jsPath, + { + kind: 'entry', + content: () => res.content, + }, + ); + + files.set( + mapPath, + { + kind: 'sourcemap', + content: () => res.sourceMap.toJSON(), + }, + ); + + for (const [relative, buffer] of res.assets) { + files.set( + relative, + { + kind: 'asset', + content: () => buffer, + }, + ); + } + + const bundle: BundleResultBundle = { + js: { + path: jsPath, + content: res.content, + }, + sourceMap: { + path: mapPath, + map: res.sourceMap, + }, + }; + return { + entry: bundle, + bundles: [bundle], + files, + }; + } } diff --git a/packages/@romejs/core/master/commands.ts b/packages/@romejs/core/master/commands.ts index 1bfca110d06..c7b4daf3276 100644 --- a/packages/@romejs/core/master/commands.ts +++ b/packages/@romejs/core/master/commands.ts @@ -35,18 +35,18 @@ import {JSONPropertyValue} from '@romejs/codec-json'; import {SharedCommand} from '../common/commands'; export type MasterCommand> = SharedCommand & { - overrideClientFlags?: Partial; - overrideRequestFlags?: Partial; - callback: ( - req: MasterRequest, - commandFlags: Flags, - ) => undefined | Promise; + overrideClientFlags?: Partial; + overrideRequestFlags?: Partial; + callback: ( + req: MasterRequest, + commandFlags: Flags, + ) => undefined | Promise; }; export function createMasterCommand>( - cmd: MasterCommand, + cmd: MasterCommand, ): MasterCommand { - return cmd; + return cmd; } // rome-ignore lint/noExplicitAny diff --git a/packages/@romejs/core/master/commands/_evict.ts b/packages/@romejs/core/master/commands/_evict.ts index badb3f72dbb..f2976aad28d 100644 --- a/packages/@romejs/core/master/commands/_evict.ts +++ b/packages/@romejs/core/master/commands/_evict.ts @@ -10,29 +10,29 @@ import {commandCategories} from '../../common/commands'; import {createMasterCommand} from '../commands'; export default createMasterCommand({ - description: 'evict a file from the memory cache', - category: commandCategories.INTERNAL, - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: MasterRequest): Promise { - const { - master, - reporter, - client, - query: {args}, - } = req; + description: 'evict a file from the memory cache', + category: commandCategories.INTERNAL, + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: MasterRequest): Promise { + const { + master, + reporter, + client, + query: {args}, + } = req; - const files = - args.length === 0 ? master.fileAllocator.getAllOwnedFilenames() : args; + const files = + args.length === 0 ? master.fileAllocator.getAllOwnedFilenames() : args; - for (const file of files) { - await master.fileAllocator.evict(client.flags.cwd.resolve(file)); - reporter.success(`Evicted ${file}`); - } + for (const file of files) { + await master.fileAllocator.evict(client.flags.cwd.resolve(file)); + reporter.success(`Evicted ${file}`); + } - reporter.info(`Evicted ${String(files.length)} files`); - }, + reporter.info(`Evicted ${String(files.length)} files`); + }, }); diff --git a/packages/@romejs/core/master/commands/_moduleSignature.ts b/packages/@romejs/core/master/commands/_moduleSignature.ts index b9a14e1fa1b..f8f2fc0009c 100644 --- a/packages/@romejs/core/master/commands/_moduleSignature.ts +++ b/packages/@romejs/core/master/commands/_moduleSignature.ts @@ -11,25 +11,25 @@ import {createMasterCommand} from '../commands'; import {createUnknownFilePath} from '@romejs/path'; export default createMasterCommand({ - category: commandCategories.INTERNAL, - description: 'get the module type signature of a file', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: MasterRequest): Promise { - const {master, reporter} = req; - const {args} = req.query; - req.expectArgumentLength(1); + category: commandCategories.INTERNAL, + description: 'get the module type signature of a file', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: MasterRequest): Promise { + const {master, reporter} = req; + const {args} = req.query; + req.expectArgumentLength(1); - const filename = await master.resolver.resolveEntryAssertPath( - { - ...req.getResolverOptionsFromFlags(), - source: createUnknownFilePath(args[0]), - }, - {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})}, - ); - reporter.inspect(await req.requestWorkerModuleSignature(filename, {})); - }, + const filename = await master.resolver.resolveEntryAssertPath( + { + ...req.getResolverOptionsFromFlags(), + source: createUnknownFilePath(args[0]), + }, + {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})}, + ); + reporter.inspect(await req.requestWorkerModuleSignature(filename, {})); + }, }); diff --git a/packages/@romejs/core/master/commands/_noop.ts b/packages/@romejs/core/master/commands/_noop.ts index 4a0c4313590..5b34b5d8fca 100644 --- a/packages/@romejs/core/master/commands/_noop.ts +++ b/packages/@romejs/core/master/commands/_noop.ts @@ -10,14 +10,14 @@ import {commandCategories} from '../../common/commands'; import {createMasterCommand} from '../commands'; export default createMasterCommand({ - category: commandCategories.INTERNAL, - description: 'TODO', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: MasterRequest): Promise { - req; - }, + category: commandCategories.INTERNAL, + description: 'TODO', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: MasterRequest): Promise { + req; + }, }); diff --git a/packages/@romejs/core/master/commands/analyzeDependencies.ts b/packages/@romejs/core/master/commands/analyzeDependencies.ts index 3fc6307a485..b8f172b6226 100644 --- a/packages/@romejs/core/master/commands/analyzeDependencies.ts +++ b/packages/@romejs/core/master/commands/analyzeDependencies.ts @@ -13,90 +13,90 @@ import {createUnknownFilePath} from '@romejs/path'; import {SourceLocation} from '@romejs/parser-core'; type Flags = { - focusSource: undefined | string; - compact: boolean; + focusSource: undefined | string; + compact: boolean; }; function removeLoc(obj: T): Omit { - const {loc, ...locless} = obj; - loc; - return locless; + const {loc, ...locless} = obj; + loc; + return locless; } export default createMasterCommand({ - category: commandCategories.SOURCE_CODE, - description: 'analyze and dump the dependencies of a file', - usage: '', - examples: [], - defineFlags(c: Consumer): Flags { - return { - compact: c.get('compact').asBoolean(false), - focusSource: c.get('focusSource').asStringOrVoid(), - }; - }, - async callback(req: MasterRequest, commandFlags: Flags): Promise { - const {master, reporter} = req; - const {args} = req.query; - req.expectArgumentLength(1); + category: commandCategories.SOURCE_CODE, + description: 'analyze and dump the dependencies of a file', + usage: '', + examples: [], + defineFlags(c: Consumer): Flags { + return { + compact: c.get('compact').asBoolean(false), + focusSource: c.get('focusSource').asStringOrVoid(), + }; + }, + async callback(req: MasterRequest, commandFlags: Flags): Promise { + const {master, reporter} = req; + const {args} = req.query; + req.expectArgumentLength(1); - const filename = await master.resolver.resolveEntryAssertPath( - { - ...req.getResolverOptionsFromFlags(), - source: createUnknownFilePath(args[0]), - }, - {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})}, - ); + const filename = await master.resolver.resolveEntryAssertPath( + { + ...req.getResolverOptionsFromFlags(), + source: createUnknownFilePath(args[0]), + }, + {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})}, + ); - let res = await req.requestWorkerAnalyzeDependencies(filename, {}); + let res = await req.requestWorkerAnalyzeDependencies(filename, {}); - const {focusSource} = commandFlags; - if (focusSource !== undefined) { - res = { - ...res, - importFirstUsage: res.importFirstUsage.filter((dep) => { - return dep.source === focusSource; - }), - dependencies: res.dependencies.filter((dep) => { - return dep.source === focusSource; - }), - }; - } + const {focusSource} = commandFlags; + if (focusSource !== undefined) { + res = { + ...res, + importFirstUsage: res.importFirstUsage.filter((dep) => { + return dep.source === focusSource; + }), + dependencies: res.dependencies.filter((dep) => { + return dep.source === focusSource; + }), + }; + } - if (commandFlags.compact) { - res = { - ...res, - importFirstUsage: res.importFirstUsage.map((imp) => { - return removeLoc(imp); - }), - exports: res.exports.map((exp) => { - // This weird switch is because TS only returns an object with the properties common amongst all - switch (exp.type) { - case 'local': - return removeLoc(exp); + if (commandFlags.compact) { + res = { + ...res, + importFirstUsage: res.importFirstUsage.map((imp) => { + return removeLoc(imp); + }), + exports: res.exports.map((exp) => { + // This weird switch is because TS only returns an object with the properties common amongst all + switch (exp.type) { + case 'local': + return removeLoc(exp); - case 'external': - return removeLoc(exp); + case 'external': + return removeLoc(exp); - case 'externalAll': - return removeLoc(exp); + case 'externalAll': + return removeLoc(exp); - case 'externalNamespace': - return removeLoc(exp); - } - }), - dependencies: res.dependencies.map((dep) => { - return { - ...removeLoc(dep), - names: dep.names.map((name) => { - return removeLoc(name); - }), - }; - }), - }; - } + case 'externalNamespace': + return removeLoc(exp); + } + }), + dependencies: res.dependencies.map((dep) => { + return { + ...removeLoc(dep), + names: dep.names.map((name) => { + return removeLoc(name); + }), + }; + }), + }; + } - reporter.inspect(res); - }, + reporter.inspect(res); + }, }); diff --git a/packages/@romejs/core/master/commands/bundle.ts b/packages/@romejs/core/master/commands/bundle.ts index 76d03e5af47..7665ca9e29c 100644 --- a/packages/@romejs/core/master/commands/bundle.ts +++ b/packages/@romejs/core/master/commands/bundle.ts @@ -14,53 +14,53 @@ import {Consumer} from '@romejs/consume'; import {markup} from '@romejs/string-markup'; type Flags = { - quiet: boolean; + quiet: boolean; }; export default createMasterCommand({ - category: commandCategories.SOURCE_CODE, - description: 'build a standalone js bundle for a package', - usage: '', - examples: [], - defineFlags(consumer: Consumer): Flags { - return { - quiet: consumer.get('quiet').asBoolean(false), - }; - }, - async callback(req: MasterRequest, commandFlags: Flags): Promise { - const {flags} = req.client; - const {args} = req.query; - const {reporter} = req; - req.expectArgumentLength(2); + category: commandCategories.SOURCE_CODE, + description: 'build a standalone js bundle for a package', + usage: '', + examples: [], + defineFlags(consumer: Consumer): Flags { + return { + quiet: consumer.get('quiet').asBoolean(false), + }; + }, + async callback(req: MasterRequest, commandFlags: Flags): Promise { + const {flags} = req.client; + const {args} = req.query; + const {reporter} = req; + req.expectArgumentLength(2); - const [entryFilename, outputFolder] = args; - const bundler = Bundler.createFromMasterRequest(req); + const [entryFilename, outputFolder] = args; + const bundler = Bundler.createFromMasterRequest(req); - const resolution = await bundler.getResolvedEntry(entryFilename); - const {files: outFiles} = await bundler.bundleManifest(resolution); + const resolution = await bundler.getResolvedEntry(entryFilename); + const {files: outFiles} = await bundler.bundleManifest(resolution); - const savedList = []; - const dir = flags.cwd.resolve(outputFolder); - for (const [filename, {kind, content}] of outFiles) { - const buff = content(); - const file = dir.append(filename); - const loc = file.join(); - savedList.push( - markup`${filename} ${Buffer.byteLength( - buff, - )} ${kind}`, - ); - await createDirectory(file.getParent(), {recursive: true}); - await writeFile(file, buff); - } + const savedList = []; + const dir = flags.cwd.resolve(outputFolder); + for (const [filename, {kind, content}] of outFiles) { + const buff = content(); + const file = dir.append(filename); + const loc = file.join(); + savedList.push( + markup`${filename} ${Buffer.byteLength( + buff, + )} ${kind}`, + ); + await createDirectory(file.getParent(), {recursive: true}); + await writeFile(file, buff); + } - if (commandFlags.quiet) { - reporter.success(markup`Saved to `); - } else { - reporter.success( - markup`Saved the following files to `, - ); - reporter.list(savedList); - } - }, + if (commandFlags.quiet) { + reporter.success(markup`Saved to `); + } else { + reporter.success( + markup`Saved the following files to `, + ); + reporter.list(savedList); + } + }, }); diff --git a/packages/@romejs/core/master/commands/ci.ts b/packages/@romejs/core/master/commands/ci.ts index 87d821a34cc..479ce510c97 100644 --- a/packages/@romejs/core/master/commands/ci.ts +++ b/packages/@romejs/core/master/commands/ci.ts @@ -14,75 +14,75 @@ import test from './test'; import {Consumer} from '@romejs/consume'; async function runChildCommand( - req: MasterRequest, - fn: () => Promise, + req: MasterRequest, + fn: () => Promise, ): Promise { - try { - await fn(); - } catch (err) { - if (err instanceof DiagnosticsPrinter) { - // If the command raises diagnostics, it is safe to throw the printer. - // By doing so, the `ci` command bails and is marked as failed. - if (err.hasDiagnostics()) { - throw err; - } else { - req.master.handleRequestError(req, err); - } - } else { - throw err; - } - } + try { + await fn(); + } catch (err) { + if (err instanceof DiagnosticsPrinter) { + // If the command raises diagnostics, it is safe to throw the printer. + // By doing so, the `ci` command bails and is marked as failed. + if (err.hasDiagnostics()) { + throw err; + } else { + req.master.handleRequestError(req, err); + } + } else { + throw err; + } + } } type Flags = { - fix: boolean; + fix: boolean; }; export default createMasterCommand({ - category: commandCategories.CODE_QUALITY, - description: 'run lint and tests', - usage: '', - examples: [], - defineFlags(consumer: Consumer): Flags { - return { - fix: consumer.get('fix').asBoolean(false), - }; - }, - async callback(req: MasterRequest, flags: Flags): Promise { - const {reporter} = req; + category: commandCategories.CODE_QUALITY, + description: 'run lint and tests', + usage: '', + examples: [], + defineFlags(consumer: Consumer): Flags { + return { + fix: consumer.get('fix').asBoolean(false), + }; + }, + async callback(req: MasterRequest, flags: Flags): Promise { + const {reporter} = req; - reporter.heading('Running lint'); - await runChildCommand( - req, - async () => { - await lint.callback( - req, - { - formatOnly: false, - decisions: [], - save: flags.fix, - changed: undefined, - }, - ); - }, - ); + reporter.heading('Running lint'); + await runChildCommand( + req, + async () => { + await lint.callback( + req, + { + formatOnly: false, + decisions: [], + save: flags.fix, + changed: undefined, + }, + ); + }, + ); - reporter.heading('Running tests'); - await runChildCommand( - req, - async () => { - await test.callback( - req, - { - focusAllowed: false, - coverage: false, - freezeSnapshots: !flags.fix, - updateSnapshots: flags.fix, - showAllCoverage: false, - syncTests: false, - }, - ); - }, - ); - }, + reporter.heading('Running tests'); + await runChildCommand( + req, + async () => { + await test.callback( + req, + { + focusAllowed: false, + coverage: false, + freezeSnapshots: !flags.fix, + updateSnapshots: flags.fix, + showAllCoverage: false, + syncTests: false, + }, + ); + }, + ); + }, }); diff --git a/packages/@romejs/core/master/commands/compile.ts b/packages/@romejs/core/master/commands/compile.ts index 3db5197ea9d..f359115dc81 100644 --- a/packages/@romejs/core/master/commands/compile.ts +++ b/packages/@romejs/core/master/commands/compile.ts @@ -15,50 +15,46 @@ import {Consumer} from '@romejs/consume'; import Bundler from '../bundler/Bundler'; type Flags = { - bundle: boolean; + bundle: boolean; }; export default createMasterCommand({ - category: commandCategories.SOURCE_CODE, - description: 'compile a single file', - usage: '', - examples: [], - defineFlags(c: Consumer): Flags { - return { - bundle: c.get('bundle').asBoolean(false), - }; - }, - async callback(req: MasterRequest, commandFlags: Flags): Promise { - const {master, reporter} = req; - const {args} = req.query; - req.expectArgumentLength(1); - - const resolved = await master.resolver.resolveEntryAssert( - { - ...req.getResolverOptionsFromFlags(), - source: createUnknownFilePath(args[0]), - }, - {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})}, - ); - - let res: WorkerCompileResult; - if (commandFlags.bundle) { - const bundler = Bundler.createFromMasterRequest(req); - res = await bundler.compile(resolved.path); - } else { - res = await req.requestWorkerCompile(resolved.path, 'compile', {}, {}); - } - - const {compiledCode, diagnostics, suppressions}: WorkerCompileResult = res; - - if (diagnostics.length > 0) { - throw new DiagnosticsError( - 'Compile diagnostics', - diagnostics, - suppressions, - ); - } - - reporter.writeAll(compiledCode); - }, + category: commandCategories.SOURCE_CODE, + description: 'compile a single file', + usage: '', + examples: [], + defineFlags(c: Consumer): Flags { + return { + bundle: c.get('bundle').asBoolean(false), + }; + }, + async callback(req: MasterRequest, commandFlags: Flags): Promise { + const {master, reporter} = req; + const {args} = req.query; + req.expectArgumentLength(1); + + const resolved = await master.resolver.resolveEntryAssert( + { + ...req.getResolverOptionsFromFlags(), + source: createUnknownFilePath(args[0]), + }, + {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})}, + ); + + let res: WorkerCompileResult; + if (commandFlags.bundle) { + const bundler = Bundler.createFromMasterRequest(req); + res = await bundler.compile(resolved.path); + } else { + res = await req.requestWorkerCompile(resolved.path, 'compile', {}, {}); + } + + const {compiledCode, diagnostics, suppressions}: WorkerCompileResult = res; + + if (diagnostics.length > 0) { + throw new DiagnosticsError('Compile diagnostics', diagnostics, suppressions); + } + + reporter.writeAll(compiledCode); + }, }); diff --git a/packages/@romejs/core/master/commands/config.ts b/packages/@romejs/core/master/commands/config.ts index 9b5e85ad4e4..6e7b2919102 100644 --- a/packages/@romejs/core/master/commands/config.ts +++ b/packages/@romejs/core/master/commands/config.ts @@ -14,109 +14,109 @@ import {markup} from '@romejs/string-markup'; import {descriptions} from '@romejs/diagnostics'; export default createMasterCommand({ - category: commandCategories.PROJECT_MANAGEMENT, - description: 'Modify a project config', - usage: '(enable|disable|set) key [value]', - examples: [ - { - command: 'set name my_awesome_project', - description: 'Set the project name', - }, - ], - defineFlags() { - return {}; - }, - async callback(req: MasterRequest): Promise { - const {reporter} = req; - req.expectArgumentLength(2, 3); + category: commandCategories.PROJECT_MANAGEMENT, + description: 'Modify a project config', + usage: '(enable|disable|set) key [value]', + examples: [ + { + command: 'set name my_awesome_project', + description: 'Set the project name', + }, + ], + defineFlags() { + return {}; + }, + async callback(req: MasterRequest): Promise { + const {reporter} = req; + req.expectArgumentLength(2, 3); - const project = await req.assertClientCwdProject(); + const project = await req.assertClientCwdProject(); - let keyParts: string; - let value: boolean | string; + let keyParts: string; + let value: boolean | string; - const [action, ...restArgs] = req.query.args; - switch (action) { - case 'enable': { - req.expectArgumentLength(2); - keyParts = req.query.args[1]; - value = true; - break; - } + const [action, ...restArgs] = req.query.args; + switch (action) { + case 'enable': { + req.expectArgumentLength(2); + keyParts = req.query.args[1]; + value = true; + break; + } - case 'disable': { - req.expectArgumentLength(2); - keyParts = req.query.args[1]; - value = false; - break; - } + case 'disable': { + req.expectArgumentLength(2); + keyParts = req.query.args[1]; + value = false; + break; + } - case 'set-directory': { - req.expectArgumentLength(3); - [keyParts, value] = restArgs; + case 'set-directory': { + req.expectArgumentLength(3); + [keyParts, value] = restArgs; - // If the value is an absolute path, then make it relative to the project folder - const path = createUnknownFilePath(value); - if (path.isAbsolute()) { - value = assertHardMeta(project.meta).projectFolder.relative(path).join(); - } + // If the value is an absolute path, then make it relative to the project folder + const path = createUnknownFilePath(value); + if (path.isAbsolute()) { + value = assertHardMeta(project.meta).projectFolder.relative(path).join(); + } - break; - } + break; + } - case 'set': { - req.expectArgumentLength(3); - [keyParts, value] = restArgs; - break; - } + case 'set': { + req.expectArgumentLength(3); + [keyParts, value] = restArgs; + break; + } - default: - throw req.throwDiagnosticFlagError({ - description: descriptions.FLAGS.UNKNOWN_ACTION(action), - target: { - type: 'arg', - key: 0, - }, - }); - } + default: + throw req.throwDiagnosticFlagError({ + description: descriptions.FLAGS.UNKNOWN_ACTION(action), + target: { + type: 'arg', + key: 0, + }, + }); + } - try { - await modifyProjectConfig( - project.meta, - { - pre: (meta) => { - reporter.success( - markup`Setting ${keyParts} to ${JSON.stringify( - value, - )} in the project config `, - ); + try { + await modifyProjectConfig( + project.meta, + { + pre: (meta) => { + reporter.success( + markup`Setting ${keyParts} to ${JSON.stringify( + value, + )} in the project config `, + ); - if (value === 'true' || value === 'false') { - const suggestedCommand = value === 'true' ? 'enable' : 'disable'; - reporter.warn( - markup`Value is the string ${value} but it looks like a boolean. You probably meant to use the command:`, - ); - reporter.command(markup`config ${suggestedCommand} ${keyParts}`); - } - }, - modify: (consumer) => { - // Set the specified value - let keyConsumer = consumer; - for (const key of keyParts.split('.')) { - if (!keyConsumer.exists()) { - keyConsumer.setValue({}); - } - keyConsumer = keyConsumer.get(key); - } - keyConsumer.setValue(value); - }, - }, - ); - } catch (err) { - reporter.error( - 'Error occured while testing new project config. Your changes have not been saved.', - ); - throw err; - } - }, + if (value === 'true' || value === 'false') { + const suggestedCommand = value === 'true' ? 'enable' : 'disable'; + reporter.warn( + markup`Value is the string ${value} but it looks like a boolean. You probably meant to use the command:`, + ); + reporter.command(markup`config ${suggestedCommand} ${keyParts}`); + } + }, + modify: (consumer) => { + // Set the specified value + let keyConsumer = consumer; + for (const key of keyParts.split('.')) { + if (!keyConsumer.exists()) { + keyConsumer.setValue({}); + } + keyConsumer = keyConsumer.get(key); + } + keyConsumer.setValue(value); + }, + }, + ); + } catch (err) { + reporter.error( + 'Error occured while testing new project config. Your changes have not been saved.', + ); + throw err; + } + }, }); diff --git a/packages/@romejs/core/master/commands/develop.ts b/packages/@romejs/core/master/commands/develop.ts index 4888aa952e5..96b43dc7638 100644 --- a/packages/@romejs/core/master/commands/develop.ts +++ b/packages/@romejs/core/master/commands/develop.ts @@ -12,32 +12,32 @@ import {commandCategories} from '../../common/commands'; import {createMasterCommand} from '../commands'; type Flags = { - port: number; + port: number; }; const DEFAULT_PORT = 8_081; export default createMasterCommand({ - category: commandCategories.SOURCE_CODE, - description: 'start a web server', - usage: '', - examples: [], - defineFlags(c: Consumer): Flags { - return { - port: c.get('port').asNumber(DEFAULT_PORT), - }; - }, - async callback(req: MasterRequest, flags: Flags): Promise { - // Initialize cwd early since we'll need it for any requests - await req.master.projectManager.findProject(req.client.flags.cwd); + category: commandCategories.SOURCE_CODE, + description: 'start a web server', + usage: '', + examples: [], + defineFlags(c: Consumer): Flags { + return { + port: c.get('port').asNumber(DEFAULT_PORT), + }; + }, + async callback(req: MasterRequest, flags: Flags): Promise { + // Initialize cwd early since we'll need it for any requests + await req.master.projectManager.findProject(req.client.flags.cwd); - const web = new WebServer(req); - web.listen(flags.port); + const web = new WebServer(req); + web.listen(flags.port); - req.endEvent.subscribe(() => { - web.close(); - }); + req.endEvent.subscribe(() => { + web.close(); + }); - await new Promise(() => {}); - }, + await new Promise(() => {}); + }, }); diff --git a/packages/@romejs/core/master/commands/format.ts b/packages/@romejs/core/master/commands/format.ts index 70806947597..051904303b2 100644 --- a/packages/@romejs/core/master/commands/format.ts +++ b/packages/@romejs/core/master/commands/format.ts @@ -12,45 +12,45 @@ import {createUnknownFilePath} from '@romejs/path'; import {Consumer} from '@romejs/consume'; type Flags = { - allowDiagnostics: boolean; + allowDiagnostics: boolean; }; export default createMasterCommand({ - category: commandCategories.INTERNAL, - description: 'TODO', - usage: '', - examples: [], - defineFlags(c: Consumer): Flags { - return { - allowDiagnostics: c.get('allowDiagnostics').asBoolean(false), - }; - }, - async callback(req: MasterRequest, flags: Flags): Promise { - const {reporter, master} = req; - const {args} = req.query; - req.expectArgumentLength(1); + category: commandCategories.INTERNAL, + description: 'TODO', + usage: '', + examples: [], + defineFlags(c: Consumer): Flags { + return { + allowDiagnostics: c.get('allowDiagnostics').asBoolean(false), + }; + }, + async callback(req: MasterRequest, flags: Flags): Promise { + const {reporter, master} = req; + const {args} = req.query; + req.expectArgumentLength(1); - const filename = await master.resolver.resolveEntryAssertPath( - { - ...req.getResolverOptionsFromFlags(), - source: createUnknownFilePath(args[0]), - }, - {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})}, - ); + const filename = await master.resolver.resolveEntryAssertPath( + { + ...req.getResolverOptionsFromFlags(), + source: createUnknownFilePath(args[0]), + }, + {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})}, + ); - const res = await req.requestWorkerFormat( - filename, - { - allowParserDiagnostics: flags.allowDiagnostics, - }, - ); + const res = await req.requestWorkerFormat( + filename, + { + allowParserDiagnostics: flags.allowDiagnostics, + }, + ); - if (res === undefined) { - reporter.error('No formatter for this file'); - return undefined; - } else { - reporter.writeAll(res.formatted); - return res.formatted; - } - }, + if (res === undefined) { + reporter.error('No formatter for this file'); + return undefined; + } else { + reporter.writeAll(res.formatted); + return res.formatted; + } + }, }); diff --git a/packages/@romejs/core/master/commands/lint.ts b/packages/@romejs/core/master/commands/lint.ts index ee7ff9c5470..75469b23bfe 100644 --- a/packages/@romejs/core/master/commands/lint.ts +++ b/packages/@romejs/core/master/commands/lint.ts @@ -7,14 +7,14 @@ import {MasterRequest} from '@romejs/core'; import Linter, { - LinterCompilerOptionsPerFile, - LinterOptions, + LinterCompilerOptionsPerFile, + LinterOptions, } from '../linter/Linter'; import {markup} from '@romejs/string-markup'; import {createMasterCommand} from '../commands'; import { - LintCompilerOptionsDecisions, - parseDecisionStrings, + LintCompilerOptionsDecisions, + parseDecisionStrings, } from '@romejs/js-compiler'; import {Consumer} from '@romejs/consume'; import {commandCategories} from '@romejs/core/common/commands'; @@ -22,89 +22,89 @@ import {createUnknownFilePath} from '@romejs/path'; import {LINTABLE_EXTENSIONS} from '@romejs/core/common/file-handlers'; type Flags = { - decisions: Array; - save: boolean; - changed: undefined | string; - formatOnly: boolean; + decisions: Array; + save: boolean; + changed: undefined | string; + formatOnly: boolean; }; export default createMasterCommand({ - category: commandCategories.CODE_QUALITY, - description: 'run lint against a set of files', - allowRequestFlags: ['watch', 'review'], - usage: '', - examples: [], - defineFlags(consumer: Consumer): Flags { - return { - decisions: consumer.get('decisions').asImplicitArray().map((item) => - item.asString() - ), - save: consumer.get('save').asBoolean(false), - formatOnly: consumer.get('formatOnly').asBoolean(false), - changed: consumer.get('changed').asStringOrVoid(), - }; - }, - async callback(req: MasterRequest, flags: Flags): Promise { - const {reporter} = req; + category: commandCategories.CODE_QUALITY, + description: 'run lint against a set of files', + allowRequestFlags: ['watch', 'review'], + usage: '', + examples: [], + defineFlags(consumer: Consumer): Flags { + return { + decisions: consumer.get('decisions').asImplicitArray().map((item) => + item.asString() + ), + save: consumer.get('save').asBoolean(false), + formatOnly: consumer.get('formatOnly').asBoolean(false), + changed: consumer.get('changed').asStringOrVoid(), + }; + }, + async callback(req: MasterRequest, flags: Flags): Promise { + const {reporter} = req; - let lintCompilerOptionsPerFile: LinterCompilerOptionsPerFile = {}; - let globalDecisions: LintCompilerOptionsDecisions = []; - const {decisions} = flags; - if (decisions !== undefined) { - ({lintCompilerOptionsPerFile, globalDecisions} = parseDecisionStrings( - decisions, - req.client.flags.cwd, - (description) => { - throw req.throwDiagnosticFlagError({ - description, - target: {type: 'flag', key: 'decisions'}, - }); - }, - )); - } + let lintCompilerOptionsPerFile: LinterCompilerOptionsPerFile = {}; + let globalDecisions: LintCompilerOptionsDecisions = []; + const {decisions} = flags; + if (decisions !== undefined) { + ({lintCompilerOptionsPerFile, globalDecisions} = parseDecisionStrings( + decisions, + req.client.flags.cwd, + (description) => { + throw req.throwDiagnosticFlagError({ + description, + target: {type: 'flag', key: 'decisions'}, + }); + }, + )); + } - // Look up arguments manually in vsc if we were passed a changes branch - let args; - if (flags.changed !== undefined) { - // No arguments expected when using this flag - req.expectArgumentLength(0); + // Look up arguments manually in vsc if we were passed a changes branch + let args; + if (flags.changed !== undefined) { + // No arguments expected when using this flag + req.expectArgumentLength(0); - const client = await req.getVCSClient(); - const target = flags.changed === '' ? client.trunkBranch : flags.changed; - args = await client.getModifiedFiles(target); + const client = await req.getVCSClient(); + const target = flags.changed === '' ? client.trunkBranch : flags.changed; + args = await client.getModifiedFiles(target); - // Only include lintable files - args = args.filter((arg) => { - const path = createUnknownFilePath(arg); + // Only include lintable files + args = args.filter((arg) => { + const path = createUnknownFilePath(arg); - for (const ext of LINTABLE_EXTENSIONS) { - if (path.hasEndExtension(ext)) { - return true; - } - } + for (const ext of LINTABLE_EXTENSIONS) { + if (path.hasEndExtension(ext)) { + return true; + } + } - return false; - }); + return false; + }); - if (args.length === 0) { - reporter.warn(`No files changed from ${target}`); - } else { - reporter.info(`Files changed from ${target}`); - reporter.list(args.map((arg) => markup``)); - reporter.hr(); - } - } + if (args.length === 0) { + reporter.warn(`No files changed from ${target}`); + } else { + reporter.info(`Files changed from ${target}`); + reporter.list(args.map((arg) => markup``)); + reporter.hr(); + } + } - const opts: LinterOptions = { - hasDecisions: flags.decisions.length > 0, - lintCompilerOptionsPerFile, - globalDecisions, - save: flags.save, - formatOnly: flags.formatOnly, - args, - }; + const opts: LinterOptions = { + hasDecisions: flags.decisions.length > 0, + lintCompilerOptionsPerFile, + globalDecisions, + save: flags.save, + formatOnly: flags.formatOnly, + args, + }; - const linter = new Linter(req, opts); - await linter.run(req.query.requestFlags.watch); - }, + const linter = new Linter(req, opts); + await linter.run(req.query.requestFlags.watch); + }, }); diff --git a/packages/@romejs/core/master/commands/lsp.ts b/packages/@romejs/core/master/commands/lsp.ts index e071b308939..a452284fedf 100644 --- a/packages/@romejs/core/master/commands/lsp.ts +++ b/packages/@romejs/core/master/commands/lsp.ts @@ -11,27 +11,27 @@ import {createMasterCommand} from '../commands'; import LSPServer from '../lsp/LSPServer'; export default createMasterCommand({ - category: commandCategories.PROJECT_MANAGEMENT, - description: 'TODO', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: MasterRequest): Promise { - const {master, bridge} = req; + category: commandCategories.PROJECT_MANAGEMENT, + description: 'TODO', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: MasterRequest): Promise { + const {master, bridge} = req; - const lspServer = new LSPServer(req); - master.connectedLSPServers.add(lspServer); + const lspServer = new LSPServer(req); + master.connectedLSPServers.add(lspServer); - bridge.endEvent.subscribe(() => { - master.connectedLSPServers.delete(lspServer); - }); + bridge.endEvent.subscribe(() => { + master.connectedLSPServers.delete(lspServer); + }); - bridge.lspFromClientBuffer.subscribe((chunk) => { - lspServer.append(chunk); - }); + bridge.lspFromClientBuffer.subscribe((chunk) => { + lspServer.append(chunk); + }); - await bridge.endEvent.wait(); - }, + await bridge.endEvent.wait(); + }, }); diff --git a/packages/@romejs/core/master/commands/parse.ts b/packages/@romejs/core/master/commands/parse.ts index 1b5b2c634a8..6fcbd9af271 100644 --- a/packages/@romejs/core/master/commands/parse.ts +++ b/packages/@romejs/core/master/commands/parse.ts @@ -14,48 +14,48 @@ import {ConstSourceType, program} from '@romejs/js-ast'; import {removeLoc} from '@romejs/js-ast-utils'; type Flags = { - allowDiagnostics: boolean; - compact: boolean; - sourceType: undefined | ConstSourceType; + allowDiagnostics: boolean; + compact: boolean; + sourceType: undefined | ConstSourceType; }; export default createMasterCommand({ - category: commandCategories.SOURCE_CODE, - description: 'parse a single file and dump its ast', - usage: '', - examples: [], - defineFlags(c: Consumer): Flags { - return { - allowDiagnostics: c.get('allowDiagnostics').asBoolean(false), - compact: c.get('compact').asBoolean(true), - sourceType: c.get('sourceType').asStringSetOrVoid(['module', 'script']), - }; - }, - async callback(req: MasterRequest, flags: Flags): Promise { - const {master, reporter} = req; - const {args} = req.query; - req.expectArgumentLength(1); + category: commandCategories.SOURCE_CODE, + description: 'parse a single file and dump its ast', + usage: '', + examples: [], + defineFlags(c: Consumer): Flags { + return { + allowDiagnostics: c.get('allowDiagnostics').asBoolean(false), + compact: c.get('compact').asBoolean(true), + sourceType: c.get('sourceType').asStringSetOrVoid(['module', 'script']), + }; + }, + async callback(req: MasterRequest, flags: Flags): Promise { + const {master, reporter} = req; + const {args} = req.query; + req.expectArgumentLength(1); - const filename = await master.resolver.resolveEntryAssertPath( - { - ...req.getResolverOptionsFromFlags(), - source: createUnknownFilePath(args[0]), - }, - {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})}, - ); + const filename = await master.resolver.resolveEntryAssertPath( + { + ...req.getResolverOptionsFromFlags(), + source: createUnknownFilePath(args[0]), + }, + {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})}, + ); - let ast = await req.requestWorkerParse( - filename, - { - sourceType: flags.sourceType, - allowParserDiagnostics: flags.allowDiagnostics, - }, - ); + let ast = await req.requestWorkerParse( + filename, + { + sourceType: flags.sourceType, + allowParserDiagnostics: flags.allowDiagnostics, + }, + ); - if (flags.compact) { - ast = program.assert(removeLoc(ast)); - } + if (flags.compact) { + ast = program.assert(removeLoc(ast)); + } - reporter.inspect(ast); - }, + reporter.inspect(ast); + }, }); diff --git a/packages/@romejs/core/master/commands/publish.ts b/packages/@romejs/core/master/commands/publish.ts index 43e65cd0d14..42a157bc095 100644 --- a/packages/@romejs/core/master/commands/publish.ts +++ b/packages/@romejs/core/master/commands/publish.ts @@ -10,16 +10,16 @@ import {commandCategories} from '../../common/commands'; import {createMasterCommand} from '../commands'; export default createMasterCommand({ - category: commandCategories.PROJECT_MANAGEMENT, - description: 'TODO', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: MasterRequest): Promise { - req.expectArgumentLength(1); + category: commandCategories.PROJECT_MANAGEMENT, + description: 'TODO', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: MasterRequest): Promise { + req.expectArgumentLength(1); - // TODO - }, + // TODO + }, }); diff --git a/packages/@romejs/core/master/commands/resolve.ts b/packages/@romejs/core/master/commands/resolve.ts index 200531706e4..2e56e01a652 100644 --- a/packages/@romejs/core/master/commands/resolve.ts +++ b/packages/@romejs/core/master/commands/resolve.ts @@ -11,47 +11,47 @@ import {createMasterCommand} from '../commands'; import {createUnknownFilePath} from '@romejs/path'; export default createMasterCommand({ - category: commandCategories.SOURCE_CODE, - description: 'resolve a file', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: MasterRequest): Promise { - const {master, reporter} = req; - const {args} = req.query; - const {flags} = req.client; - req.expectArgumentLength(1, 2); + category: commandCategories.SOURCE_CODE, + description: 'resolve a file', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: MasterRequest): Promise { + const {master, reporter} = req; + const {args} = req.query; + const {flags} = req.client; + req.expectArgumentLength(1, 2); - let origin; - let relative = ''; - let key; + let origin; + let relative = ''; + let key; - if (args.length === 2) { - origin = flags.cwd.resolveMaybeUrl(args[0]); - relative = args[1]; - key = 1; - } else { - origin = flags.cwd; - relative = args[0]; - key = 0; - } + if (args.length === 2) { + origin = flags.cwd.resolveMaybeUrl(args[0]); + relative = args[1]; + key = 1; + } else { + origin = flags.cwd; + relative = args[0]; + key = 0; + } - const query = { - ...req.getResolverOptionsFromFlags(), - origin, - source: createUnknownFilePath(relative), - }; + const query = { + ...req.getResolverOptionsFromFlags(), + origin, + source: createUnknownFilePath(relative), + }; - const resolved = await master.resolver.resolveEntryAssert( - query, - { - location: req.getDiagnosticPointerFromFlags({type: 'arg', key}), - }, - ); - const filename = resolved.ref.real.join(); - reporter.logAll(filename); - return filename; - }, + const resolved = await master.resolver.resolveEntryAssert( + query, + { + location: req.getDiagnosticPointerFromFlags({type: 'arg', key}), + }, + ); + const filename = resolved.ref.real.join(); + reporter.logAll(filename); + return filename; + }, }); diff --git a/packages/@romejs/core/master/commands/run.ts b/packages/@romejs/core/master/commands/run.ts index e5dc04bfaf2..0c917cf05f5 100644 --- a/packages/@romejs/core/master/commands/run.ts +++ b/packages/@romejs/core/master/commands/run.ts @@ -15,74 +15,74 @@ import {AbsoluteFilePath, createRelativeFilePath} from '@romejs/path'; // This will be dispatched to the client where it has a special case for `executeCode` type RunResult = { - type: 'executeCode'; - filename: string; - code: string; - map: SourceMap; + type: 'executeCode'; + filename: string; + code: string; + map: SourceMap; }; export default createMasterCommand({ - category: commandCategories.PROJECT_MANAGEMENT, - description: 'TODO', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback(req: MasterRequest): Promise { - const {args} = req.query; - const {flags} = req.client; - const {master} = req; - req.expectArgumentLength(1); + category: commandCategories.PROJECT_MANAGEMENT, + description: 'TODO', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback(req: MasterRequest): Promise { + const {args} = req.query; + const {flags} = req.client; + const {master} = req; + req.expectArgumentLength(1); - async function executeCode(path: AbsoluteFilePath): Promise { - const bundler = Bundler.createFromMasterRequest(req); - const {entry} = await bundler.bundle(path); - return { - type: 'executeCode', - filename: path.join(), - code: entry.js.content, - map: entry.sourceMap.map.serialize(), - }; - } + async function executeCode(path: AbsoluteFilePath): Promise { + const bundler = Bundler.createFromMasterRequest(req); + const {entry} = await bundler.bundle(path); + return { + type: 'executeCode', + filename: path.join(), + code: entry.js.content, + map: entry.sourceMap.map.serialize(), + }; + } - // Get the current project - const project: undefined | ProjectDefinition = await master.projectManager.findProject( - flags.cwd, - ); + // Get the current project + const project: undefined | ProjectDefinition = await master.projectManager.findProject( + flags.cwd, + ); - // check for absolute paths - const target = args[0]; - const resolved = await master.resolver.resolveEntry({ - ...req.getResolverOptionsFromFlags(), - source: createRelativeFilePath(target), - }); - if (resolved.type === 'FOUND') { - return executeCode(resolved.path); - } + // check for absolute paths + const target = args[0]; + const resolved = await master.resolver.resolveEntry({ + ...req.getResolverOptionsFromFlags(), + source: createRelativeFilePath(target), + }); + if (resolved.type === 'FOUND') { + return executeCode(resolved.path); + } - // check for bin files in any manifests that belong to any projects - if (project !== undefined) { - for (const {manifest, folder} of project.packages.values()) { - const relative = manifest.bin.get(target); - if (relative === undefined) { - continue; - } + // check for bin files in any manifests that belong to any projects + if (project !== undefined) { + for (const {manifest, folder} of project.packages.values()) { + const relative = manifest.bin.get(target); + if (relative === undefined) { + continue; + } - const resolved = await master.resolver.resolveEntryAssertPath({ - ...req.getResolverOptionsFromFlags(), - origin: folder, - platform: 'node', - source: createRelativeFilePath(relative), - }); + const resolved = await master.resolver.resolveEntryAssertPath({ + ...req.getResolverOptionsFromFlags(), + origin: folder, + platform: 'node', + source: createRelativeFilePath(relative), + }); - return executeCode(resolved); - } - } + return executeCode(resolved); + } + } - // TODO check node_modules/.bin + // TODO check node_modules/.bin - // TODO check package.json scripts - throw new Error(`Failed to find "${target}"`); - }, + // TODO check package.json scripts + throw new Error(`Failed to find "${target}"`); + }, }); diff --git a/packages/@romejs/core/master/commands/status.ts b/packages/@romejs/core/master/commands/status.ts index 2809b06732b..12dd910dc03 100644 --- a/packages/@romejs/core/master/commands/status.ts +++ b/packages/@romejs/core/master/commands/status.ts @@ -11,65 +11,65 @@ import {commandCategories} from '../../common/commands'; import {createMasterCommand} from '../commands'; type StatusResult = { - master: { - heapTotal: number; - pid: number; - uptime: number; - }; - workers: Array; - projects: Array<{ - id: number; - }>; + master: { + heapTotal: number; + pid: number; + uptime: number; + }; + workers: Array; + projects: Array<{ + id: number; + }>; }; type StatusWorkerResult = { - astCacheSize: number; - heapTotal: number; - pid: number; - uptime: number; - ownedBytes: number; - ownedFileCount: number; + astCacheSize: number; + heapTotal: number; + pid: number; + uptime: number; + ownedBytes: number; + ownedFileCount: number; }; export default createMasterCommand({ - category: commandCategories.PROCESS_MANAGEMENT, - description: 'dump memory and process info of master and workers', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback({master}: MasterRequest): Promise { - const workers = await Promise.all( - master.workerManager.getWorkers().map(async ( - worker, - ): Promise => { - const workerStatus: WorkerStatus = await worker.bridge.status.call(); + category: commandCategories.PROCESS_MANAGEMENT, + description: 'dump memory and process info of master and workers', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback({master}: MasterRequest): Promise { + const workers = await Promise.all( + master.workerManager.getWorkers().map(async ( + worker, + ): Promise => { + const workerStatus: WorkerStatus = await worker.bridge.status.call(); - return { - astCacheSize: workerStatus.astCacheSize, - heapTotal: workerStatus.memoryUsage.heapTotal, - pid: workerStatus.pid, - uptime: workerStatus.uptime, - ownedBytes: worker.byteCount, - ownedFileCount: worker.fileCount, - }; - }), - ); + return { + astCacheSize: workerStatus.astCacheSize, + heapTotal: workerStatus.memoryUsage.heapTotal, + pid: workerStatus.pid, + uptime: workerStatus.uptime, + ownedBytes: worker.byteCount, + ownedFileCount: worker.fileCount, + }; + }), + ); - const {heapTotal} = process.memoryUsage(); - return { - master: { - heapTotal, - pid: process.pid, - uptime: process.uptime(), - }, - workers, - projects: master.projectManager.getProjects().map((project) => { - return { - id: project.id, - }; - }), - }; - }, + const {heapTotal} = process.memoryUsage(); + return { + master: { + heapTotal, + pid: process.pid, + uptime: process.uptime(), + }, + workers, + projects: master.projectManager.getProjects().map((project) => { + return { + id: project.id, + }; + }), + }; + }, }); diff --git a/packages/@romejs/core/master/commands/stop.ts b/packages/@romejs/core/master/commands/stop.ts index be3cf571756..72098f42055 100644 --- a/packages/@romejs/core/master/commands/stop.ts +++ b/packages/@romejs/core/master/commands/stop.ts @@ -10,14 +10,14 @@ import {commandCategories} from '../../common/commands'; import {createMasterCommand} from '../commands'; export default createMasterCommand({ - category: commandCategories.PROCESS_MANAGEMENT, - description: 'stop daemon', - usage: '', - examples: [], - defineFlags() { - return {}; - }, - async callback({master}: MasterRequest) { - master.end(); - }, + category: commandCategories.PROCESS_MANAGEMENT, + description: 'stop daemon', + usage: '', + examples: [], + defineFlags() { + return {}; + }, + async callback({master}: MasterRequest) { + master.end(); + }, }); diff --git a/packages/@romejs/core/master/commands/test.ts b/packages/@romejs/core/master/commands/test.ts index 3ccfc72b789..349dd45a7d5 100644 --- a/packages/@romejs/core/master/commands/test.ts +++ b/packages/@romejs/core/master/commands/test.ts @@ -18,82 +18,82 @@ import {TestMasterRunnerOptions, TestSources} from '../testing/types'; type Flags = Omit; export default createMasterCommand({ - category: commandCategories.CODE_QUALITY, - description: 'run tests', - usage: '', - examples: [], - defineFlags(c: Consumer): Flags { - return { - coverage: c.get('coverage').asBoolean(false), - showAllCoverage: c.get('showAllCoverage').asBoolean(false), - updateSnapshots: c.get('updateSnapshots').asBoolean(false), - freezeSnapshots: c.get('freezeSnapshots').asBoolean(false), - focusAllowed: c.get('focusAllowed').asBoolean(true), - syncTests: c.get('syncTests').asBoolean(false), - }; - }, - async callback(req: MasterRequest, commandFlags: Flags): Promise { - const {paths} = await req.getFilesFromArgs({ - tryAlternateArg: (path) => { - if (path.hasExtension('test')) { - return undefined; - } else { - return path.getParent().append( - `${path.getExtensionlessBasename()}.test${path.getExtensions()}`, - ); - } - }, - test: (path) => path.hasExtension('test'), - noun: 'test', - verb: 'testing', - configCategory: 'tests', - advice: [ - { - type: 'log', - category: 'info', - text: 'Searched for files with .test.* file extension', - }, - ], - extensions: JS_EXTENSIONS, - disabledDiagnosticCategory: 'tests/disabled', - }); + category: commandCategories.CODE_QUALITY, + description: 'run tests', + usage: '', + examples: [], + defineFlags(c: Consumer): Flags { + return { + coverage: c.get('coverage').asBoolean(false), + showAllCoverage: c.get('showAllCoverage').asBoolean(false), + updateSnapshots: c.get('updateSnapshots').asBoolean(false), + freezeSnapshots: c.get('freezeSnapshots').asBoolean(false), + focusAllowed: c.get('focusAllowed').asBoolean(true), + syncTests: c.get('syncTests').asBoolean(false), + }; + }, + async callback(req: MasterRequest, commandFlags: Flags): Promise { + const {paths} = await req.getFilesFromArgs({ + tryAlternateArg: (path) => { + if (path.hasExtension('test')) { + return undefined; + } else { + return path.getParent().append( + `${path.getExtensionlessBasename()}.test${path.getExtensions()}`, + ); + } + }, + test: (path) => path.hasExtension('test'), + noun: 'test', + verb: 'testing', + configCategory: 'tests', + advice: [ + { + type: 'log', + category: 'info', + text: 'Searched for files with .test.* file extension', + }, + ], + extensions: JS_EXTENSIONS, + disabledDiagnosticCategory: 'tests/disabled', + }); - let addDiagnostics: Diagnostics = []; + let addDiagnostics: Diagnostics = []; - const tests: TestSources = new Map(); + const tests: TestSources = new Map(); - const bundler = new Bundler( - req, - req.getBundlerConfigFromFlags({ - mocks: true, - }), - ); + const bundler = new Bundler( + req, + req.getBundlerConfigFromFlags({ + mocks: true, + }), + ); - for (const [path, res] of await bundler.bundleMultiple( - Array.from(paths), - { - deferredSourceMaps: true, - }, - )) { - tests.set( - path.join(), - { - code: res.entry.js.content, - sourceMap: res.entry.sourceMap.map, - ref: req.master.projectManager.getFileReference(path), - }, - ); - } + for (const [path, res] of await bundler.bundleMultiple( + Array.from(paths), + { + deferredSourceMaps: true, + }, + )) { + tests.set( + path.join(), + { + code: res.entry.js.content, + sourceMap: res.entry.sourceMap.map, + ref: req.master.projectManager.getFileReference(path), + }, + ); + } - const runner = new TestMasterRunner({ - addDiagnostics, - options: { - ...commandFlags, - verboseDiagnostics: req.query.requestFlags.verboseDiagnostics, - }, - sources: tests, - request: req, - }); - await runner.init(); - }, + const runner = new TestMasterRunner({ + addDiagnostics, + options: { + ...commandFlags, + verboseDiagnostics: req.query.requestFlags.verboseDiagnostics, + }, + sources: tests, + request: req, + }); + await runner.init(); + }, }); diff --git a/packages/@romejs/core/master/dependencies/DependencyGraph.ts b/packages/@romejs/core/master/dependencies/DependencyGraph.ts index 8594da5bbf8..3f0653371c7 100644 --- a/packages/@romejs/core/master/dependencies/DependencyGraph.ts +++ b/packages/@romejs/core/master/dependencies/DependencyGraph.ts @@ -19,350 +19,349 @@ import {Event} from '@romejs/events'; import {WorkerAnalyzeDependencyResult} from '../../common/bridges/WorkerBridge'; import {MasterRequest} from '@romejs/core'; import { - AbsoluteFilePath, - AbsoluteFilePathMap, - createUnknownFilePath, + AbsoluteFilePath, + AbsoluteFilePathMap, + createUnknownFilePath, } from '@romejs/path'; import {AnalyzeModuleType} from '../../common/types/analyzeDependencies'; import {markup} from '@romejs/string-markup'; export type DependencyGraphSeedResult = { - node: DependencyNode; - order: DependencyOrder; - cached: boolean; + node: DependencyNode; + order: DependencyOrder; + cached: boolean; }; const BUILTINS = [ - 'electron', - 'buffer', - 'child_process', - 'crypto', - 'dgram', - 'dns', - 'fs', - 'http', - 'https', - 'net', - 'os', - 'readline', - 'stream', - 'string_decoder', - 'tls', - 'tty', - 'zlib', - 'constants', - 'events', - 'url', - 'assert', - 'util', - 'path', - 'punycode', - 'querystring', - 'cluster', - 'console', - 'module', - 'process', - 'vm', - 'domain', - 'v8', - 'repl', - 'timers', - 'inspector', + 'electron', + 'buffer', + 'child_process', + 'crypto', + 'dgram', + 'dns', + 'fs', + 'http', + 'https', + 'net', + 'os', + 'readline', + 'stream', + 'string_decoder', + 'tls', + 'tty', + 'zlib', + 'constants', + 'events', + 'url', + 'assert', + 'util', + 'path', + 'punycode', + 'querystring', + 'cluster', + 'console', + 'module', + 'process', + 'vm', + 'domain', + 'v8', + 'repl', + 'timers', + 'inspector', ]; type SeedQueueItem = { - all: boolean; - async: boolean; - ancestry: Array; - type: AnalyzeModuleType; - loc: undefined | SourceLocation; + all: boolean; + async: boolean; + ancestry: Array; + type: AnalyzeModuleType; + loc: undefined | SourceLocation; }; export type DependencyGraphWorkerQueue = WorkerQueue; export default class DependencyGraph { - constructor(request: MasterRequest, resolverOpts: ResolverOptions) { - this.request = request; - this.master = request.master; - this.nodes = new AbsoluteFilePathMap(); - this.resolverOpts = resolverOpts; - - this.locker = new Locker(); - this.closeEvent = new Event({name: 'DependencyGraph.closeEvent'}); - } - - request: MasterRequest; - resolverOpts: ResolverOptions; - master: Master; - nodes: AbsoluteFilePathMap; - locker: Locker; - closeEvent: Event; - - close() { - this.closeEvent.send(); - } - - isExternal(source: string): boolean { - return BUILTINS.includes(source); - } - - getBundleBuddyStats(entries: Array): BundleBuddyStats { - const stats: BundleBuddyStats = []; - - for (const node of this.nodes.values()) { - const source = node.uid; - - for (const absoluteTarget of node.relativeToAbsolutePath.values()) { - const target = this.getNode(absoluteTarget).uid; - stats.push({ - target, - source, - }); - } - } - - for (const absoluteEntry of entries) { - const source = this.getNode(absoluteEntry).uid; - stats.push({ - source, - target: undefined, - }); - } - - return stats; - } - - deleteNode(path: AbsoluteFilePath) { - this.nodes.delete(path); - } - - addNode(path: AbsoluteFilePath, res: WorkerAnalyzeDependencyResult) { - const module = new DependencyNode( - this, - this.master.projectManager.getFileReference(path), - res, - ); - this.nodes.set(path, module); - return module; - } - - maybeGetNode(path: AbsoluteFilePath): undefined | DependencyNode { - return this.nodes.get(path); - } - - getNode(path: AbsoluteFilePath): DependencyNode { - const mod = this.maybeGetNode(path); - if (mod === undefined) { - throw new Error(`No module found for ${path.join()}`); - } - return mod; - } - - async seed( - { - paths, - diagnosticsProcessor, - analyzeProgress, - validate = false, - }: { - paths: Array; - diagnosticsProcessor: DiagnosticsProcessor; - analyzeProgress?: ReporterProgress; - validate?: boolean; - }, - ): Promise { - const workerQueue: DependencyGraphWorkerQueue = new WorkerQueue(this.master); - - workerQueue.addCallback(async (path, item) => { - await this.resolve( - path, - { - workerQueue, - all: item.all, - async: item.async, - ancestry: item.ancestry, - }, - diagnosticsProcessor, - analyzeProgress, - ); - }); - - // Add initial queue items - const roots: Array = await Promise.all( - paths.map((path) => - this.resolve( - path, - { - workerQueue, - all: true, - async: false, - ancestry: [], - }, - diagnosticsProcessor, - analyzeProgress, - ) - ), - ); - - await workerQueue.spin(); - - if (diagnosticsProcessor.hasDiagnostics()) { - return; - } - - if (validate) { - for (const root of roots) { - this.validateTransitive(root, diagnosticsProcessor); - } - } - } - - validate( - node: DependencyNode, - diagnosticsProcessor: DiagnosticsProcessor, - ): boolean { - const resolvedImports = node.resolveImports(); - return ( - diagnosticsProcessor.addDiagnostics(resolvedImports.diagnostics).length > - 0 - ); - } - - validateTransitive( - node: DependencyNode, - diagnosticsProcessor: DiagnosticsProcessor, - ) { - const order = node.getDependencyOrder(); - diagnosticsProcessor.addDiagnostics(order.diagnostics); - - for (const path of order.files) { - this.validate(this.getNode(path), diagnosticsProcessor); - } - } - - async resolve( - path: AbsoluteFilePath, - opts: { - all: boolean; - async: boolean; - ancestry: Array; - workerQueue: DependencyGraphWorkerQueue; - }, - diagnosticsProcessor: DiagnosticsProcessor, - analyzeProgress?: ReporterProgress, - ): Promise { - const filename = path.join(); - const {async, all, ancestry} = opts; - const {master} = this; - - // We have a lock here in case we hit `this.resolve` while we're waiting for the `analyzeDependencies` result - const lock = await this.locker.getLock(filename); - - if (this.nodes.has(path)) { - const node = this.getNode(path); - - if (all) { - node.setAll(true); - } - - if (async) { - node.setUsedAsync(true); - } - - lock.release(); - - return node; - } - - const progressText = markup``; - - if (analyzeProgress !== undefined) { - analyzeProgress.pushText(progressText); - } - - const res: WorkerAnalyzeDependencyResult = await this.request.requestWorkerAnalyzeDependencies( - path, - {}, - ); - - const node = this.addNode(path, res); - node.setAll(all); - node.setUsedAsync(async); - lock.release(); - - const {dependencies, diagnostics} = res; - - if (diagnostics.length > 0) { - diagnosticsProcessor.addDiagnostics(diagnostics); - } - - // If we're a remote path then the origin should be the URL and not our local path - const remote = this.master.projectManager.getRemoteFromLocalPath(path); - const origin = remote === undefined ? path : remote.getParent(); - - // Resolve full locations - await Promise.all( - dependencies.map(async (dep) => { - const {source, optional} = dep; - if (this.isExternal(source)) { - return; - } - - const {diagnostics} = await catchDiagnostics( - async () => { - const resolved = await master.resolver.resolveAssert( - { - ...this.resolverOpts, - origin, - source: createUnknownFilePath(source), - }, - dep.loc === undefined - ? undefined - : { - location: { - sourceText: undefined, - ...dep.loc, - language: 'js', - mtime: undefined, - }, - }, - ); - - node.addDependency(source, resolved.path, dep); - }, - { - category: 'DependencyGraph', - message: 'Caught by resolve', - }, - ); - - if (diagnostics !== undefined && !optional) { - diagnosticsProcessor.addDiagnostics(diagnostics); - } - }), - ); - - // Queue our dependencies... - const subAncestry = [...ancestry, filename]; - for (const path of node.getAbsoluteDependencies()) { - const dep = node.getDependencyInfoFromAbsolute(path).analyze; - await opts.workerQueue.pushQueue( - path, - { - all: dep.all, - async: dep.async, - type: dep.type, - loc: dep.loc, - ancestry: subAncestry, - }, - ); - } - - if (analyzeProgress !== undefined) { - analyzeProgress.popText(progressText); - analyzeProgress.tick(); - } - - return node; - } + constructor(request: MasterRequest, resolverOpts: ResolverOptions) { + this.request = request; + this.master = request.master; + this.nodes = new AbsoluteFilePathMap(); + this.resolverOpts = resolverOpts; + + this.locker = new Locker(); + this.closeEvent = new Event({name: 'DependencyGraph.closeEvent'}); + } + + request: MasterRequest; + resolverOpts: ResolverOptions; + master: Master; + nodes: AbsoluteFilePathMap; + locker: Locker; + closeEvent: Event; + + close() { + this.closeEvent.send(); + } + + isExternal(source: string): boolean { + return BUILTINS.includes(source); + } + + getBundleBuddyStats(entries: Array): BundleBuddyStats { + const stats: BundleBuddyStats = []; + + for (const node of this.nodes.values()) { + const source = node.uid; + + for (const absoluteTarget of node.relativeToAbsolutePath.values()) { + const target = this.getNode(absoluteTarget).uid; + stats.push({ + target, + source, + }); + } + } + + for (const absoluteEntry of entries) { + const source = this.getNode(absoluteEntry).uid; + stats.push({ + source, + target: undefined, + }); + } + + return stats; + } + + deleteNode(path: AbsoluteFilePath) { + this.nodes.delete(path); + } + + addNode(path: AbsoluteFilePath, res: WorkerAnalyzeDependencyResult) { + const module = new DependencyNode( + this, + this.master.projectManager.getFileReference(path), + res, + ); + this.nodes.set(path, module); + return module; + } + + maybeGetNode(path: AbsoluteFilePath): undefined | DependencyNode { + return this.nodes.get(path); + } + + getNode(path: AbsoluteFilePath): DependencyNode { + const mod = this.maybeGetNode(path); + if (mod === undefined) { + throw new Error(`No module found for ${path.join()}`); + } + return mod; + } + + async seed( + { + paths, + diagnosticsProcessor, + analyzeProgress, + validate = false, + }: { + paths: Array; + diagnosticsProcessor: DiagnosticsProcessor; + analyzeProgress?: ReporterProgress; + validate?: boolean; + }, + ): Promise { + const workerQueue: DependencyGraphWorkerQueue = new WorkerQueue(this.master); + + workerQueue.addCallback(async (path, item) => { + await this.resolve( + path, + { + workerQueue, + all: item.all, + async: item.async, + ancestry: item.ancestry, + }, + diagnosticsProcessor, + analyzeProgress, + ); + }); + + // Add initial queue items + const roots: Array = await Promise.all( + paths.map((path) => + this.resolve( + path, + { + workerQueue, + all: true, + async: false, + ancestry: [], + }, + diagnosticsProcessor, + analyzeProgress, + ) + ), + ); + + await workerQueue.spin(); + + if (diagnosticsProcessor.hasDiagnostics()) { + return; + } + + if (validate) { + for (const root of roots) { + this.validateTransitive(root, diagnosticsProcessor); + } + } + } + + validate( + node: DependencyNode, + diagnosticsProcessor: DiagnosticsProcessor, + ): boolean { + const resolvedImports = node.resolveImports(); + return ( + diagnosticsProcessor.addDiagnostics(resolvedImports.diagnostics).length > 0 + ); + } + + validateTransitive( + node: DependencyNode, + diagnosticsProcessor: DiagnosticsProcessor, + ) { + const order = node.getDependencyOrder(); + diagnosticsProcessor.addDiagnostics(order.diagnostics); + + for (const path of order.files) { + this.validate(this.getNode(path), diagnosticsProcessor); + } + } + + async resolve( + path: AbsoluteFilePath, + opts: { + all: boolean; + async: boolean; + ancestry: Array; + workerQueue: DependencyGraphWorkerQueue; + }, + diagnosticsProcessor: DiagnosticsProcessor, + analyzeProgress?: ReporterProgress, + ): Promise { + const filename = path.join(); + const {async, all, ancestry} = opts; + const {master} = this; + + // We have a lock here in case we hit `this.resolve` while we're waiting for the `analyzeDependencies` result + const lock = await this.locker.getLock(filename); + + if (this.nodes.has(path)) { + const node = this.getNode(path); + + if (all) { + node.setAll(true); + } + + if (async) { + node.setUsedAsync(true); + } + + lock.release(); + + return node; + } + + const progressText = markup``; + + if (analyzeProgress !== undefined) { + analyzeProgress.pushText(progressText); + } + + const res: WorkerAnalyzeDependencyResult = await this.request.requestWorkerAnalyzeDependencies( + path, + {}, + ); + + const node = this.addNode(path, res); + node.setAll(all); + node.setUsedAsync(async); + lock.release(); + + const {dependencies, diagnostics} = res; + + if (diagnostics.length > 0) { + diagnosticsProcessor.addDiagnostics(diagnostics); + } + + // If we're a remote path then the origin should be the URL and not our local path + const remote = this.master.projectManager.getRemoteFromLocalPath(path); + const origin = remote === undefined ? path : remote.getParent(); + + // Resolve full locations + await Promise.all( + dependencies.map(async (dep) => { + const {source, optional} = dep; + if (this.isExternal(source)) { + return; + } + + const {diagnostics} = await catchDiagnostics( + async () => { + const resolved = await master.resolver.resolveAssert( + { + ...this.resolverOpts, + origin, + source: createUnknownFilePath(source), + }, + dep.loc === undefined + ? undefined + : { + location: { + sourceText: undefined, + ...dep.loc, + language: 'js', + mtime: undefined, + }, + }, + ); + + node.addDependency(source, resolved.path, dep); + }, + { + category: 'DependencyGraph', + message: 'Caught by resolve', + }, + ); + + if (diagnostics !== undefined && !optional) { + diagnosticsProcessor.addDiagnostics(diagnostics); + } + }), + ); + + // Queue our dependencies... + const subAncestry = [...ancestry, filename]; + for (const path of node.getAbsoluteDependencies()) { + const dep = node.getDependencyInfoFromAbsolute(path).analyze; + await opts.workerQueue.pushQueue( + path, + { + all: dep.all, + async: dep.async, + type: dep.type, + loc: dep.loc, + ancestry: subAncestry, + }, + ); + } + + if (analyzeProgress !== undefined) { + analyzeProgress.popText(progressText); + analyzeProgress.tick(); + } + + return node; + } } diff --git a/packages/@romejs/core/master/dependencies/DependencyNode.ts b/packages/@romejs/core/master/dependencies/DependencyNode.ts index f504b7b0c37..6e0efff8708 100644 --- a/packages/@romejs/core/master/dependencies/DependencyNode.ts +++ b/packages/@romejs/core/master/dependencies/DependencyNode.ts @@ -10,10 +10,10 @@ import {BundleCompileResolvedImports} from '@romejs/js-compiler'; import {ConstImportModuleKind} from '@romejs/js-ast'; import {SourceLocation} from '@romejs/parser-core'; import { - Diagnostic, - DiagnosticLocation, - Diagnostics, - descriptions, + Diagnostic, + DiagnosticLocation, + Diagnostics, + descriptions, } from '@romejs/diagnostics'; import {ProjectDefinition} from '@romejs/project'; import DependencyOrderer, {DependencyOrder} from './DependencyOrderer'; @@ -22,496 +22,486 @@ import {AbsoluteFilePath, AbsoluteFilePathMap} from '@romejs/path'; import {getFileHandler} from '../../common/file-handlers/index'; import {ExtensionHandler} from '../../common/file-handlers/types'; import { - AnalyzeDependency, - AnalyzeDependencyName, - AnalyzeExportLocal, - AnalyzeModuleType, - AnyAnalyzeExport, + AnalyzeDependency, + AnalyzeDependencyName, + AnalyzeExportLocal, + AnalyzeModuleType, + AnyAnalyzeExport, } from '@romejs/core'; import {FileReference} from '@romejs/core/common/types/files'; type ResolvedImportFound = { - type: 'FOUND'; - node: DependencyNode; - record: AnalyzeExportLocal; + type: 'FOUND'; + node: DependencyNode; + record: AnalyzeExportLocal; }; type ResolvedImportNotFound = { - type: 'NOT_FOUND'; - node: DependencyNode; - loc: undefined | SourceLocation; - name: string; + type: 'NOT_FOUND'; + node: DependencyNode; + loc: undefined | SourceLocation; + name: string; }; type ResolvedImport = ResolvedImportFound | ResolvedImportNotFound; function equalKind( - producer: AnyAnalyzeExport, - consumerKind: ConstImportModuleKind, + producer: AnyAnalyzeExport, + consumerKind: ConstImportModuleKind, ): boolean { - // Allow importing functions and classes as `type` and `typeof` - if ( - producer.type === 'local' && - (producer.valueType === 'class' || producer.valueType === 'function') && - (consumerKind === 'type' || consumerKind === 'typeof') - ) { - return true; - } - - // You can only import a type or a class as a type - if (producer.kind === 'type') { - return consumerKind === 'type'; - } - - // You can only import a value as a value or typeof - if (producer.kind === 'value') { - return consumerKind === 'typeof' || consumerKind === 'value'; - } - - return false; + // Allow importing functions and classes as `type` and `typeof` + if ( + producer.type === 'local' && + (producer.valueType === 'class' || producer.valueType === 'function') && + (consumerKind === 'type' || consumerKind === 'typeof') + ) { + return true; + } + + // You can only import a type or a class as a type + if (producer.kind === 'type') { + return consumerKind === 'type'; + } + + // You can only import a value as a value or typeof + if (producer.kind === 'value') { + return consumerKind === 'typeof' || consumerKind === 'value'; + } + + return false; } type DependencyNodeDependency = { - analyze: AnalyzeDependency; - path: AbsoluteFilePath; + analyze: AnalyzeDependency; + path: AbsoluteFilePath; }; type ResolveImportsResult = { - diagnostics: Diagnostics; - resolved: BundleCompileResolvedImports; + diagnostics: Diagnostics; + resolved: BundleCompileResolvedImports; }; export default class DependencyNode { - constructor( - graph: DependencyGraph, - ref: FileReference, - res: WorkerAnalyzeDependencyResult, - ) { - this.graph = graph; - - this.project = graph.master.projectManager.assertProjectExisting(ref.real); - this.uid = ref.uid; - this.path = ref.real; - this.ref = ref; - this.type = res.moduleType; - - this.usedAsync = false; - this.all = false; - this.relativeToAbsolutePath = new Map(); - this.absoluteToAnalyzeDependency = new AbsoluteFilePathMap(); - - this.analyze = res; - - const {handler} = getFileHandler(ref.real, this.project.config); - this.handler = handler; - } - - analyze: WorkerAnalyzeDependencyResult; - graph: DependencyGraph; - relativeToAbsolutePath: Map; - absoluteToAnalyzeDependency: AbsoluteFilePathMap; - type: AnalyzeModuleType; - project: ProjectDefinition; - path: AbsoluteFilePath; - uid: string; - ref: FileReference; - all: boolean; - usedAsync: boolean; - handler: undefined | ExtensionHandler; - resolveImportsCache: undefined | ResolveImportsResult; - - getMtime(): number { - return this.graph.master.memoryFs.getMtime(this.path); - } - - setUsedAsync(usedAsync: boolean) { - this.usedAsync = usedAsync; - } - - setAll(all: boolean) { - this.all = all; - } - - hasEscapedExports(): boolean { - for (const exp of this.analyze.exports) { - if (exp.type === 'local' && exp.name === '*') { - return true; - } - } - return false; - } - - getDependents(): Array { - const dependents: Array = []; - for (const node of this.graph.nodes.values()) { - if (node.absoluteToAnalyzeDependency.has(this.path)) { - dependents.push(node); - } - } - return dependents; - } - - addDependency( - relative: string, - absolute: AbsoluteFilePath, - dep: AnalyzeDependency, - ) { - this.relativeToAbsolutePath.set(relative, absolute); - this.absoluteToAnalyzeDependency.set( - absolute, - { - analyze: dep, - path: absolute, - }, - ); - } - - getDependencyInfoFromAbsolute( - path: AbsoluteFilePath, - ): DependencyNodeDependency { - const dep = this.absoluteToAnalyzeDependency.get(path); - if (dep === undefined) { - throw new Error('Expected dependency'); - } - return dep; - } - - getNodeFromRelativeDependency(relative: string): DependencyNode { - const absolute = this.relativeToAbsolutePath.get(relative); - if (absolute === undefined) { - throw new Error(`Expected dependency ${relative} in ${this.path}`); - } - return this.graph.getNode(absolute); - } - - getAbsoluteDependencies(): Array { - return Array.from(this.relativeToAbsolutePath.values()); - } - - getTransitiveDependencies(): Array { - let queue: Array = [this]; - - const nodes: Set = new Set(); - - while (queue.length > 0) { - const node = queue.shift(); - if (node === undefined) { - throw new Error('Already validated queue.length'); - } - - nodes.add(node); - - for (const absolute of node.getAbsoluteDependencies()) { - const node = this.graph.getNode(absolute); - - if (!nodes.has(node)) { - queue.push(node); - } - } - } - - return Array.from(nodes); - } - - getDependencyOrder(): DependencyOrder { - const orderer = new DependencyOrderer(this.graph); - return orderer.order(this.path); - } - - // Get a list of all DependencyNodes where exports could be resolved. eg. `export *` - getExportedModules( - chain: Set = new Set(), - ): Set { - if (chain.has(this)) { - return new Set(); - } else { - chain.add(this); - } - - for (const exp of this.analyze.exports) { - if ( - exp.type === 'externalAll' && - this.relativeToAbsolutePath.has(exp.source) - ) { - this.getNodeFromRelativeDependency(exp.source).getExportedModules(chain); - } - } - - return chain; - } - - getExportedNames( - kind: ConstImportModuleKind, - seen: Set = new Set(), - ): Set { - if (seen.has(this)) { - return new Set(); - } else { - seen.add(this); - } - - let names: Set = new Set(); - - for (const exp of this.analyze.exports) { - if (!equalKind(exp, kind)) { - continue; - } - - switch (exp.type) { - case 'local': { - names.add(exp.name); - break; - } - - case 'external': { - const resolved = this.getNodeFromRelativeDependency(exp.source).resolveImport( - exp.imported, - exp.loc, - ); - if (resolved.type === 'FOUND' && equalKind(resolved.record, kind)) { - names.add(exp.exported); - } - break; - } - - case 'externalNamespace': { - names.add(exp.exported); - break; - } - - case 'externalAll': { - names = new Set([ - ...names, - ...this.getNodeFromRelativeDependency(exp.source).getExportedNames( - kind, - seen, - ), - ]); - break; - } - } - } - - return names; - } - - buildDiagnosticForUnknownExport( - kind: ConstImportModuleKind, - resolved: ResolvedImportNotFound, - ): Diagnostic { - const location: DiagnosticLocation = { - ...resolved.loc, - mtime: this.getMtime(), - }; - - const expectedName = resolved.name; - const fromSource = resolved.node.uid; - - // Check if there was a matching local in any of the exported modules - for (const mod of resolved.node.getExportedModules()) { - // We use an object as a hash map so need to check for pollution - if ( - Object.prototype.hasOwnProperty.call( - mod.analyze.topLevelLocalBindings, - expectedName, - ) - ) { - const localLoc = mod.analyze.topLevelLocalBindings[expectedName]; - if (localLoc !== undefined) { - return { - description: descriptions.RESOLVER.UNKNOWN_EXPORT_POSSIBLE_UNEXPORTED_LOCAL( - expectedName, - fromSource, - localLoc, - ), - location, - }; - } - } - } - - return { - description: descriptions.RESOLVER.UNKNOWN_EXPORT( - expectedName, - fromSource, - Array.from(resolved.node.getExportedNames(kind)), - (name: string) => { - const exportInfo = resolved.node.resolveImport(name, undefined); - - if (exportInfo.type === 'NOT_FOUND') { - throw new Error( - `mod.resolveImport returned NOT_FOUND for an export ${name} in ${exportInfo.node.path} despite being returned by getExportedNames`, - ); - } - - return { - location: exportInfo.record.loc, - source: exportInfo.node === resolved.node - ? undefined - : exportInfo.node.path.join(), - }; - }, - ), - location, - }; - } - - buildDiagnosticForTypeMismatch( - resolved: ResolvedImportFound, - node: DependencyNode, - nameInfo: AnalyzeDependencyName, - ): Diagnostic { - const {name, kind, loc} = nameInfo; - const {record} = resolved; - - return { - description: descriptions.RESOLVER.IMPORT_TYPE_MISMATCH( - name, - node.uid, - kind, - record.kind, - record.loc, - ), - location: { - ...loc, - mtime: this.getMtime(), - }, - }; - } - - resolveImports(): ResolveImportsResult { - const cached = this.resolveImportsCache; - if (cached !== undefined) { - return cached; - } - - const {graph} = this; - - // Build up a map of any forwarded imports - const resolvedImports: BundleCompileResolvedImports = {}; - - // Diagnostics for unknown imports - const diagnostics: Diagnostics = []; - - // Go through all of our dependencies and check if they have any external exports to forward - const allowTypeImportsAsValue = this.analyze.syntax.includes('ts'); - for (const absolute of this.relativeToAbsolutePath.values()) { - const mod = graph.getNode(absolute); - - // We can't follow CJS names - if (mod.type === 'cjs') { - continue; - } - - const usedNames = this.getDependencyInfoFromAbsolute(absolute).analyze.names; - - // Try to resolve these exports - for (const nameInfo of usedNames) { - const {name, kind, loc} = nameInfo; - if (kind === 'type' || kind === 'typeof') { - // Disable resolving typed imports for now as there's ridiculous code that hides some behind $FlowFixMe - continue; - } - - const resolved = mod.resolveImport(name, loc); - - // Unknown import - if (resolved.type === 'NOT_FOUND') { - diagnostics.push(this.buildDiagnosticForUnknownExport(kind, resolved)); - continue; - } - - // Flag imports of the wrong type - if (!allowTypeImportsAsValue && !equalKind(resolved.record, kind)) { - diagnostics.push( - this.buildDiagnosticForTypeMismatch(resolved, mod, nameInfo), - ); - continue; - } - - // If the resolved target isn't the same as the file then forward it - if (resolved.node.uid !== mod.uid) { - resolvedImports[`${mod.uid}:${name}`] = { - id: resolved.node.uid, - name: resolved.record.name, - }; - } - } - } - - const result: ResolveImportsResult = { - resolved: resolvedImports, - diagnostics, - }; - this.resolveImportsCache = result; - return result; - } - - resolveImport( - name: string, - loc: undefined | SourceLocation, - ignoreDefault: boolean = false, - ancestry: Array = [], - ): ResolvedImport { - if (ancestry.includes(this)) { - return { - type: 'NOT_FOUND', - loc, - node: this, - name, - }; - } - - const subAncestry: Array = [...ancestry, this]; - - // We always want to resolve exports from the bottom up - const exports = this.analyze.exports.reverse(); - - for (const record of exports) { - // When resolving exportAll we never want to include the default export of those modules - if (record.type === 'local' && record.name === 'default' && ignoreDefault) { - continue; - } - - if ( - record.type === 'local' && - (record.name === name || record.name === '*') - ) { - return { - type: 'FOUND', - node: this, - record, - }; - } - - if (record.type === 'external' && record.exported === name) { - return this.getNodeFromRelativeDependency(record.source).resolveImport( - record.imported, - record.loc, - false, - subAncestry, - ); - } - - if (record.type === 'externalAll') { - const resolved = this.getNodeFromRelativeDependency(record.source).resolveImport( - name, - record.loc, - true, - subAncestry, - ); - - if (resolved.type === 'FOUND') { - return resolved; - } - } - } - - return { - type: 'NOT_FOUND', - loc, - node: this, - name, - }; - } + constructor( + graph: DependencyGraph, + ref: FileReference, + res: WorkerAnalyzeDependencyResult, + ) { + this.graph = graph; + + this.project = graph.master.projectManager.assertProjectExisting(ref.real); + this.uid = ref.uid; + this.path = ref.real; + this.ref = ref; + this.type = res.moduleType; + + this.usedAsync = false; + this.all = false; + this.relativeToAbsolutePath = new Map(); + this.absoluteToAnalyzeDependency = new AbsoluteFilePathMap(); + + this.analyze = res; + + const {handler} = getFileHandler(ref.real, this.project.config); + this.handler = handler; + } + + analyze: WorkerAnalyzeDependencyResult; + graph: DependencyGraph; + relativeToAbsolutePath: Map; + absoluteToAnalyzeDependency: AbsoluteFilePathMap; + type: AnalyzeModuleType; + project: ProjectDefinition; + path: AbsoluteFilePath; + uid: string; + ref: FileReference; + all: boolean; + usedAsync: boolean; + handler: undefined | ExtensionHandler; + resolveImportsCache: undefined | ResolveImportsResult; + + getMtime(): number { + return this.graph.master.memoryFs.getMtime(this.path); + } + + setUsedAsync(usedAsync: boolean) { + this.usedAsync = usedAsync; + } + + setAll(all: boolean) { + this.all = all; + } + + hasEscapedExports(): boolean { + for (const exp of this.analyze.exports) { + if (exp.type === 'local' && exp.name === '*') { + return true; + } + } + return false; + } + + getDependents(): Array { + const dependents: Array = []; + for (const node of this.graph.nodes.values()) { + if (node.absoluteToAnalyzeDependency.has(this.path)) { + dependents.push(node); + } + } + return dependents; + } + + addDependency( + relative: string, + absolute: AbsoluteFilePath, + dep: AnalyzeDependency, + ) { + this.relativeToAbsolutePath.set(relative, absolute); + this.absoluteToAnalyzeDependency.set( + absolute, + { + analyze: dep, + path: absolute, + }, + ); + } + + getDependencyInfoFromAbsolute(path: AbsoluteFilePath): DependencyNodeDependency { + const dep = this.absoluteToAnalyzeDependency.get(path); + if (dep === undefined) { + throw new Error('Expected dependency'); + } + return dep; + } + + getNodeFromRelativeDependency(relative: string): DependencyNode { + const absolute = this.relativeToAbsolutePath.get(relative); + if (absolute === undefined) { + throw new Error(`Expected dependency ${relative} in ${this.path}`); + } + return this.graph.getNode(absolute); + } + + getAbsoluteDependencies(): Array { + return Array.from(this.relativeToAbsolutePath.values()); + } + + getTransitiveDependencies(): Array { + let queue: Array = [this]; + + const nodes: Set = new Set(); + + while (queue.length > 0) { + const node = queue.shift(); + if (node === undefined) { + throw new Error('Already validated queue.length'); + } + + nodes.add(node); + + for (const absolute of node.getAbsoluteDependencies()) { + const node = this.graph.getNode(absolute); + + if (!nodes.has(node)) { + queue.push(node); + } + } + } + + return Array.from(nodes); + } + + getDependencyOrder(): DependencyOrder { + const orderer = new DependencyOrderer(this.graph); + return orderer.order(this.path); + } + + // Get a list of all DependencyNodes where exports could be resolved. eg. `export *` + getExportedModules(chain: Set = new Set()): Set { + if (chain.has(this)) { + return new Set(); + } else { + chain.add(this); + } + + for (const exp of this.analyze.exports) { + if (exp.type === 'externalAll' && this.relativeToAbsolutePath.has(exp.source)) { + this.getNodeFromRelativeDependency(exp.source).getExportedModules(chain); + } + } + + return chain; + } + + getExportedNames( + kind: ConstImportModuleKind, + seen: Set = new Set(), + ): Set { + if (seen.has(this)) { + return new Set(); + } else { + seen.add(this); + } + + let names: Set = new Set(); + + for (const exp of this.analyze.exports) { + if (!equalKind(exp, kind)) { + continue; + } + + switch (exp.type) { + case 'local': { + names.add(exp.name); + break; + } + + case 'external': { + const resolved = this.getNodeFromRelativeDependency(exp.source).resolveImport( + exp.imported, + exp.loc, + ); + if (resolved.type === 'FOUND' && equalKind(resolved.record, kind)) { + names.add(exp.exported); + } + break; + } + + case 'externalNamespace': { + names.add(exp.exported); + break; + } + + case 'externalAll': { + names = new Set([ + ...names, + ...this.getNodeFromRelativeDependency(exp.source).getExportedNames( + kind, + seen, + ), + ]); + break; + } + } + } + + return names; + } + + buildDiagnosticForUnknownExport( + kind: ConstImportModuleKind, + resolved: ResolvedImportNotFound, + ): Diagnostic { + const location: DiagnosticLocation = { + ...resolved.loc, + mtime: this.getMtime(), + }; + + const expectedName = resolved.name; + const fromSource = resolved.node.uid; + + // Check if there was a matching local in any of the exported modules + for (const mod of resolved.node.getExportedModules()) { + // We use an object as a hash map so need to check for pollution + if ( + Object.prototype.hasOwnProperty.call( + mod.analyze.topLevelLocalBindings, + expectedName, + ) + ) { + const localLoc = mod.analyze.topLevelLocalBindings[expectedName]; + if (localLoc !== undefined) { + return { + description: descriptions.RESOLVER.UNKNOWN_EXPORT_POSSIBLE_UNEXPORTED_LOCAL( + expectedName, + fromSource, + localLoc, + ), + location, + }; + } + } + } + + return { + description: descriptions.RESOLVER.UNKNOWN_EXPORT( + expectedName, + fromSource, + Array.from(resolved.node.getExportedNames(kind)), + (name: string) => { + const exportInfo = resolved.node.resolveImport(name, undefined); + + if (exportInfo.type === 'NOT_FOUND') { + throw new Error( + `mod.resolveImport returned NOT_FOUND for an export ${name} in ${exportInfo.node.path} despite being returned by getExportedNames`, + ); + } + + return { + location: exportInfo.record.loc, + source: exportInfo.node === resolved.node + ? undefined + : exportInfo.node.path.join(), + }; + }, + ), + location, + }; + } + + buildDiagnosticForTypeMismatch( + resolved: ResolvedImportFound, + node: DependencyNode, + nameInfo: AnalyzeDependencyName, + ): Diagnostic { + const {name, kind, loc} = nameInfo; + const {record} = resolved; + + return { + description: descriptions.RESOLVER.IMPORT_TYPE_MISMATCH( + name, + node.uid, + kind, + record.kind, + record.loc, + ), + location: { + ...loc, + mtime: this.getMtime(), + }, + }; + } + + resolveImports(): ResolveImportsResult { + const cached = this.resolveImportsCache; + if (cached !== undefined) { + return cached; + } + + const {graph} = this; + + // Build up a map of any forwarded imports + const resolvedImports: BundleCompileResolvedImports = {}; + + // Diagnostics for unknown imports + const diagnostics: Diagnostics = []; + + // Go through all of our dependencies and check if they have any external exports to forward + const allowTypeImportsAsValue = this.analyze.syntax.includes('ts'); + for (const absolute of this.relativeToAbsolutePath.values()) { + const mod = graph.getNode(absolute); + + // We can't follow CJS names + if (mod.type === 'cjs') { + continue; + } + + const usedNames = this.getDependencyInfoFromAbsolute(absolute).analyze.names; + + // Try to resolve these exports + for (const nameInfo of usedNames) { + const {name, kind, loc} = nameInfo; + if (kind === 'type' || kind === 'typeof') { + // Disable resolving typed imports for now as there's ridiculous code that hides some behind $FlowFixMe + continue; + } + + const resolved = mod.resolveImport(name, loc); + + // Unknown import + if (resolved.type === 'NOT_FOUND') { + diagnostics.push(this.buildDiagnosticForUnknownExport(kind, resolved)); + continue; + } + + // Flag imports of the wrong type + if (!allowTypeImportsAsValue && !equalKind(resolved.record, kind)) { + diagnostics.push( + this.buildDiagnosticForTypeMismatch(resolved, mod, nameInfo), + ); + continue; + } + + // If the resolved target isn't the same as the file then forward it + if (resolved.node.uid !== mod.uid) { + resolvedImports[`${mod.uid}:${name}`] = { + id: resolved.node.uid, + name: resolved.record.name, + }; + } + } + } + + const result: ResolveImportsResult = { + resolved: resolvedImports, + diagnostics, + }; + this.resolveImportsCache = result; + return result; + } + + resolveImport( + name: string, + loc: undefined | SourceLocation, + ignoreDefault: boolean = false, + ancestry: Array = [], + ): ResolvedImport { + if (ancestry.includes(this)) { + return { + type: 'NOT_FOUND', + loc, + node: this, + name, + }; + } + + const subAncestry: Array = [...ancestry, this]; + + // We always want to resolve exports from the bottom up + const exports = this.analyze.exports.reverse(); + + for (const record of exports) { + // When resolving exportAll we never want to include the default export of those modules + if (record.type === 'local' && record.name === 'default' && ignoreDefault) { + continue; + } + + if (record.type === 'local' && (record.name === name || record.name === '*')) { + return { + type: 'FOUND', + node: this, + record, + }; + } + + if (record.type === 'external' && record.exported === name) { + return this.getNodeFromRelativeDependency(record.source).resolveImport( + record.imported, + record.loc, + false, + subAncestry, + ); + } + + if (record.type === 'externalAll') { + const resolved = this.getNodeFromRelativeDependency(record.source).resolveImport( + name, + record.loc, + true, + subAncestry, + ); + + if (resolved.type === 'FOUND') { + return resolved; + } + } + } + + return { + type: 'NOT_FOUND', + loc, + node: this, + name, + }; + } } diff --git a/packages/@romejs/core/master/dependencies/DependencyOrderer.ts b/packages/@romejs/core/master/dependencies/DependencyOrderer.ts index b0e0ab8e192..478b7d9f21d 100644 --- a/packages/@romejs/core/master/dependencies/DependencyOrderer.ts +++ b/packages/@romejs/core/master/dependencies/DependencyOrderer.ts @@ -13,157 +13,156 @@ import {Diagnostics, descriptions} from '@romejs/diagnostics'; import {AbsoluteFilePath} from '@romejs/path'; type FirstTopAwaitLocations = Array<{ - mtime: number; - loc: SourceLocation; + mtime: number; + loc: SourceLocation; }>; export type DependencyOrder = { - diagnostics: Diagnostics; - firstTopAwaitLocations: FirstTopAwaitLocations; - files: Array; + diagnostics: Diagnostics; + firstTopAwaitLocations: FirstTopAwaitLocations; + files: Array; }; export default class DependencyOrderer { - constructor(graph: DependencyGraph) { - this.graph = graph; - this.orderedNodes = new Set(); - this.visitedNodes = new Set(); - this.possibleCyclePaths = new Map(); - this.diagnostics = []; - this.firstTopAwaitLocations = []; - } - - firstTopAwaitLocations: FirstTopAwaitLocations; - orderedNodes: Set; - visitedNodes: Set; - possibleCyclePaths: Map>; - diagnostics: Diagnostics; - graph: DependencyGraph; - - handleAlreadyVisitedFile( - node: DependencyNode, - path: AbsoluteFilePath, - ancestry: Array, - ) { - const filename = path.join(); - - // We flag a possible cycle when a dependency has yet to have it's own transitive dependencies resolve but it ends up going back to itself - const isPossibleCycle = - this.orderedNodes.has(node) === false && ancestry.includes(filename); - if (isPossibleCycle) { - const ourCyclePath = ancestry.concat([filename]); - const existingCycle = this.possibleCyclePaths.get(node); - - // We want to get the shortest cycle path since it's likely the most easily resolved - const isShortestCycle = - existingCycle === undefined || - existingCycle.length > ourCyclePath.length; - if (isShortestCycle) { - this.possibleCyclePaths.set(node, ourCyclePath); - } - } - } - - addFile(path: AbsoluteFilePath, ancestry: Array): void { - const node = this.graph.getNode(path); - - if (this.visitedNodes.has(node)) { - this.handleAlreadyVisitedFile(node, path, ancestry); - return; - } - - this.visitedNodes.add(node); - - const {firstTopAwaitLocation} = node.analyze; - if (firstTopAwaitLocation !== undefined) { - this.firstTopAwaitLocations.push({ - mtime: node.getMtime(), - loc: firstTopAwaitLocation, - }); - } - - const subAncestry = ancestry.concat([path.join()]); - for (const depPath of node.getAbsoluteDependencies()) { - const dep = node.getDependencyInfoFromAbsolute(depPath).analyze; - if (dep.kind === 'value') { - this.addFile(depPath, subAncestry); - } - } - - this.orderedNodes.add(node); - } - - // We detect cycles by determining if there were any references to imports at the top level that - // are for a module that will be initialized before - detectCycles() { - const flatOrder = Array.from(this.orderedNodes); - - for (let i = 0; i < flatOrder.length; i++) { - const node = flatOrder[i]; - - for (const imp of node.analyze.importFirstUsage) { - const resolved = node.getNodeFromRelativeDependency(imp.source).resolveImport( - imp.imported, - imp.loc, - ); - if (resolved.type !== 'FOUND') { - continue; - } - - // Hoisted exports will always be accessible - if (resolved.record.valueType === 'function') { - continue; - } - - const dep = resolved.node; - - const isBefore = flatOrder.indexOf(dep) > i; - if (isBefore) { - this.flagCycle(node, dep, imp); - } - } - } - } - - flagCycle( - node: DependencyNode, - dep: DependencyNode, - imp: AnalyzeDependencyImportUsageItem, - ): void { - const path = this.possibleCyclePaths.get(dep); - if (!path) { - // idk?? - return; - } - - const target = path[path.length - 1]; - const culprit = String( - path.find((value, index) => path[index - 1] === target), - ); - - this.diagnostics.push({ - description: descriptions.BUNDLER.DETECTED_CYCLE( - imp.local, - target, - culprit, - path, - ), - location: { - filename: node.path.join(), - mtime: node.getMtime(), - start: imp.loc === undefined ? undefined : imp.loc.start, - end: imp.loc === undefined ? undefined : imp.loc.end, - }, - }); - } - - order(path: AbsoluteFilePath): DependencyOrder { - this.addFile(path, []); - this.detectCycles(); - return { - firstTopAwaitLocations: this.firstTopAwaitLocations, - diagnostics: this.diagnostics, - files: Array.from(this.orderedNodes, (node) => node.path), - }; - } + constructor(graph: DependencyGraph) { + this.graph = graph; + this.orderedNodes = new Set(); + this.visitedNodes = new Set(); + this.possibleCyclePaths = new Map(); + this.diagnostics = []; + this.firstTopAwaitLocations = []; + } + + firstTopAwaitLocations: FirstTopAwaitLocations; + orderedNodes: Set; + visitedNodes: Set; + possibleCyclePaths: Map>; + diagnostics: Diagnostics; + graph: DependencyGraph; + + handleAlreadyVisitedFile( + node: DependencyNode, + path: AbsoluteFilePath, + ancestry: Array, + ) { + const filename = path.join(); + + // We flag a possible cycle when a dependency has yet to have it's own transitive dependencies resolve but it ends up going back to itself + const isPossibleCycle = + this.orderedNodes.has(node) === false && ancestry.includes(filename); + if (isPossibleCycle) { + const ourCyclePath = ancestry.concat([filename]); + const existingCycle = this.possibleCyclePaths.get(node); + + // We want to get the shortest cycle path since it's likely the most easily resolved + const isShortestCycle = + existingCycle === undefined || existingCycle.length > ourCyclePath.length; + if (isShortestCycle) { + this.possibleCyclePaths.set(node, ourCyclePath); + } + } + } + + addFile(path: AbsoluteFilePath, ancestry: Array): void { + const node = this.graph.getNode(path); + + if (this.visitedNodes.has(node)) { + this.handleAlreadyVisitedFile(node, path, ancestry); + return; + } + + this.visitedNodes.add(node); + + const {firstTopAwaitLocation} = node.analyze; + if (firstTopAwaitLocation !== undefined) { + this.firstTopAwaitLocations.push({ + mtime: node.getMtime(), + loc: firstTopAwaitLocation, + }); + } + + const subAncestry = ancestry.concat([path.join()]); + for (const depPath of node.getAbsoluteDependencies()) { + const dep = node.getDependencyInfoFromAbsolute(depPath).analyze; + if (dep.kind === 'value') { + this.addFile(depPath, subAncestry); + } + } + + this.orderedNodes.add(node); + } + + // We detect cycles by determining if there were any references to imports at the top level that + // are for a module that will be initialized before + detectCycles() { + const flatOrder = Array.from(this.orderedNodes); + + for (let i = 0; i < flatOrder.length; i++) { + const node = flatOrder[i]; + + for (const imp of node.analyze.importFirstUsage) { + const resolved = node.getNodeFromRelativeDependency(imp.source).resolveImport( + imp.imported, + imp.loc, + ); + if (resolved.type !== 'FOUND') { + continue; + } + + // Hoisted exports will always be accessible + if (resolved.record.valueType === 'function') { + continue; + } + + const dep = resolved.node; + + const isBefore = flatOrder.indexOf(dep) > i; + if (isBefore) { + this.flagCycle(node, dep, imp); + } + } + } + } + + flagCycle( + node: DependencyNode, + dep: DependencyNode, + imp: AnalyzeDependencyImportUsageItem, + ): void { + const path = this.possibleCyclePaths.get(dep); + if (!path) { + // idk?? + return; + } + + const target = path[path.length - 1]; + const culprit = String( + path.find((value, index) => path[index - 1] === target), + ); + + this.diagnostics.push({ + description: descriptions.BUNDLER.DETECTED_CYCLE( + imp.local, + target, + culprit, + path, + ), + location: { + filename: node.path.join(), + mtime: node.getMtime(), + start: imp.loc === undefined ? undefined : imp.loc.start, + end: imp.loc === undefined ? undefined : imp.loc.end, + }, + }); + } + + order(path: AbsoluteFilePath): DependencyOrder { + this.addFile(path, []); + this.detectCycles(); + return { + firstTopAwaitLocations: this.firstTopAwaitLocations, + diagnostics: this.diagnostics, + files: Array.from(this.orderedNodes, (node) => node.path), + }; + } } diff --git a/packages/@romejs/core/master/fs/FileAllocator.ts b/packages/@romejs/core/master/fs/FileAllocator.ts index c2b04a0964f..021ac05f806 100644 --- a/packages/@romejs/core/master/fs/FileAllocator.ts +++ b/packages/@romejs/core/master/fs/FileAllocator.ts @@ -13,180 +13,180 @@ import {AbsoluteFilePath} from '@romejs/path'; import {Event} from '@romejs/events'; export default class FileAllocator { - constructor(master: Master) { - this.master = master; - this.fileToWorker = new Map(); - this.locker = new Locker(); - this.evictEvent = new Event({ - name: 'evict', - }); - } - - evictEvent: Event; - master: Master; - locker: Locker; - fileToWorker: Map; - - init() { - this.master.memoryFs.deletedFileEvent.subscribe((path) => { - return this.handleDeleted(path); - }); - - this.master.memoryFs.changedFileEvent.subscribe(({path, oldStats, newStats}) => { - return this.handleChange(path, oldStats, newStats); - }); - } - - getAllOwnedFilenames(): Array { - return Array.from(this.fileToWorker.keys()); - } - - hasOwner(path: AbsoluteFilePath): boolean { - return this.getOwnerId(path) !== undefined; - } - - getOwnerId(path: AbsoluteFilePath): undefined | number { - return this.fileToWorker.get(path.join()); - } - - verifySize(path: AbsoluteFilePath, stats: Stats) { - const project = this.master.projectManager.assertProjectExisting(path); - const maxSize = project.config.files.maxSize; - - if (stats.size > maxSize) { - throw new Error( - `The file ${path.join()} exceeds the project config max size of ${maxSize} bytes`, - ); - } - } - - getOwnerAssert(path: AbsoluteFilePath): WorkerContainer { - const {workerManager} = this.master; - const workerId = this.getOwnerId(path); - if (workerId === undefined) { - throw new Error(`No worker found for ${path}`); - } - - const worker = workerManager.getWorkerAssert(workerId); - if (!worker.ready) { - throw new Error(`Worker ${workerId} isn't ready`); - } - return worker; - } - - async getOrAssignOwner(path: AbsoluteFilePath): Promise { - const {workerManager} = this.master; - - const workerId = this.getOwnerId(path); - if (workerId === undefined) { - return this.assignOwner(path); - } else { - await workerManager.locker.waitLock(workerId); - return workerManager.getWorkerAssert(workerId); - } - } - - async evict(path: AbsoluteFilePath) { - // Find owner - const workerId = this.getOwnerId(path); - if (workerId === undefined) { - return; - } - - // Notify the worker to remove it from 'it's cache - const filename = path.join(); - const worker = this.master.workerManager.getWorkerAssert(workerId); - await worker.bridge.evict.call({ - filename, - }); - this.evictEvent.send(path); - - this.master.logger.info(`[FileAllocator] Evicted %s`, filename); - } - - async handleDeleted(path: AbsoluteFilePath) { - // Find owner - const workerId = this.getOwnerId(path); - if (workerId === undefined) { - return; - } - - // Evict file from 'worker cache - await this.evict(path); - - // Disown it from 'our internal map - this.fileToWorker.delete(path.join()); - - // Remove the total size from 'this worker so it'll be assigned next - const stats = this.master.memoryFs.getFileStatsAssert(path); - this.master.workerManager.disown(workerId, stats); - } - - async handleChange( - path: AbsoluteFilePath, - oldStats: undefined | Stats, - newStats: Stats, - ) { - const filename = path.join(); - const {logger, workerManager} = this.master; - - // Send update to worker owner - if (this.hasOwner(path)) { - // Get the worker - const workerId = this.getOwnerId(path); - if (workerId === undefined) { - throw new Error(`Expected worker id for ${filename}`); - } - - // Evict the file from 'cache - await this.evict(path); - - // Verify that this file doesn't exceed any size limit - this.verifySize(path, newStats); - - // Add on the new size, and remove the old - if (oldStats === undefined) { - throw new Error( - 'File already has an owner so expected to have old stats but had none', - ); - } - workerManager.disown(workerId, oldStats); - workerManager.own(workerId, newStats); - } else if (await this.master.projectManager.maybeEvictPossibleConfig(path)) { - logger.info( - `[FileAllocator] Evicted the project belonging to config %s`, - filename, - ); - } else { - logger.info(`[FileAllocator] No owner for eviction %s`, filename); - } - } - - async assignOwner(path: AbsoluteFilePath): Promise { - const {workerManager, logger} = this.master; - - const filename = path.join(); - const lock = await this.locker.getLock(filename); - - // We may have waited on the lock and could already have an owner - if (this.hasOwner(path)) { - lock.release(); - return this.getOwnerAssert(path); - } - - const worker = await workerManager.getNextWorker(path); - - // Add ourselves to the file map - logger.info( - `[FileAllocator] File %s assigned to worker %s`, - filename, - worker.id, - ); - this.fileToWorker.set(filename, worker.id); - - // Release and continue - lock.release(); - - return worker; - } + constructor(master: Master) { + this.master = master; + this.fileToWorker = new Map(); + this.locker = new Locker(); + this.evictEvent = new Event({ + name: 'evict', + }); + } + + evictEvent: Event; + master: Master; + locker: Locker; + fileToWorker: Map; + + init() { + this.master.memoryFs.deletedFileEvent.subscribe((path) => { + return this.handleDeleted(path); + }); + + this.master.memoryFs.changedFileEvent.subscribe(({path, oldStats, newStats}) => { + return this.handleChange(path, oldStats, newStats); + }); + } + + getAllOwnedFilenames(): Array { + return Array.from(this.fileToWorker.keys()); + } + + hasOwner(path: AbsoluteFilePath): boolean { + return this.getOwnerId(path) !== undefined; + } + + getOwnerId(path: AbsoluteFilePath): undefined | number { + return this.fileToWorker.get(path.join()); + } + + verifySize(path: AbsoluteFilePath, stats: Stats) { + const project = this.master.projectManager.assertProjectExisting(path); + const maxSize = project.config.files.maxSize; + + if (stats.size > maxSize) { + throw new Error( + `The file ${path.join()} exceeds the project config max size of ${maxSize} bytes`, + ); + } + } + + getOwnerAssert(path: AbsoluteFilePath): WorkerContainer { + const {workerManager} = this.master; + const workerId = this.getOwnerId(path); + if (workerId === undefined) { + throw new Error(`No worker found for ${path}`); + } + + const worker = workerManager.getWorkerAssert(workerId); + if (!worker.ready) { + throw new Error(`Worker ${workerId} isn't ready`); + } + return worker; + } + + async getOrAssignOwner(path: AbsoluteFilePath): Promise { + const {workerManager} = this.master; + + const workerId = this.getOwnerId(path); + if (workerId === undefined) { + return this.assignOwner(path); + } else { + await workerManager.locker.waitLock(workerId); + return workerManager.getWorkerAssert(workerId); + } + } + + async evict(path: AbsoluteFilePath) { + // Find owner + const workerId = this.getOwnerId(path); + if (workerId === undefined) { + return; + } + + // Notify the worker to remove it from 'it's cache + const filename = path.join(); + const worker = this.master.workerManager.getWorkerAssert(workerId); + await worker.bridge.evict.call({ + filename, + }); + this.evictEvent.send(path); + + this.master.logger.info(`[FileAllocator] Evicted %s`, filename); + } + + async handleDeleted(path: AbsoluteFilePath) { + // Find owner + const workerId = this.getOwnerId(path); + if (workerId === undefined) { + return; + } + + // Evict file from 'worker cache + await this.evict(path); + + // Disown it from 'our internal map + this.fileToWorker.delete(path.join()); + + // Remove the total size from 'this worker so it'll be assigned next + const stats = this.master.memoryFs.getFileStatsAssert(path); + this.master.workerManager.disown(workerId, stats); + } + + async handleChange( + path: AbsoluteFilePath, + oldStats: undefined | Stats, + newStats: Stats, + ) { + const filename = path.join(); + const {logger, workerManager} = this.master; + + // Send update to worker owner + if (this.hasOwner(path)) { + // Get the worker + const workerId = this.getOwnerId(path); + if (workerId === undefined) { + throw new Error(`Expected worker id for ${filename}`); + } + + // Evict the file from 'cache + await this.evict(path); + + // Verify that this file doesn't exceed any size limit + this.verifySize(path, newStats); + + // Add on the new size, and remove the old + if (oldStats === undefined) { + throw new Error( + 'File already has an owner so expected to have old stats but had none', + ); + } + workerManager.disown(workerId, oldStats); + workerManager.own(workerId, newStats); + } else if (await this.master.projectManager.maybeEvictPossibleConfig(path)) { + logger.info( + `[FileAllocator] Evicted the project belonging to config %s`, + filename, + ); + } else { + logger.info(`[FileAllocator] No owner for eviction %s`, filename); + } + } + + async assignOwner(path: AbsoluteFilePath): Promise { + const {workerManager, logger} = this.master; + + const filename = path.join(); + const lock = await this.locker.getLock(filename); + + // We may have waited on the lock and could already have an owner + if (this.hasOwner(path)) { + lock.release(); + return this.getOwnerAssert(path); + } + + const worker = await workerManager.getNextWorker(path); + + // Add ourselves to the file map + logger.info( + `[FileAllocator] File %s assigned to worker %s`, + filename, + worker.id, + ); + this.fileToWorker.set(filename, worker.id); + + // Release and continue + lock.release(); + + return worker; + } } diff --git a/packages/@romejs/core/master/fs/MemoryFileSystem.ts b/packages/@romejs/core/master/fs/MemoryFileSystem.ts index 2d747981503..524a8ca0d73 100644 --- a/packages/@romejs/core/master/fs/MemoryFileSystem.ts +++ b/packages/@romejs/core/master/fs/MemoryFileSystem.ts @@ -7,19 +7,19 @@ import Master from '../Master'; import { - Manifest, - ManifestDefinition, - normalizeManifest, + Manifest, + ManifestDefinition, + normalizeManifest, } from '@romejs/codec-js-manifest'; import { - PathPatterns, - matchPathPatterns, - parsePathPattern, + PathPatterns, + matchPathPatterns, + parsePathPattern, } from '@romejs/path-match'; import { - ProjectConfig, - ProjectDefinition, - ROME_CONFIG_FILENAMES, + ProjectConfig, + ProjectDefinition, + ROME_CONFIG_FILENAMES, } from '@romejs/project'; import {DiagnosticsProcessor, catchDiagnostics} from '@romejs/diagnostics'; import {Event} from '@romejs/events'; @@ -27,9 +27,9 @@ import {consumeJSON} from '@romejs/codec-json'; import {humanizeNumber} from '@romejs/string-utils'; import {WorkerPartialManifest} from '../../common/bridges/WorkerBridge'; import { - AbsoluteFilePath, - AbsoluteFilePathMap, - AbsoluteFilePathSet, + AbsoluteFilePath, + AbsoluteFilePathMap, + AbsoluteFilePathSet, } from '@romejs/path'; import {exists, lstat, readFileText, readdir, watch} from '@romejs/fs'; import {getFileHandler} from '../../common/file-handlers/index'; @@ -40,889 +40,884 @@ import fs = require('fs'); const DEFAULT_DENYLIST = ['.hg', '.git']; const GLOB_IGNORE: PathPatterns = [ - parsePathPattern({input: 'node_modules'}), - parsePathPattern({input: '.git'}), - parsePathPattern({input: '.hg'}), + parsePathPattern({input: 'node_modules'}), + parsePathPattern({input: '.git'}), + parsePathPattern({input: '.hg'}), ]; function concatGlobIgnore(patterns: PathPatterns): PathPatterns { - // If there are any negate patterns then it'll never include GLOB_IGNORE - for (const {negate} of patterns) { - if (negate) { - return patterns; - } - } - - return [...GLOB_IGNORE, ...patterns]; + // If there are any negate patterns then it'll never include GLOB_IGNORE + for (const {negate} of patterns) { + if (negate) { + return patterns; + } + } + + return [...GLOB_IGNORE, ...patterns]; } function isValidManifest(path: AbsoluteFilePath): boolean { - if (path.getBasename() !== 'package.json') { - return false; - } - - // If a manifest is in node_modules, then make sure we're directly inside - // a folder in node_modules. - // - // For unscoped package, the segments should be: - // -1: package.json - // -2: module folder - // -3: node_modules - // - // For scoped package (@scope/some-module), the segments should be: - // -1: package.json - // -2: module folder - // -3: scope folder - // -4: node_modules - const segments = path.getSegments(); - if (segments.includes('node_modules')) { - // Unscoped package - if (segments[segments.length - 3] === 'node_modules') { - return true; - } - - // Scoped module - if ( - segments[segments.length - 4] === 'node_modules' && - segments[segments.length - 3].startsWith('@') - ) { - return true; - } - - return false; - } - - return true; + if (path.getBasename() !== 'package.json') { + return false; + } + + // If a manifest is in node_modules, then make sure we're directly inside + // a folder in node_modules. + // + // For unscoped package, the segments should be: + // -1: package.json + // -2: module folder + // -3: node_modules + // + // For scoped package (@scope/some-module), the segments should be: + // -1: package.json + // -2: module folder + // -3: scope folder + // -4: node_modules + const segments = path.getSegments(); + if (segments.includes('node_modules')) { + // Unscoped package + if (segments[segments.length - 3] === 'node_modules') { + return true; + } + + // Scoped module + if ( + segments[segments.length - 4] === 'node_modules' && + segments[segments.length - 3].startsWith('@') + ) { + return true; + } + + return false; + } + + return true; } // Whenever we're performing an operation on a set of files, always do these first as they may influence how the rest are processed const PRIORITY_FILES = new Set(ROME_CONFIG_FILENAMES); type DeclareManifestOpts = { - diagnostics: DiagnosticsProcessor; - dirname: AbsoluteFilePath; - path: AbsoluteFilePath; + diagnostics: DiagnosticsProcessor; + dirname: AbsoluteFilePath; + path: AbsoluteFilePath; }; type CrawlOptions = { - diagnostics: DiagnosticsProcessor; - crawl: boolean; - onFoundDirectory?: (path: AbsoluteFilePath) => void; - tick?: (path: AbsoluteFilePath) => void; + diagnostics: DiagnosticsProcessor; + crawl: boolean; + onFoundDirectory?: (path: AbsoluteFilePath) => void; + tick?: (path: AbsoluteFilePath) => void; }; export type StatsType = 'unknown' | 'directory' | 'file'; export type Stats = { - size: number; - mtime: number; - type: StatsType; + size: number; + mtime: number; + type: StatsType; }; export type WatcherClose = () => void; export type MemoryFSGlobOptions = { - extensions?: Array; - overrideIgnore?: PathPatterns; - getProjectIgnore?: (project: ProjectDefinition) => PathPatterns; - test?: (path: AbsoluteFilePath) => boolean; + extensions?: Array; + overrideIgnore?: PathPatterns; + getProjectIgnore?: (project: ProjectDefinition) => PathPatterns; + test?: (path: AbsoluteFilePath) => boolean; }; async function createWatcher( - memoryFs: MemoryFileSystem, - diagnostics: DiagnosticsProcessor, - projectFolderPath: AbsoluteFilePath, + memoryFs: MemoryFileSystem, + diagnostics: DiagnosticsProcessor, + projectFolderPath: AbsoluteFilePath, ): Promise { - const projectFolder = projectFolderPath.join(); - const {logger} = memoryFs.master; - - // Create activity spinners for all connected reporters - const activity = memoryFs.master.connectedReporters.progress({ - initDelay: 1_000, - title: `Adding project ${projectFolder}`, - }); - - const watchers: AbsoluteFilePathMap = new AbsoluteFilePathMap(); - - try { - function onFoundDirectory(folderPath: AbsoluteFilePath) { - if (watchers.has(folderPath)) { - return; - } - - let recursive = true; - - if (process.platform === 'linux') { - // Node on Linux doesn't support recursive directory watching so we need an fs.watch for every directory... - recursive = false; - } else if (!folderPath.equal(projectFolderPath)) { - // If we're on any other platform then only watch the root project folder - return; - } - - const watcher = watch( - folderPath, - {recursive, persistent: false}, - (eventType, filename) => { - if (filename === null) { - // TODO not sure how we want to handle this? - return; - } - - const path = folderPath.resolve(filename); - - memoryFs.stat(path).then((newStats) => { - const diagnostics = memoryFs.master.createDisconnectedDiagnosticsProcessor([ - { - category: 'memory-fs', - message: 'Processing fs.watch changes', - }, - ]); - - if (newStats.type === 'file') { - memoryFs.handleFileChange( - path, - newStats, - { - diagnostics, - crawl: true, - }, - ); - } else if (newStats.type === 'directory') { - memoryFs.addDirectory( - path, - newStats, - { - crawl: true, - diagnostics, - onFoundDirectory, - }, - ); - } - }).catch((err) => { - if (err.code === 'ENOENT') { - memoryFs.handleDeletion(path); - } else { - throw err; - } - }); - }, - ); - watchers.set(folderPath, watcher); - } - - // No need to call watch() on the projectFolder since it will call us - - // Perform an initial crawl - const stats = await memoryFs.stat(projectFolderPath); - await memoryFs.addDirectory( - projectFolderPath, - stats, - { - crawl: true, - diagnostics, - onFoundDirectory, - }, - ); - logger.info( - `[MemoryFileSystem] Finished initial crawl for ${projectFolder} - added ${humanizeNumber( - memoryFs.countFiles(projectFolderPath), - )} files`, - ); - } finally { - activity.end(); - } - - return () => { - for (const watcher of watchers.values()) { - watcher.close(); - } - }; + const projectFolder = projectFolderPath.join(); + const {logger} = memoryFs.master; + + // Create activity spinners for all connected reporters + const activity = memoryFs.master.connectedReporters.progress({ + initDelay: 1_000, + title: `Adding project ${projectFolder}`, + }); + + const watchers: AbsoluteFilePathMap = new AbsoluteFilePathMap(); + + try { + function onFoundDirectory(folderPath: AbsoluteFilePath) { + if (watchers.has(folderPath)) { + return; + } + + let recursive = true; + + if (process.platform === 'linux') { + // Node on Linux doesn't support recursive directory watching so we need an fs.watch for every directory... + recursive = false; + } else if (!folderPath.equal(projectFolderPath)) { + // If we're on any other platform then only watch the root project folder + return; + } + + const watcher = watch( + folderPath, + {recursive, persistent: false}, + (eventType, filename) => { + if (filename === null) { + // TODO not sure how we want to handle this? + return; + } + + const path = folderPath.resolve(filename); + + memoryFs.stat(path).then((newStats) => { + const diagnostics = memoryFs.master.createDisconnectedDiagnosticsProcessor([ + { + category: 'memory-fs', + message: 'Processing fs.watch changes', + }, + ]); + + if (newStats.type === 'file') { + memoryFs.handleFileChange( + path, + newStats, + { + diagnostics, + crawl: true, + }, + ); + } else if (newStats.type === 'directory') { + memoryFs.addDirectory( + path, + newStats, + { + crawl: true, + diagnostics, + onFoundDirectory, + }, + ); + } + }).catch((err) => { + if (err.code === 'ENOENT') { + memoryFs.handleDeletion(path); + } else { + throw err; + } + }); + }, + ); + watchers.set(folderPath, watcher); + } + + // No need to call watch() on the projectFolder since it will call us + + // Perform an initial crawl + const stats = await memoryFs.stat(projectFolderPath); + await memoryFs.addDirectory( + projectFolderPath, + stats, + { + crawl: true, + diagnostics, + onFoundDirectory, + }, + ); + logger.info( + `[MemoryFileSystem] Finished initial crawl for ${projectFolder} - added ${humanizeNumber( + memoryFs.countFiles(projectFolderPath), + )} files`, + ); + } finally { + activity.end(); + } + + return () => { + for (const watcher of watchers.values()) { + watcher.close(); + } + }; } export default class MemoryFileSystem { - constructor(master: Master) { - this.master = master; - - this.watchPromises = new Map(); - this.directoryListings = new AbsoluteFilePathMap(); - this.directories = new AbsoluteFilePathMap(); - this.files = new AbsoluteFilePathMap(); - this.manifests = new AbsoluteFilePathMap(); - this.watchers = new Map(); - this.manifestCounter = 0; - - this.changedFileEvent = new Event({ - name: 'MemoryFileSystem.changedFile', - onError: master.onFatalErrorBound, - }); - this.deletedFileEvent = new Event({ - name: 'MemoryFileSystem.deletedFile', - onError: master.onFatalErrorBound, - }); - } - - manifestCounter: number; - master: Master; - directoryListings: AbsoluteFilePathMap>; - directories: AbsoluteFilePathMap; - files: AbsoluteFilePathMap; - manifests: AbsoluteFilePathMap; - - watchers: Map< - string, - { - path: AbsoluteFilePath; - close: WatcherClose; - } - >; - - watchPromises: Map< - string, - { - promise: Promise; - path: AbsoluteFilePath; - } - >; - - changedFileEvent: Event< - { - path: AbsoluteFilePath; - oldStats: undefined | Stats; - newStats: Stats; - }, - void - >; - deletedFileEvent: Event; - - init() {} - - unwatch(dirPath: AbsoluteFilePath) { - const dir = dirPath.join(); - const watcher = this.watchers.get(dir); - if (watcher === undefined) { - return; - } - - this.watchers.delete(dir); - watcher.close(); - - // Go through and clear all files and directories from our internal maps - - // NOTE: We deliberately do not call 'deletedFileEvent' as the code that - - // calls us will already be cleaning up - let queue: Array = [dirPath]; - while (queue.length > 0) { - const path = queue.pop(); - if (path === undefined) { - throw new Error('Unknown path'); - } - - this.directories.delete(path); - this.manifests.delete(path); - this.files.delete(path); - - const listing = this.directoryListings.get(path); - if (listing !== undefined) { - this.directoryListings.delete(path); - queue = queue.concat(Array.from(listing.values())); - } - } - } - - unwatchAll() { - for (const {close} of this.watchers.values()) { - close(); - } - } - - readdir(path: AbsoluteFilePath): Iterable { - const listing = this.directoryListings.get(path); - if (listing === undefined) { - return []; - } else { - return listing.values(); - } - } - - isDirectory(path: AbsoluteFilePath): boolean { - return this.directories.has(path); - } - - isFile(path: AbsoluteFilePath): boolean { - return this.files.has(path); - } - - getFiles(): Array { - return Array.from(this.files.values()); - } - - getManifestDefinition( - dirname: AbsoluteFilePath, - ): undefined | ManifestDefinition { - return this.manifests.get(dirname); - } - - getManifest(dirname: AbsoluteFilePath): undefined | Manifest { - const def = this.getManifestDefinition(dirname); - if (def === undefined) { - return undefined; - } else { - return def.manifest; - } - } - - getOwnedManifest(path: AbsoluteFilePath): undefined | ManifestDefinition { - for (const dir of path.getChain()) { - const def = this.master.memoryFs.getManifestDefinition(dir); - if (def !== undefined) { - return def; - } - } - return undefined; - } - - getPartialManifest(def: ManifestDefinition): WorkerPartialManifest { - return { - path: def.path.join(), - type: def.manifest.type, - }; - } - - addFileToDirectoryListing(path: AbsoluteFilePath): void { - const dirname = path.getParent(); - let listing = this.directoryListings.get(dirname); - if (listing === undefined) { - listing = new AbsoluteFilePathMap(); - this.directoryListings.set(dirname, listing); - } - listing.set(path, path); - } - - handleDeletion(path: AbsoluteFilePath): void { - // If a folder then evict all children - const folderInfo = this.directories.get(path); - if (folderInfo !== undefined) { - this.directories.delete(path); - - const listing = this.directoryListings.get(path); - if (listing !== undefined) { - this.directoryListings.delete(path); - for (const path of listing.values()) { - this.handleDeletion(path); - } - } - } - - // Remove from 'all possible caches - this.files.delete(path); - - // If this is a manifest filename then clear it from 'any possible package and our internal module map - const basename = path.getBasename(); - if (basename === 'package.json') { - this.handleDeletedManifest(path); - } - - // Remove from 'parent directory listing - const dirname = path.getParent(); - const parentListing = this.directoryListings.get(dirname); - if (parentListing !== undefined) { - parentListing.delete(path); - } - - this.deletedFileEvent.send(path); - } - - handleDeletedManifest(path: AbsoluteFilePath): void { - const folder = path.getParent(); - const def = this.manifests.get(folder); - if (def !== undefined) { - this.manifests.delete(folder); - } - } - - async handleFileChange( - path: AbsoluteFilePath, - stats: Stats, - opts: CrawlOptions, - ): Promise { - const oldStats: undefined | Stats = this.getFileStats(path); - const changed = await this.addFile(path, stats, opts); - if (changed) { - const newStats: Stats = this.getFileStatsAssert(path); - this.changedFileEvent.send({path, oldStats, newStats}); - } - return changed; - } - - async waitIfInitializingWatch( - projectFolderPath: AbsoluteFilePath, - ): Promise { - // Defer if we're initializing a parent folder - for (const {promise, path} of this.watchPromises.values()) { - if (projectFolderPath.isRelativeTo(path)) { - await promise; - return; - } - } - - // Wait if we're initializing descendents - for (const {path, promise} of this.watchPromises.values()) { - if (path.isRelativeTo(projectFolderPath)) { - await promise; - } - } - } - - async watch( - projectFolderPath: AbsoluteFilePath, - projectConfig: ProjectConfig, - ): Promise { - const {logger} = this.master; - const projectFolder = projectFolderPath.join(); - const folderLink = markup``; - - // Defer if we're already currently initializing this project - const cached = this.watchPromises.get(projectFolder); - if (cached !== undefined) { - await cached; - return undefined; - } - - // Check if we're already watching this folder - if (this.watchers.has(projectFolder)) { - return undefined; - } - - // Check if we're already watching a parent directory - for (const {path} of this.watchers.values()) { - if (projectFolderPath.isRelativeTo(path)) { - logger.info( - `[MemoryFileSystem] Skipped crawl for ${folderLink} because we're already watching the parent directory ${path.join()}`, - ); - return undefined; - } - } - - // Wait for other initializations - await this.waitIfInitializingWatch(projectFolderPath); - - // New watch target - logger.info(`[MemoryFileSystem] Adding new project folder ${folderLink}`); - - // Remove watchers that are descedents of this folder as this watcher will handle them - for (const [loc, {close, path}] of this.watchers) { - if (path.isRelativeTo(projectFolderPath)) { - this.watchers.delete(loc); - close(); - } - } - - const diagnostics = this.master.createDiagnosticsProcessor({ - origins: [ - { - category: 'memory-fs', - message: 'Crawling project folder', - }, - ], - }); - - logger.info(`[MemoryFileSystem] Watching ${folderLink}`); - const promise = createWatcher(this, diagnostics, projectFolderPath); - this.watchPromises.set( - projectFolder, - { - path: projectFolderPath, - promise, - }, - ); - - const watcherClose = await promise; - this.watchers.set( - projectFolder, - { - path: projectFolderPath, - close: watcherClose, - }, - ); - this.watchPromises.delete(projectFolder); - - diagnostics.maybeThrowDiagnosticsError(); - } - - async stat(path: AbsoluteFilePath): Promise { - const stats = await lstat(path); - - let type: StatsType = 'unknown'; - if (stats.isDirectory()) { - type = 'directory'; - } else if (stats.isFile()) { - type = 'file'; - } - - return { - type, - size: stats.size, - mtime: stats.mtimeMs, - }; - } - - getMtime(path: AbsoluteFilePath) { - const stats = this.getFileStats(path); - if (stats === undefined) { - throw new Error(`File ${path.join()} not in database, cannot get mtime`); - } else { - return stats.mtime; - } - } - - getFileStats(path: AbsoluteFilePath): undefined | Stats { - return this.files.get(path); - } - - getFileStatsAssert(path: AbsoluteFilePath): Stats { - const stats = this.getFileStats(path); - if (stats === undefined) { - throw new Error(`Expected file stats for ${path}`); - } - return stats; - } - - isIgnored(path: AbsoluteFilePath, type: 'directory' | 'file'): boolean { - const project = this.master.projectManager.findProjectExisting(path); - if (project === undefined) { - return false; - } - - // If we're a file and don't have an extension handler so there's no reason for us to care about it - if (type === 'file' && getFileHandler(path, project.config) === undefined) { - return true; - } - - // Ensure we aren't in any of the default denylists - const basename = path.getBasename(); - if (DEFAULT_DENYLIST.includes(basename)) { - return true; - } - - return false; - } - - isInsideProject(path: AbsoluteFilePath): boolean { - return path.getSegments().includes('node_modules') === false; - } - - // This is a wrapper around _declareManifest as it can produce diagnostics - async declareManifest(opts: DeclareManifestOpts): Promise { - const {diagnostics} = await catchDiagnostics(() => { - return this._declareManifest(opts); - }); - - if (diagnostics !== undefined) { - opts.diagnostics.addDiagnostics(diagnostics); - } - } - - async _declareManifest( - { - path, - diagnostics, - }: DeclareManifestOpts, - ): Promise { - // Fetch the manifest - const manifestRaw = await readFileText(path); - const hash = crypto.createHash('sha256').update(manifestRaw).digest('hex'); - - const consumer = consumeJSON({ - path, - input: manifestRaw, - consumeDiagnosticCategory: 'parse/manifest', - }); - - const { - manifest, - diagnostics: normalizedDiagnostics, - } = await normalizeManifest(path, consumer); - - // If manifest is undefined then we failed to validate and have diagnostics - if (normalizedDiagnostics.length > 0) { - diagnostics.addDiagnostics(normalizedDiagnostics); - return; - } - - const folder = path.getParent(); - const manifestId = this.manifestCounter++; - const def: ManifestDefinition = { - id: manifestId, - path, - folder, - consumer, - manifest, - hash, - }; - - this.manifests.set(folder, def); - - // If we aren't in node_modules then this is a project package - const isProjectPackage = this.isInsideProject(path); - const {projectManager} = this.master; - const project = projectManager.findProjectExisting(path); - if (project !== undefined) { - projectManager.declareManifest( - project, - isProjectPackage, - def, - diagnostics, - ); - } - - // Tell all workers of our discovery - for (const worker of this.master.workerManager.getWorkers()) { - worker.bridge.updateManifests.call({ - manifests: [{id: def.id, manifest: this.getPartialManifest(def)}], - }); - } - } - - glob( - cwd: AbsoluteFilePath, - opts: MemoryFSGlobOptions = {}, - ): AbsoluteFilePathSet { - const {extensions, getProjectIgnore, test, overrideIgnore = []} = opts; - - const paths: AbsoluteFilePathSet = new AbsoluteFilePathSet(); - - let crawl: Array = [cwd]; - - const ignoresByProject: Map = new Map(); - - while (crawl.length > 0) { - const path = crawl.pop()!; - - const project = this.master.projectManager.assertProjectExisting(path); - - let ignore: PathPatterns = overrideIgnore; - - // Get ignore patterns - if (getProjectIgnore !== undefined) { - const projectIgnore = ignoresByProject.get(project); - if (projectIgnore === undefined) { - ignore = concatGlobIgnore([...ignore, ...getProjectIgnore(project)]); - ignoresByProject.set(project, ignore); - } else { - ignore = projectIgnore; - } - } - - const ignoreMatched = matchPathPatterns(path, ignore, cwd); - - // Don't even recurse into explicit matches - if (ignoreMatched === 'EXPLICIT_MATCH') { - continue; - } - - // Add if a matching file - if (this.files.has(path) && ignoreMatched === 'NO_MATCH') { - if (test !== undefined && !test(path)) { - continue; - } - - // Check extensions - if (extensions !== undefined) { - let matchedExt = false; - for (const ext of extensions) { - matchedExt = path.hasEndExtension(ext); - if (matchedExt) { - break; - } - } - if (!matchedExt) { - continue; - } - } - - paths.add(path); - continue; - } - - // Crawl if we're a folder - - // NOTE: We still continue crawling on implicit matches - const listing = this.directoryListings.get(path); - if (listing !== undefined) { - crawl = crawl.concat(Array.from(listing.values())); - continue; - } - - // TODO maybe throw? not a file or folder, doesn't exist! - } - - return paths; - } - - getAllFilesInFolder(folder: AbsoluteFilePath): Array { - let files: Array = []; - - const listing = this.directoryListings.get(folder); - if (listing !== undefined) { - for (const file of listing.keys()) { - if (this.files.has(file)) { - files.push(file); - } else { - files = files.concat(this.getAllFilesInFolder(file)); - } - } - } - - return files; - } - - countFiles(folder: AbsoluteFilePath): number { - let count: number = 0; - - const listing = this.directoryListings.get(folder); - if (listing !== undefined) { - for (const file of listing.keys()) { - count++; - count += this.countFiles(file); - } - } - - return count; - } - - hasStatsChanged(path: AbsoluteFilePath, newStats: Stats): boolean { - const oldStats = this.directories.get(path) || this.files.get(path); - return oldStats === undefined || newStats.mtime !== oldStats.mtime; - } - - async addDirectory( - folderPath: AbsoluteFilePath, - stats: Stats, - opts: CrawlOptions, - ): Promise { - if (!this.hasStatsChanged(folderPath, stats)) { - return false; - } - - // Check if this folder has been ignored - if (this.isIgnored(folderPath, 'directory')) { - return false; - } - - if (opts.tick !== undefined) { - opts.tick(folderPath); - } - - this.addFileToDirectoryListing(folderPath); - this.directories.set(folderPath, stats); - - if (opts.onFoundDirectory !== undefined) { - opts.onFoundDirectory(folderPath); - } - - if (opts.crawl) { - // Crawl the folder - const files = await readdir(folderPath); - - // Declare the file - const declareItem = async (path: AbsoluteFilePath) => { - const stats = await this.stat(path); - if (stats.type === 'file') { - await this.addFile(path, stats, opts); - } else if (stats.type === 'directory') { - await this.addDirectory(path, stats, opts); - } - }; - - // Give priority to package.json in case we want to derive something from the project config - for (const file of files) { - if (PRIORITY_FILES.has(file.getBasename())) { - files.delete(file); - await declareItem(file); - } - } - - // Add the rest of the items - await Promise.all(Array.from(files, declareItem)); - } - - return true; - } - - exists(path: AbsoluteFilePath): undefined | boolean { - // if we have this in our cache then the file exists - if (this.files.has(path) || this.directories.has(path)) { - return true; - } - - // If we're still performing an initial crawl of any path higher in the tree then we don't know if it exists yet - for (const {path: projectFolder} of this.watchPromises.values()) { - if (path.isRelativeTo(projectFolder)) { - return undefined; - } - } - - // if we're watching the parent folder then we'd have it in our cache if it existed - const parent = path.getParent(); - if (this.directories.has(parent)) { - return false; - } - - return undefined; - } - - async existsHard(path: AbsoluteFilePath): Promise { - const resolvedExistence: undefined | boolean = this.exists(path); - if (resolvedExistence === undefined) { - return exists(path); - } else { - return resolvedExistence; - } - } - - async addFile( - path: AbsoluteFilePath, - stats: Stats, - opts: CrawlOptions, - ): Promise { - if (!this.hasStatsChanged(path, stats)) { - return false; - } - - // Check if this file has been ignored - if (this.isIgnored(path, 'file')) { - return false; - } - - if (opts.tick !== undefined) { - opts.tick(path); - } - - this.files.set(path, stats); - this.addFileToDirectoryListing(path); - - const basename = path.getBasename(); - const dirname = path.getParent(); - - // Warn about potentially incorrect Rome config filenames - const {projectManager} = this.master; - projectManager.checkConfigFile(path, opts.diagnostics); - - // Add project if this is a config - if (ROME_CONFIG_FILENAMES.includes(basename)) { - await projectManager.queueAddProject(dirname, path); - } - - if (isValidManifest(path)) { - await this.declareManifest({ - diagnostics: opts.diagnostics, - dirname, - path, - }); - } - - return true; - } + constructor(master: Master) { + this.master = master; + + this.watchPromises = new Map(); + this.directoryListings = new AbsoluteFilePathMap(); + this.directories = new AbsoluteFilePathMap(); + this.files = new AbsoluteFilePathMap(); + this.manifests = new AbsoluteFilePathMap(); + this.watchers = new Map(); + this.manifestCounter = 0; + + this.changedFileEvent = new Event({ + name: 'MemoryFileSystem.changedFile', + onError: master.onFatalErrorBound, + }); + this.deletedFileEvent = new Event({ + name: 'MemoryFileSystem.deletedFile', + onError: master.onFatalErrorBound, + }); + } + + manifestCounter: number; + master: Master; + directoryListings: AbsoluteFilePathMap>; + directories: AbsoluteFilePathMap; + files: AbsoluteFilePathMap; + manifests: AbsoluteFilePathMap; + + watchers: Map< + string, + { + path: AbsoluteFilePath; + close: WatcherClose; + } + >; + + watchPromises: Map< + string, + { + promise: Promise; + path: AbsoluteFilePath; + } + >; + + changedFileEvent: Event< + { + path: AbsoluteFilePath; + oldStats: undefined | Stats; + newStats: Stats; + }, + void + >; + deletedFileEvent: Event; + + init() {} + + unwatch(dirPath: AbsoluteFilePath) { + const dir = dirPath.join(); + const watcher = this.watchers.get(dir); + if (watcher === undefined) { + return; + } + + this.watchers.delete(dir); + watcher.close(); + + // Go through and clear all files and directories from our internal maps + + // NOTE: We deliberately do not call 'deletedFileEvent' as the code that + + // calls us will already be cleaning up + let queue: Array = [dirPath]; + while (queue.length > 0) { + const path = queue.pop(); + if (path === undefined) { + throw new Error('Unknown path'); + } + + this.directories.delete(path); + this.manifests.delete(path); + this.files.delete(path); + + const listing = this.directoryListings.get(path); + if (listing !== undefined) { + this.directoryListings.delete(path); + queue = queue.concat(Array.from(listing.values())); + } + } + } + + unwatchAll() { + for (const {close} of this.watchers.values()) { + close(); + } + } + + readdir(path: AbsoluteFilePath): Iterable { + const listing = this.directoryListings.get(path); + if (listing === undefined) { + return []; + } else { + return listing.values(); + } + } + + isDirectory(path: AbsoluteFilePath): boolean { + return this.directories.has(path); + } + + isFile(path: AbsoluteFilePath): boolean { + return this.files.has(path); + } + + getFiles(): Array { + return Array.from(this.files.values()); + } + + getManifestDefinition( + dirname: AbsoluteFilePath, + ): undefined | ManifestDefinition { + return this.manifests.get(dirname); + } + + getManifest(dirname: AbsoluteFilePath): undefined | Manifest { + const def = this.getManifestDefinition(dirname); + if (def === undefined) { + return undefined; + } else { + return def.manifest; + } + } + + getOwnedManifest(path: AbsoluteFilePath): undefined | ManifestDefinition { + for (const dir of path.getChain()) { + const def = this.master.memoryFs.getManifestDefinition(dir); + if (def !== undefined) { + return def; + } + } + return undefined; + } + + getPartialManifest(def: ManifestDefinition): WorkerPartialManifest { + return { + path: def.path.join(), + type: def.manifest.type, + }; + } + + addFileToDirectoryListing(path: AbsoluteFilePath): void { + const dirname = path.getParent(); + let listing = this.directoryListings.get(dirname); + if (listing === undefined) { + listing = new AbsoluteFilePathMap(); + this.directoryListings.set(dirname, listing); + } + listing.set(path, path); + } + + handleDeletion(path: AbsoluteFilePath): void { + // If a folder then evict all children + const folderInfo = this.directories.get(path); + if (folderInfo !== undefined) { + this.directories.delete(path); + + const listing = this.directoryListings.get(path); + if (listing !== undefined) { + this.directoryListings.delete(path); + for (const path of listing.values()) { + this.handleDeletion(path); + } + } + } + + // Remove from 'all possible caches + this.files.delete(path); + + // If this is a manifest filename then clear it from 'any possible package and our internal module map + const basename = path.getBasename(); + if (basename === 'package.json') { + this.handleDeletedManifest(path); + } + + // Remove from 'parent directory listing + const dirname = path.getParent(); + const parentListing = this.directoryListings.get(dirname); + if (parentListing !== undefined) { + parentListing.delete(path); + } + + this.deletedFileEvent.send(path); + } + + handleDeletedManifest(path: AbsoluteFilePath): void { + const folder = path.getParent(); + const def = this.manifests.get(folder); + if (def !== undefined) { + this.manifests.delete(folder); + } + } + + async handleFileChange( + path: AbsoluteFilePath, + stats: Stats, + opts: CrawlOptions, + ): Promise { + const oldStats: undefined | Stats = this.getFileStats(path); + const changed = await this.addFile(path, stats, opts); + if (changed) { + const newStats: Stats = this.getFileStatsAssert(path); + this.changedFileEvent.send({path, oldStats, newStats}); + } + return changed; + } + + async waitIfInitializingWatch( + projectFolderPath: AbsoluteFilePath, + ): Promise { + // Defer if we're initializing a parent folder + for (const {promise, path} of this.watchPromises.values()) { + if (projectFolderPath.isRelativeTo(path)) { + await promise; + return; + } + } + + // Wait if we're initializing descendents + for (const {path, promise} of this.watchPromises.values()) { + if (path.isRelativeTo(projectFolderPath)) { + await promise; + } + } + } + + async watch( + projectFolderPath: AbsoluteFilePath, + projectConfig: ProjectConfig, + ): Promise { + const {logger} = this.master; + const projectFolder = projectFolderPath.join(); + const folderLink = markup``; + + // Defer if we're already currently initializing this project + const cached = this.watchPromises.get(projectFolder); + if (cached !== undefined) { + await cached; + return undefined; + } + + // Check if we're already watching this folder + if (this.watchers.has(projectFolder)) { + return undefined; + } + + // Check if we're already watching a parent directory + for (const {path} of this.watchers.values()) { + if (projectFolderPath.isRelativeTo(path)) { + logger.info( + `[MemoryFileSystem] Skipped crawl for ${folderLink} because we're already watching the parent directory ${path.join()}`, + ); + return undefined; + } + } + + // Wait for other initializations + await this.waitIfInitializingWatch(projectFolderPath); + + // New watch target + logger.info(`[MemoryFileSystem] Adding new project folder ${folderLink}`); + + // Remove watchers that are descedents of this folder as this watcher will handle them + for (const [loc, {close, path}] of this.watchers) { + if (path.isRelativeTo(projectFolderPath)) { + this.watchers.delete(loc); + close(); + } + } + + const diagnostics = this.master.createDiagnosticsProcessor({ + origins: [ + { + category: 'memory-fs', + message: 'Crawling project folder', + }, + ], + }); + + logger.info(`[MemoryFileSystem] Watching ${folderLink}`); + const promise = createWatcher(this, diagnostics, projectFolderPath); + this.watchPromises.set( + projectFolder, + { + path: projectFolderPath, + promise, + }, + ); + + const watcherClose = await promise; + this.watchers.set( + projectFolder, + { + path: projectFolderPath, + close: watcherClose, + }, + ); + this.watchPromises.delete(projectFolder); + + diagnostics.maybeThrowDiagnosticsError(); + } + + async stat(path: AbsoluteFilePath): Promise { + const stats = await lstat(path); + + let type: StatsType = 'unknown'; + if (stats.isDirectory()) { + type = 'directory'; + } else if (stats.isFile()) { + type = 'file'; + } + + return { + type, + size: stats.size, + mtime: stats.mtimeMs, + }; + } + + getMtime(path: AbsoluteFilePath) { + const stats = this.getFileStats(path); + if (stats === undefined) { + throw new Error(`File ${path.join()} not in database, cannot get mtime`); + } else { + return stats.mtime; + } + } + + getFileStats(path: AbsoluteFilePath): undefined | Stats { + return this.files.get(path); + } + + getFileStatsAssert(path: AbsoluteFilePath): Stats { + const stats = this.getFileStats(path); + if (stats === undefined) { + throw new Error(`Expected file stats for ${path}`); + } + return stats; + } + + isIgnored(path: AbsoluteFilePath, type: 'directory' | 'file'): boolean { + const project = this.master.projectManager.findProjectExisting(path); + if (project === undefined) { + return false; + } + + // If we're a file and don't have an extension handler so there's no reason for us to care about it + if (type === 'file' && getFileHandler(path, project.config) === undefined) { + return true; + } + + // Ensure we aren't in any of the default denylists + const basename = path.getBasename(); + if (DEFAULT_DENYLIST.includes(basename)) { + return true; + } + + return false; + } + + isInsideProject(path: AbsoluteFilePath): boolean { + return path.getSegments().includes('node_modules') === false; + } + + // This is a wrapper around _declareManifest as it can produce diagnostics + async declareManifest(opts: DeclareManifestOpts): Promise { + const {diagnostics} = await catchDiagnostics(() => { + return this._declareManifest(opts); + }); + + if (diagnostics !== undefined) { + opts.diagnostics.addDiagnostics(diagnostics); + } + } + + async _declareManifest( + { + path, + diagnostics, + }: DeclareManifestOpts, + ): Promise { + // Fetch the manifest + const manifestRaw = await readFileText(path); + const hash = crypto.createHash('sha256').update(manifestRaw).digest('hex'); + + const consumer = consumeJSON({ + path, + input: manifestRaw, + consumeDiagnosticCategory: 'parse/manifest', + }); + + const { + manifest, + diagnostics: normalizedDiagnostics, + } = await normalizeManifest(path, consumer); + + // If manifest is undefined then we failed to validate and have diagnostics + if (normalizedDiagnostics.length > 0) { + diagnostics.addDiagnostics(normalizedDiagnostics); + return; + } + + const folder = path.getParent(); + const manifestId = this.manifestCounter++; + const def: ManifestDefinition = { + id: manifestId, + path, + folder, + consumer, + manifest, + hash, + }; + + this.manifests.set(folder, def); + + // If we aren't in node_modules then this is a project package + const isProjectPackage = this.isInsideProject(path); + const {projectManager} = this.master; + const project = projectManager.findProjectExisting(path); + if (project !== undefined) { + projectManager.declareManifest(project, isProjectPackage, def, diagnostics); + } + + // Tell all workers of our discovery + for (const worker of this.master.workerManager.getWorkers()) { + worker.bridge.updateManifests.call({ + manifests: [{id: def.id, manifest: this.getPartialManifest(def)}], + }); + } + } + + glob( + cwd: AbsoluteFilePath, + opts: MemoryFSGlobOptions = {}, + ): AbsoluteFilePathSet { + const {extensions, getProjectIgnore, test, overrideIgnore = []} = opts; + + const paths: AbsoluteFilePathSet = new AbsoluteFilePathSet(); + + let crawl: Array = [cwd]; + + const ignoresByProject: Map = new Map(); + + while (crawl.length > 0) { + const path = crawl.pop()!; + + const project = this.master.projectManager.assertProjectExisting(path); + + let ignore: PathPatterns = overrideIgnore; + + // Get ignore patterns + if (getProjectIgnore !== undefined) { + const projectIgnore = ignoresByProject.get(project); + if (projectIgnore === undefined) { + ignore = concatGlobIgnore([...ignore, ...getProjectIgnore(project)]); + ignoresByProject.set(project, ignore); + } else { + ignore = projectIgnore; + } + } + + const ignoreMatched = matchPathPatterns(path, ignore, cwd); + + // Don't even recurse into explicit matches + if (ignoreMatched === 'EXPLICIT_MATCH') { + continue; + } + + // Add if a matching file + if (this.files.has(path) && ignoreMatched === 'NO_MATCH') { + if (test !== undefined && !test(path)) { + continue; + } + + // Check extensions + if (extensions !== undefined) { + let matchedExt = false; + for (const ext of extensions) { + matchedExt = path.hasEndExtension(ext); + if (matchedExt) { + break; + } + } + if (!matchedExt) { + continue; + } + } + + paths.add(path); + continue; + } + + // Crawl if we're a folder + + // NOTE: We still continue crawling on implicit matches + const listing = this.directoryListings.get(path); + if (listing !== undefined) { + crawl = crawl.concat(Array.from(listing.values())); + continue; + } + + // TODO maybe throw? not a file or folder, doesn't exist! + } + + return paths; + } + + getAllFilesInFolder(folder: AbsoluteFilePath): Array { + let files: Array = []; + + const listing = this.directoryListings.get(folder); + if (listing !== undefined) { + for (const file of listing.keys()) { + if (this.files.has(file)) { + files.push(file); + } else { + files = files.concat(this.getAllFilesInFolder(file)); + } + } + } + + return files; + } + + countFiles(folder: AbsoluteFilePath): number { + let count: number = 0; + + const listing = this.directoryListings.get(folder); + if (listing !== undefined) { + for (const file of listing.keys()) { + count++; + count += this.countFiles(file); + } + } + + return count; + } + + hasStatsChanged(path: AbsoluteFilePath, newStats: Stats): boolean { + const oldStats = this.directories.get(path) || this.files.get(path); + return oldStats === undefined || newStats.mtime !== oldStats.mtime; + } + + async addDirectory( + folderPath: AbsoluteFilePath, + stats: Stats, + opts: CrawlOptions, + ): Promise { + if (!this.hasStatsChanged(folderPath, stats)) { + return false; + } + + // Check if this folder has been ignored + if (this.isIgnored(folderPath, 'directory')) { + return false; + } + + if (opts.tick !== undefined) { + opts.tick(folderPath); + } + + this.addFileToDirectoryListing(folderPath); + this.directories.set(folderPath, stats); + + if (opts.onFoundDirectory !== undefined) { + opts.onFoundDirectory(folderPath); + } + + if (opts.crawl) { + // Crawl the folder + const files = await readdir(folderPath); + + // Declare the file + const declareItem = async (path: AbsoluteFilePath) => { + const stats = await this.stat(path); + if (stats.type === 'file') { + await this.addFile(path, stats, opts); + } else if (stats.type === 'directory') { + await this.addDirectory(path, stats, opts); + } + }; + + // Give priority to package.json in case we want to derive something from the project config + for (const file of files) { + if (PRIORITY_FILES.has(file.getBasename())) { + files.delete(file); + await declareItem(file); + } + } + + // Add the rest of the items + await Promise.all(Array.from(files, declareItem)); + } + + return true; + } + + exists(path: AbsoluteFilePath): undefined | boolean { + // if we have this in our cache then the file exists + if (this.files.has(path) || this.directories.has(path)) { + return true; + } + + // If we're still performing an initial crawl of any path higher in the tree then we don't know if it exists yet + for (const {path: projectFolder} of this.watchPromises.values()) { + if (path.isRelativeTo(projectFolder)) { + return undefined; + } + } + + // if we're watching the parent folder then we'd have it in our cache if it existed + const parent = path.getParent(); + if (this.directories.has(parent)) { + return false; + } + + return undefined; + } + + async existsHard(path: AbsoluteFilePath): Promise { + const resolvedExistence: undefined | boolean = this.exists(path); + if (resolvedExistence === undefined) { + return exists(path); + } else { + return resolvedExistence; + } + } + + async addFile( + path: AbsoluteFilePath, + stats: Stats, + opts: CrawlOptions, + ): Promise { + if (!this.hasStatsChanged(path, stats)) { + return false; + } + + // Check if this file has been ignored + if (this.isIgnored(path, 'file')) { + return false; + } + + if (opts.tick !== undefined) { + opts.tick(path); + } + + this.files.set(path, stats); + this.addFileToDirectoryListing(path); + + const basename = path.getBasename(); + const dirname = path.getParent(); + + // Warn about potentially incorrect Rome config filenames + const {projectManager} = this.master; + projectManager.checkConfigFile(path, opts.diagnostics); + + // Add project if this is a config + if (ROME_CONFIG_FILENAMES.includes(basename)) { + await projectManager.queueAddProject(dirname, path); + } + + if (isValidManifest(path)) { + await this.declareManifest({ + diagnostics: opts.diagnostics, + dirname, + path, + }); + } + + return true; + } } diff --git a/packages/@romejs/core/master/fs/Resolver.ts b/packages/@romejs/core/master/fs/Resolver.ts index fbf261c13f3..687b2772892 100644 --- a/packages/@romejs/core/master/fs/Resolver.ts +++ b/packages/@romejs/core/master/fs/Resolver.ts @@ -12,12 +12,12 @@ import {DEFAULT_PROJECT_CONFIG, ProjectDefinition} from '@romejs/project'; import {FileReference} from '../../common/types/files'; import resolverSuggest from './resolverSuggest'; import { - AbsoluteFilePath, - RelativeFilePath, - URLFilePath, - UnknownFilePath, - createFilePathFromSegments, - createRelativeFilePath, + AbsoluteFilePath, + RelativeFilePath, + URLFilePath, + UnknownFilePath, + createFilePathFromSegments, + createRelativeFilePath, } from '@romejs/path'; import {DiagnosticAdvice, DiagnosticLocation} from '@romejs/diagnostics'; import {IMPLICIT_JS_EXTENSIONS} from '../../common/file-handlers/javascript'; @@ -28,817 +28,810 @@ import {MOCKS_FOLDER_NAME} from '@romejs/core/common/constants'; import {Consumer} from '@romejs/consume'; function request( - url: string, + url: string, ): Promise< - | ResolverQueryResponseFetchError - | { - type: 'DOWNLOADED'; - content: string; - } + | ResolverQueryResponseFetchError + | { + type: 'DOWNLOADED'; + content: string; + } > { - return new Promise((resolve) => { - const req = https.get( - url, - (res) => { - if (res.statusCode !== 200) { - console.log('non-200 return'); - resolve({ - type: 'FETCH_ERROR', - source: undefined, - advice: [ - { - type: 'log', - category: 'info', - text: ` returned a ${res.statusCode} status code`, - }, - ], - }); - return; - } - - let data = ''; - - res.on( - 'data', - (chunk) => { - data += chunk; - }, - ); - - res.on( - 'end', - () => { - resolve({type: 'DOWNLOADED', content: data}); - }, - ); - }, - ); - - req.on( - 'error', - (err) => { - resolve({ - type: 'FETCH_ERROR', - source: undefined, - advice: [ - { - type: 'log', - category: 'info', - text: ` resulted in the error "${err.message}"`, - }, - ], - }); - }, - ); - }); + return new Promise((resolve) => { + const req = https.get( + url, + (res) => { + if (res.statusCode !== 200) { + console.log('non-200 return'); + resolve({ + type: 'FETCH_ERROR', + source: undefined, + advice: [ + { + type: 'log', + category: 'info', + text: ` returned a ${res.statusCode} status code`, + }, + ], + }); + return; + } + + let data = ''; + + res.on( + 'data', + (chunk) => { + data += chunk; + }, + ); + + res.on( + 'end', + () => { + resolve({type: 'DOWNLOADED', content: data}); + }, + ); + }, + ); + + req.on( + 'error', + (err) => { + resolve({ + type: 'FETCH_ERROR', + source: undefined, + advice: [ + { + type: 'log', + category: 'info', + text: ` resulted in the error "${err.message}"`, + }, + ], + }); + }, + ); + }); } const NODE_MODULES = 'node_modules'; export type ResolverRemoteQuery = Omit & { - origin: URLFilePath | AbsoluteFilePath; - source: UnknownFilePath; - // Allows a resolution to stop at a folder or package boundary - requestedType?: 'package' | 'folder'; - // Treat the source as a path (without being explicitly relative), and then a module/package if it fails to resolve - entry?: boolean; - // Strict disables implicit extensions - strict?: boolean; + origin: URLFilePath | AbsoluteFilePath; + source: UnknownFilePath; + // Allows a resolution to stop at a folder or package boundary + requestedType?: 'package' | 'folder'; + // Treat the source as a path (without being explicitly relative), and then a module/package if it fails to resolve + entry?: boolean; + // Strict disables implicit extensions + strict?: boolean; }; export type ResolverLocalQuery = Omit & { - origin: AbsoluteFilePath; + origin: AbsoluteFilePath; }; export type ResolverQuerySource = - | undefined - | { - source?: string; - location?: DiagnosticLocation; - }; + | undefined + | { + source?: string; + location?: DiagnosticLocation; + }; type ResolverQueryResponseFoundType = - | 'package' - | 'mock' - | 'virtual' - | 'implicitPlatform' - | 'implicitScale' - | 'implicitExtension' - | 'implicitIndex'; + | 'package' + | 'mock' + | 'virtual' + | 'implicitPlatform' + | 'implicitScale' + | 'implicitExtension' + | 'implicitIndex'; export type ResolverQueryResponseFound = { - type: 'FOUND'; - types: Array; - path: AbsoluteFilePath; - ref: FileReference; + type: 'FOUND'; + types: Array; + path: AbsoluteFilePath; + ref: FileReference; }; export type ResolverQueryResponseMissing = { - type: 'MISSING'; - source: undefined | ResolverQuerySource; - advice?: undefined; + type: 'MISSING'; + source: undefined | ResolverQuerySource; + advice?: undefined; }; export type ResolverQueryResponseUnsupported = { - type: 'UNSUPPORTED'; - source: undefined | ResolverQuerySource; - advice: DiagnosticAdvice; + type: 'UNSUPPORTED'; + source: undefined | ResolverQuerySource; + advice: DiagnosticAdvice; }; export type ResolverQueryResponseFetchError = { - type: 'FETCH_ERROR'; - source: undefined | ResolverQuerySource; - advice: DiagnosticAdvice; + type: 'FETCH_ERROR'; + source: undefined | ResolverQuerySource; + advice: DiagnosticAdvice; }; type FilenameVariant = { - path: UnknownFilePath; - types: Array; + path: UnknownFilePath; + types: Array; }; const QUERY_RESPONSE_MISSING: ResolverQueryResponseMissing = { - type: 'MISSING', - source: undefined, + type: 'MISSING', + source: undefined, }; export type ResolverQueryResponseNotFound = - | ResolverQueryResponseMissing - | ResolverQueryResponseFetchError - | ResolverQueryResponseUnsupported; + | ResolverQueryResponseMissing + | ResolverQueryResponseFetchError + | ResolverQueryResponseUnsupported; export type ResolverQueryResponse = - | ResolverQueryResponseFound - | ResolverQueryResponseNotFound; + | ResolverQueryResponseFound + | ResolverQueryResponseNotFound; function shouldReturnQueryResponse(res: ResolverQueryResponse): boolean { - return res.type === 'FOUND' || res.source !== undefined; + return res.type === 'FOUND' || res.source !== undefined; } export function isPathLike(source: UnknownFilePath): boolean { - return source.isAbsolute() || source.isExplicitRelative(); + return source.isAbsolute() || source.isExplicitRelative(); } function appendTypeQueryResponse( - res: ResolverQueryResponse, - types: Array, + res: ResolverQueryResponse, + types: Array, ): ResolverQueryResponse { - if (res.type === 'FOUND') { - return { - ...res, - types: [...res.types, ...types], - }; - } else { - return res; - } + if (res.type === 'FOUND') { + return { + ...res, + types: [...res.types, ...types], + }; + } else { + return res; + } } export type ResolverOptions = { - origin?: AbsoluteFilePath; - mocks?: boolean; - platform?: Platform; - scale?: number; + origin?: AbsoluteFilePath; + mocks?: boolean; + platform?: Platform; + scale?: number; }; type ExportAlias = { - key: Consumer; - value: RelativeFilePath; + key: Consumer; + value: RelativeFilePath; }; function attachExportAliasIfUnresolved( - res: ResolverQueryResponse, - alias: ExportAlias, + res: ResolverQueryResponse, + alias: ExportAlias, ) { - if (res.type === 'FOUND') { - return res; - } - - const location = alias.key.getDiagnosticLocation('value'); - - return { - ...res, - source: location === undefined - ? undefined - : { - location, - source: alias.value.join(), - }, - }; + if (res.type === 'FOUND') { + return res; + } + + const location = alias.key.getDiagnosticLocation('value'); + + return { + ...res, + source: location === undefined + ? undefined + : { + location, + source: alias.value.join(), + }, + }; } function getExportsAlias( - { - manifest, - relative, - platform, - }: { - manifest: Manifest; - relative: UnknownFilePath; - platform?: Platform; - }, + { + manifest, + relative, + platform, + }: { + manifest: Manifest; + relative: UnknownFilePath; + platform?: Platform; + }, ): undefined | ExportAlias { - if (typeof manifest.exports === 'boolean') { - return undefined; - } - - if (platform === undefined) { - return undefined; - } - - if (!relative.isRelative()) { - return undefined; - } - - const aliases = manifest.exports.get(relative.assertRelative()); - if (aliases === undefined) { - return undefined; - } - - const alias = aliases.get(platform); - if (alias !== undefined) { - return { - key: alias.consumer, - value: alias.relative, - }; - } - - const def = aliases.get('default'); - if (def !== undefined) { - return { - key: def.consumer, - value: def.relative, - }; - } - - // TODO check for folder aliases - return undefined; + if (typeof manifest.exports === 'boolean') { + return undefined; + } + + if (platform === undefined) { + return undefined; + } + + if (!relative.isRelative()) { + return undefined; + } + + const aliases = manifest.exports.get(relative.assertRelative()); + if (aliases === undefined) { + return undefined; + } + + const alias = aliases.get(platform); + if (alias !== undefined) { + return { + key: alias.consumer, + value: alias.relative, + }; + } + + const def = aliases.get('default'); + if (def !== undefined) { + return { + key: def.consumer, + value: def.relative, + }; + } + + // TODO check for folder aliases + return undefined; } function getPreferredMainKey( - consumer: Consumer, - manifest: Manifest, - platform?: Platform, + consumer: Consumer, + manifest: Manifest, + platform?: Platform, ): undefined | ExportAlias { - const alias = getExportsAlias({ - manifest, - relative: createRelativeFilePath('.'), - platform, - }); - if (alias !== undefined) { - return alias; - } - - if (manifest.main !== undefined) { - return { - key: consumer.get('main'), - value: createRelativeFilePath(manifest.main), - }; - } - - return undefined; + const alias = getExportsAlias({ + manifest, + relative: createRelativeFilePath('.'), + platform, + }); + if (alias !== undefined) { + return alias; + } + + if (manifest.main !== undefined) { + return { + key: consumer.get('main'), + value: createRelativeFilePath(manifest.main), + }; + } + + return undefined; } export default class Resolver { - constructor(master: Master) { - this.master = master; - } - - master: Master; - - init() {} - - async findProjectFromQuery(query: ResolverRemoteQuery) { - // If we were passed an absolute path then we should find and add the project it belongs to - if (query.source.isAbsolute()) { - await this.master.projectManager.findProject( - query.source.assertAbsolute(), - ); - } else if (query.origin.isAbsolute()) { - const origin = query.origin.assertAbsolute(); - await this.master.projectManager.findProject(origin); - await this.master.projectManager.findProject( - origin.append(query.source.assertRelative()), - ); - } - } - - async resolveEntryAssert( - query: ResolverRemoteQuery, - querySource?: ResolverQuerySource, - ): Promise { - await this.findProjectFromQuery(query); - return this.resolveAssert({...query, entry: true}, querySource); - } - - // I found myself wanting only `ref.path` a lot so this is just a helper method - async resolveEntryAssertPath( - query: ResolverRemoteQuery, - querySource?: ResolverQuerySource, - ): Promise { - const res = await this.resolveEntryAssert(query, querySource); - return res.path; - } - - async resolveEntry(query: ResolverRemoteQuery): Promise { - await this.findProjectFromQuery(query); - return this.resolveRemote({...query, entry: true}); - } - - async resolveAssert( - query: ResolverRemoteQuery, - origQuerySource?: ResolverQuerySource, - ): Promise { - const resolved = await this.resolveRemote(query); - if (resolved.type === 'FOUND') { - return resolved; - } else { - throw resolverSuggest(this, query, resolved, origQuerySource); - } - } - - async resolveRemote( - query: ResolverRemoteQuery, - ): Promise { - const {origin, source} = query; - - if (source.isURL()) { - const sourceURL = source.assertURL(); - const protocol = sourceURL.getProtocol(); - - switch (protocol) { - case 'http': - case 'https': { - let projectConfig = DEFAULT_PROJECT_CONFIG; - - if (origin.isAbsolute()) { - const project = this.master.projectManager.findProjectExisting( - query.origin.assertAbsolute(), - ); - if (project !== undefined) { - projectConfig = project.config; - } - } - - const remotePath = projectConfig.files.vendorPath.append( - source.join().replace(/[\/:]/g, '$').replace(/\$+/g, '$'), - ); - - if (!this.master.memoryFs.exists(remotePath)) { - const result = await request(source.join()); - if (result.type === 'DOWNLOADED') { - await writeFile(remotePath, result.content); - } else { - return result; - } - } - - return { - type: 'FOUND', - types: [], - ref: this.master.projectManager.getURLFileReference( - remotePath, - sourceURL, - ), - path: remotePath, - }; - } - - default: - return { - type: 'UNSUPPORTED', - source: undefined, - advice: [ - { - type: 'log', - category: 'info', - text: `${protocol} is not a supported remote protocol`, - }, - ], - }; - } - } - - if (origin.isURL()) { - if (source.isAbsolute() || source.isExplicitRelative()) { - // Relative to the origin - return this.resolveRemote({ - ...query, - source: origin.resolve(source), - }); - } else { - // TODO add support for import maps - return { - type: 'MISSING', - source: undefined, - }; - } - } - - return this.resolveLocal({ - ...query, - origin: query.origin.assertAbsolute(), - }); - } - - resolveLocal(query: ResolverLocalQuery): ResolverQueryResponse { - // Do some basic checks to determine if this is an absolute or relative path - if (isPathLike(query.source)) { - return this.resolvePath(query); - } - - // Now resolve it as a module - const resolved = this.resolveModule(query); - - // If we didn't resolve to a module, and we were asked to resolve relative, then do that - if (resolved.type === 'MISSING' && query.entry === true) { - return this.resolvePath(query); - } - - return resolved; - } - - *getFilenameVariants( - query: ResolverLocalQuery, - path: UnknownFilePath, - ): Iterable { - const seen: Set = new Set(); - for (const variant of this._getFilenameVariants(query, path, [])) { - const filename = variant.path.join(); - if (seen.has(filename)) { - continue; - } - - seen.add(filename); - yield variant; - } - } - - *_getFilenameVariants( - query: ResolverLocalQuery, - path: UnknownFilePath, - callees: Array, - ): Iterable { - const {platform} = query; - - yield {path, types: callees}; - - // - const {handler} = this.master.projectManager.getHandlerWithProject( - path.isAbsolute() ? path.assertAbsolute() : query.origin, - ); - const usesUnknownExtension = !query.strict && handler === undefined; - - // Check with appended `platform` - if (platform !== undefined && !callees.includes('implicitPlatform')) { - yield* this._getFilenameVariants( - query, - path.addExtension(`.${platform}`), - [...callees, 'implicitPlatform'], - ); - - // Check if this platform has any subplatforms - const platformAliases = PLATFORM_ALIASES[platform]; - if (platformAliases !== undefined) { - for (const platform of platformAliases) { - yield* this._getFilenameVariants( - query, - path.addExtension(`.${platform}`, true), - [...callees, 'implicitPlatform'], - ); - } - } - } - - // Check with appended extensions - if (usesUnknownExtension && !callees.includes('implicitExtension')) { - for (const ext of IMPLICIT_JS_EXTENSIONS) { - yield* this._getFilenameVariants( - query, - path.addExtension(`.${ext}`), - [...callees, 'implicitExtension'], - ); - } - } - - // Check with appended `scale`, other.filename - if ( - handler !== undefined && - handler.canHaveScale === true && - !callees.includes('implicitScale') - ) { - const scale = query.scale === undefined ? 3 : query.scale; - for (let i = scale; i >= 1; i--) { - yield* this._getFilenameVariants( - query, - path.changeBasename( - `${path.getExtensionlessBasename()}@${String(i)}x${path.memoizedExtension}`, - ), - [...callees, 'implicitScale'], - ); - } - } - } - - finishResolverQueryResponse( - path: AbsoluteFilePath, - types?: Array, - ): ResolverQueryResponse { - return { - type: 'FOUND', - types: types === undefined ? [] : types, - ref: this.master.projectManager.getFileReference(path), - path, - }; - } - - getOriginFolder(query: ResolverLocalQuery): AbsoluteFilePath { - const {memoryFs} = this.master; - const {origin} = query; - - if (memoryFs.isFile(origin)) { - return origin.getParent(); - } else { - return origin; - } - } - - resolvePath( - query: ResolverLocalQuery, - checkVariants: boolean = true, - types?: Array, - ): ResolverQueryResponse { - const {memoryFs} = this.master; - - // Resolve the path heiarchy - const originFolder = this.getOriginFolder(query); - const resolvedOrigin = originFolder.resolve(query.source); - - // Check if this is an absolute filename - if (memoryFs.isFile(resolvedOrigin)) { - // If we're querying a package then we should never return a file - if (query.requestedType === 'package') { - return QUERY_RESPONSE_MISSING; - } - - return this.finishResolverQueryResponse(resolvedOrigin, types); - } - - // Check variants - if (checkVariants) { - for (const variant of this.getFilenameVariants(query, resolvedOrigin)) { - if (variant.path.equal(resolvedOrigin)) { - continue; - } - - const resolved = this.resolvePath( - {...query, source: variant.path}, - false, - variant.types, - ); - - if (shouldReturnQueryResponse(resolved)) { - return appendTypeQueryResponse(resolved, variant.types); - } - } - } - - // check if this is a folder - if (memoryFs.isDirectory(resolvedOrigin)) { - if (query.requestedType === 'folder') { - return this.finishResolverQueryResponse(resolvedOrigin, types); - } - - // If this has a package.json then follow the `main` field - const manifestDef = memoryFs.getManifestDefinition(resolvedOrigin); - if (manifestDef !== undefined) { - // If we're resolving a package then don't follow this - if (query.requestedType === 'package') { - return this.finishResolverQueryResponse(resolvedOrigin, types); - } - - const main = getPreferredMainKey( - manifestDef.consumer, - manifestDef.manifest, - query.platform, - ); - if (main !== undefined) { - const resolved = this.resolvePath( - { - ...query, - origin: resolvedOrigin, - source: main.value, - }, - true, - ['package'], - ); - - return attachExportAliasIfUnresolved(resolved, main); - } - } - - if (!query.strict) { - // Check if it has an index.* file - for (const ext of IMPLICIT_JS_EXTENSIONS) { - const indexResolved = this.resolvePath( - { - ...query, - source: resolvedOrigin.append(`index.${ext}`), - }, - true, - ['implicitIndex'], - ); - - if (shouldReturnQueryResponse(indexResolved)) { - return indexResolved; - } - } - } - } - - return QUERY_RESPONSE_MISSING; - } - - resolvePackageFolder( - query: ResolverLocalQuery, - moduleName: string, - ): undefined | ManifestDefinition { - // Find the project - const project = this.master.projectManager.findProjectExisting(query.origin); - if (project === undefined) { - return undefined; - } - - // Find the package - const projects = this.master.projectManager.getHierarchyFromProject(project); - - for (const project of projects) { - const pkg = project.packages.get(moduleName); - if (pkg !== undefined) { - return pkg; - } - } - - return undefined; - } - - resolvePackage( - query: ResolverLocalQuery, - moduleName: string, - moduleNameParts: Array, - ): ResolverQueryResponse { - const manifestDef = this.resolvePackageFolder(query, moduleName); - return this.resolveManifest(query, manifestDef, moduleNameParts); - } - - resolveManifest( - query: ResolverLocalQuery, - manifestDef: undefined | ManifestDefinition, - moduleNameParts: Array, - ): ResolverQueryResponse { - if (manifestDef === undefined) { - return QUERY_RESPONSE_MISSING; - } - - if (moduleNameParts.length > 0) { - // Submodules of this package are private - if (manifestDef.manifest.exports === false) { - return QUERY_RESPONSE_MISSING; - } - - // Check if we're allowed to touch this submodule - if (manifestDef.manifest.exports !== true) { - const alias = getExportsAlias({ - manifest: manifestDef.manifest, - relative: createFilePathFromSegments(moduleNameParts), - platform: query.platform, - }); - - if (alias === undefined) { - // No submodule found - return QUERY_RESPONSE_MISSING; - } - - // Alias found! - const resolved = this.resolvePath( - { - ...query, - source: manifestDef.folder.append(alias.value), - }, - true, - ['package'], - ); - return attachExportAliasIfUnresolved(resolved, alias); - } - } - - // All exports are enabled or we are importing the root - return this.resolvePath( - { - ...query, - source: manifestDef.folder.append(moduleNameParts), - }, - true, - ['package'], - ); - } - - resolveMock( - query: ResolverLocalQuery, - project: ProjectDefinition | undefined, - parentDirectories: Array, - ): ResolverQueryResponse { - if (project === undefined) { - return QUERY_RESPONSE_MISSING; - } - - const moduleName = query.source.assertRelative(); - - for (const dir of parentDirectories) { - const mocksDir = dir.append(MOCKS_FOLDER_NAME); - - // No use resolving against a directory that doesn't exist - if (!this.master.memoryFs.exists(mocksDir)) { - continue; - } - - const resolved = this.resolveLocal({ - ...query, - source: mocksDir.append(moduleName), - }); - - if (shouldReturnQueryResponse(resolved)) { - return appendTypeQueryResponse(resolved, ['mock']); - } - } - - return QUERY_RESPONSE_MISSING; - } - - // Given a reference to a module, extract the module name and any trailing relative paths - splitModuleName(path: UnknownFilePath): [string, Array] { - // fetch the first part of the path as that's the module name - // possible values of `moduleNameFull` could be `react` or `react/lib/whatever` - const [moduleName, ...moduleNameParts] = path.getSegments(); - - // For scoped modules in the form of `@romejs/bar`, make sure we keep the `/bar` on the module name - if (moduleName[0] === '@' && moduleNameParts.length > 0) { - return [`${moduleName}/${moduleNameParts.shift()}`, moduleNameParts]; - } - - return [moduleName, moduleNameParts]; - } - - resolveModule(query: ResolverLocalQuery): ResolverQueryResponse { - const {origin, source} = query; - - // Get project for the origin - const project = this.master.projectManager.findProjectExisting(origin); - - // Get all the parent directories for when we crawl up - const parentDirectories = this.getOriginFolder(query).getChain(); - - // If mocks are enabled for this query then check all parent mocks folder - if (query.mocks === true) { - const mockResolved = this.resolveMock(query, project, parentDirectories); - if (shouldReturnQueryResponse(mockResolved)) { - return mockResolved; - } - } - - // Extract the module name and it's relative file parts - const [moduleName, moduleNameParts] = this.splitModuleName(source); - - // Resolve a virtual module - const virtualResolved = this.master.virtualModules.resolve(moduleName); - if (virtualResolved !== undefined) { - return this.resolvePath( - { - ...query, - source: virtualResolved.append(moduleNameParts), - }, - true, - ['virtual'], - ); - } - - // Check if it matches any of our project packages - const packageResolved = this.resolvePackage( - query, - moduleName, - moduleNameParts, - ); - if (shouldReturnQueryResponse(packageResolved)) { - return packageResolved; - } - - // Check all parent directories for node_modules - for (const dir of parentDirectories) { - const modulePath = dir.append(NODE_MODULES).append(moduleName); - const manifestDef = this.master.memoryFs.getManifestDefinition(modulePath); - if (manifestDef !== undefined) { - return this.resolveManifest(query, manifestDef, moduleNameParts); - } - } - - return QUERY_RESPONSE_MISSING; - } + constructor(master: Master) { + this.master = master; + } + + master: Master; + + init() {} + + async findProjectFromQuery(query: ResolverRemoteQuery) { + // If we were passed an absolute path then we should find and add the project it belongs to + if (query.source.isAbsolute()) { + await this.master.projectManager.findProject(query.source.assertAbsolute()); + } else if (query.origin.isAbsolute()) { + const origin = query.origin.assertAbsolute(); + await this.master.projectManager.findProject(origin); + await this.master.projectManager.findProject( + origin.append(query.source.assertRelative()), + ); + } + } + + async resolveEntryAssert( + query: ResolverRemoteQuery, + querySource?: ResolverQuerySource, + ): Promise { + await this.findProjectFromQuery(query); + return this.resolveAssert({...query, entry: true}, querySource); + } + + // I found myself wanting only `ref.path` a lot so this is just a helper method + async resolveEntryAssertPath( + query: ResolverRemoteQuery, + querySource?: ResolverQuerySource, + ): Promise { + const res = await this.resolveEntryAssert(query, querySource); + return res.path; + } + + async resolveEntry(query: ResolverRemoteQuery): Promise { + await this.findProjectFromQuery(query); + return this.resolveRemote({...query, entry: true}); + } + + async resolveAssert( + query: ResolverRemoteQuery, + origQuerySource?: ResolverQuerySource, + ): Promise { + const resolved = await this.resolveRemote(query); + if (resolved.type === 'FOUND') { + return resolved; + } else { + throw resolverSuggest(this, query, resolved, origQuerySource); + } + } + + async resolveRemote(query: ResolverRemoteQuery): Promise { + const {origin, source} = query; + + if (source.isURL()) { + const sourceURL = source.assertURL(); + const protocol = sourceURL.getProtocol(); + + switch (protocol) { + case 'http': + case 'https': { + let projectConfig = DEFAULT_PROJECT_CONFIG; + + if (origin.isAbsolute()) { + const project = this.master.projectManager.findProjectExisting( + query.origin.assertAbsolute(), + ); + if (project !== undefined) { + projectConfig = project.config; + } + } + + const remotePath = projectConfig.files.vendorPath.append( + source.join().replace(/[\/:]/g, '$').replace(/\$+/g, '$'), + ); + + if (!this.master.memoryFs.exists(remotePath)) { + const result = await request(source.join()); + if (result.type === 'DOWNLOADED') { + await writeFile(remotePath, result.content); + } else { + return result; + } + } + + return { + type: 'FOUND', + types: [], + ref: this.master.projectManager.getURLFileReference(remotePath, sourceURL), + path: remotePath, + }; + } + + default: + return { + type: 'UNSUPPORTED', + source: undefined, + advice: [ + { + type: 'log', + category: 'info', + text: `${protocol} is not a supported remote protocol`, + }, + ], + }; + } + } + + if (origin.isURL()) { + if (source.isAbsolute() || source.isExplicitRelative()) { + // Relative to the origin + return this.resolveRemote({ + ...query, + source: origin.resolve(source), + }); + } else { + // TODO add support for import maps + return { + type: 'MISSING', + source: undefined, + }; + } + } + + return this.resolveLocal({ + ...query, + origin: query.origin.assertAbsolute(), + }); + } + + resolveLocal(query: ResolverLocalQuery): ResolverQueryResponse { + // Do some basic checks to determine if this is an absolute or relative path + if (isPathLike(query.source)) { + return this.resolvePath(query); + } + + // Now resolve it as a module + const resolved = this.resolveModule(query); + + // If we didn't resolve to a module, and we were asked to resolve relative, then do that + if (resolved.type === 'MISSING' && query.entry === true) { + return this.resolvePath(query); + } + + return resolved; + } + + *getFilenameVariants( + query: ResolverLocalQuery, + path: UnknownFilePath, + ): Iterable { + const seen: Set = new Set(); + for (const variant of this._getFilenameVariants(query, path, [])) { + const filename = variant.path.join(); + if (seen.has(filename)) { + continue; + } + + seen.add(filename); + yield variant; + } + } + + *_getFilenameVariants( + query: ResolverLocalQuery, + path: UnknownFilePath, + callees: Array, + ): Iterable { + const {platform} = query; + + yield {path, types: callees}; + + // + const {handler} = this.master.projectManager.getHandlerWithProject( + path.isAbsolute() ? path.assertAbsolute() : query.origin, + ); + const usesUnknownExtension = !query.strict && handler === undefined; + + // Check with appended `platform` + if (platform !== undefined && !callees.includes('implicitPlatform')) { + yield* this._getFilenameVariants( + query, + path.addExtension(`.${platform}`), + [...callees, 'implicitPlatform'], + ); + + // Check if this platform has any subplatforms + const platformAliases = PLATFORM_ALIASES[platform]; + if (platformAliases !== undefined) { + for (const platform of platformAliases) { + yield* this._getFilenameVariants( + query, + path.addExtension(`.${platform}`, true), + [...callees, 'implicitPlatform'], + ); + } + } + } + + // Check with appended extensions + if (usesUnknownExtension && !callees.includes('implicitExtension')) { + for (const ext of IMPLICIT_JS_EXTENSIONS) { + yield* this._getFilenameVariants( + query, + path.addExtension(`.${ext}`), + [...callees, 'implicitExtension'], + ); + } + } + + // Check with appended `scale`, other.filename + if ( + handler !== undefined && + handler.canHaveScale === true && + !callees.includes('implicitScale') + ) { + const scale = query.scale === undefined ? 3 : query.scale; + for (let i = scale; i >= 1; i--) { + yield* this._getFilenameVariants( + query, + path.changeBasename( + `${path.getExtensionlessBasename()}@${String(i)}x${path.memoizedExtension}`, + ), + [...callees, 'implicitScale'], + ); + } + } + } + + finishResolverQueryResponse( + path: AbsoluteFilePath, + types?: Array, + ): ResolverQueryResponse { + return { + type: 'FOUND', + types: types === undefined ? [] : types, + ref: this.master.projectManager.getFileReference(path), + path, + }; + } + + getOriginFolder(query: ResolverLocalQuery): AbsoluteFilePath { + const {memoryFs} = this.master; + const {origin} = query; + + if (memoryFs.isFile(origin)) { + return origin.getParent(); + } else { + return origin; + } + } + + resolvePath( + query: ResolverLocalQuery, + checkVariants: boolean = true, + types?: Array, + ): ResolverQueryResponse { + const {memoryFs} = this.master; + + // Resolve the path heiarchy + const originFolder = this.getOriginFolder(query); + const resolvedOrigin = originFolder.resolve(query.source); + + // Check if this is an absolute filename + if (memoryFs.isFile(resolvedOrigin)) { + // If we're querying a package then we should never return a file + if (query.requestedType === 'package') { + return QUERY_RESPONSE_MISSING; + } + + return this.finishResolverQueryResponse(resolvedOrigin, types); + } + + // Check variants + if (checkVariants) { + for (const variant of this.getFilenameVariants(query, resolvedOrigin)) { + if (variant.path.equal(resolvedOrigin)) { + continue; + } + + const resolved = this.resolvePath( + {...query, source: variant.path}, + false, + variant.types, + ); + + if (shouldReturnQueryResponse(resolved)) { + return appendTypeQueryResponse(resolved, variant.types); + } + } + } + + // check if this is a folder + if (memoryFs.isDirectory(resolvedOrigin)) { + if (query.requestedType === 'folder') { + return this.finishResolverQueryResponse(resolvedOrigin, types); + } + + // If this has a package.json then follow the `main` field + const manifestDef = memoryFs.getManifestDefinition(resolvedOrigin); + if (manifestDef !== undefined) { + // If we're resolving a package then don't follow this + if (query.requestedType === 'package') { + return this.finishResolverQueryResponse(resolvedOrigin, types); + } + + const main = getPreferredMainKey( + manifestDef.consumer, + manifestDef.manifest, + query.platform, + ); + if (main !== undefined) { + const resolved = this.resolvePath( + { + ...query, + origin: resolvedOrigin, + source: main.value, + }, + true, + ['package'], + ); + + return attachExportAliasIfUnresolved(resolved, main); + } + } + + if (!query.strict) { + // Check if it has an index.* file + for (const ext of IMPLICIT_JS_EXTENSIONS) { + const indexResolved = this.resolvePath( + { + ...query, + source: resolvedOrigin.append(`index.${ext}`), + }, + true, + ['implicitIndex'], + ); + + if (shouldReturnQueryResponse(indexResolved)) { + return indexResolved; + } + } + } + } + + return QUERY_RESPONSE_MISSING; + } + + resolvePackageFolder( + query: ResolverLocalQuery, + moduleName: string, + ): undefined | ManifestDefinition { + // Find the project + const project = this.master.projectManager.findProjectExisting(query.origin); + if (project === undefined) { + return undefined; + } + + // Find the package + const projects = this.master.projectManager.getHierarchyFromProject(project); + + for (const project of projects) { + const pkg = project.packages.get(moduleName); + if (pkg !== undefined) { + return pkg; + } + } + + return undefined; + } + + resolvePackage( + query: ResolverLocalQuery, + moduleName: string, + moduleNameParts: Array, + ): ResolverQueryResponse { + const manifestDef = this.resolvePackageFolder(query, moduleName); + return this.resolveManifest(query, manifestDef, moduleNameParts); + } + + resolveManifest( + query: ResolverLocalQuery, + manifestDef: undefined | ManifestDefinition, + moduleNameParts: Array, + ): ResolverQueryResponse { + if (manifestDef === undefined) { + return QUERY_RESPONSE_MISSING; + } + + if (moduleNameParts.length > 0) { + // Submodules of this package are private + if (manifestDef.manifest.exports === false) { + return QUERY_RESPONSE_MISSING; + } + + // Check if we're allowed to touch this submodule + if (manifestDef.manifest.exports !== true) { + const alias = getExportsAlias({ + manifest: manifestDef.manifest, + relative: createFilePathFromSegments(moduleNameParts), + platform: query.platform, + }); + + if (alias === undefined) { + // No submodule found + return QUERY_RESPONSE_MISSING; + } + + // Alias found! + const resolved = this.resolvePath( + { + ...query, + source: manifestDef.folder.append(alias.value), + }, + true, + ['package'], + ); + return attachExportAliasIfUnresolved(resolved, alias); + } + } + + // All exports are enabled or we are importing the root + return this.resolvePath( + { + ...query, + source: manifestDef.folder.append(moduleNameParts), + }, + true, + ['package'], + ); + } + + resolveMock( + query: ResolverLocalQuery, + project: ProjectDefinition | undefined, + parentDirectories: Array, + ): ResolverQueryResponse { + if (project === undefined) { + return QUERY_RESPONSE_MISSING; + } + + const moduleName = query.source.assertRelative(); + + for (const dir of parentDirectories) { + const mocksDir = dir.append(MOCKS_FOLDER_NAME); + + // No use resolving against a directory that doesn't exist + if (!this.master.memoryFs.exists(mocksDir)) { + continue; + } + + const resolved = this.resolveLocal({ + ...query, + source: mocksDir.append(moduleName), + }); + + if (shouldReturnQueryResponse(resolved)) { + return appendTypeQueryResponse(resolved, ['mock']); + } + } + + return QUERY_RESPONSE_MISSING; + } + + // Given a reference to a module, extract the module name and any trailing relative paths + splitModuleName(path: UnknownFilePath): [string, Array] { + // fetch the first part of the path as that's the module name + // possible values of `moduleNameFull` could be `react` or `react/lib/whatever` + const [moduleName, ...moduleNameParts] = path.getSegments(); + + // For scoped modules in the form of `@romejs/bar`, make sure we keep the `/bar` on the module name + if (moduleName[0] === '@' && moduleNameParts.length > 0) { + return [`${moduleName}/${moduleNameParts.shift()}`, moduleNameParts]; + } + + return [moduleName, moduleNameParts]; + } + + resolveModule(query: ResolverLocalQuery): ResolverQueryResponse { + const {origin, source} = query; + + // Get project for the origin + const project = this.master.projectManager.findProjectExisting(origin); + + // Get all the parent directories for when we crawl up + const parentDirectories = this.getOriginFolder(query).getChain(); + + // If mocks are enabled for this query then check all parent mocks folder + if (query.mocks === true) { + const mockResolved = this.resolveMock(query, project, parentDirectories); + if (shouldReturnQueryResponse(mockResolved)) { + return mockResolved; + } + } + + // Extract the module name and it's relative file parts + const [moduleName, moduleNameParts] = this.splitModuleName(source); + + // Resolve a virtual module + const virtualResolved = this.master.virtualModules.resolve(moduleName); + if (virtualResolved !== undefined) { + return this.resolvePath( + { + ...query, + source: virtualResolved.append(moduleNameParts), + }, + true, + ['virtual'], + ); + } + + // Check if it matches any of our project packages + const packageResolved = this.resolvePackage( + query, + moduleName, + moduleNameParts, + ); + if (shouldReturnQueryResponse(packageResolved)) { + return packageResolved; + } + + // Check all parent directories for node_modules + for (const dir of parentDirectories) { + const modulePath = dir.append(NODE_MODULES).append(moduleName); + const manifestDef = this.master.memoryFs.getManifestDefinition(modulePath); + if (manifestDef !== undefined) { + return this.resolveManifest(query, manifestDef, moduleNameParts); + } + } + + return QUERY_RESPONSE_MISSING; + } } diff --git a/packages/@romejs/core/master/fs/VirtualModules.ts b/packages/@romejs/core/master/fs/VirtualModules.ts index 23364511f7e..94e0a3a44cd 100644 --- a/packages/@romejs/core/master/fs/VirtualModules.ts +++ b/packages/@romejs/core/master/fs/VirtualModules.ts @@ -10,53 +10,53 @@ import {modules} from './runtime-modules'; import {AbsoluteFilePath} from '@romejs/path'; import {createDirectory, writeFile} from '@romejs/fs'; import { - DEFAULT_PROJECT_CONFIG, - DEFAULT_PROJECT_CONFIG_META, - ProjectConfig, + DEFAULT_PROJECT_CONFIG, + DEFAULT_PROJECT_CONFIG_META, + ProjectConfig, } from '@romejs/project'; export default class VirtualModules { - constructor(master: Master) { - this.master = master; - this.runtimeModulesPath = master.userConfig.runtimeModulesPath; - } + constructor(master: Master) { + this.master = master; + this.runtimeModulesPath = master.userConfig.runtimeModulesPath; + } - runtimeModulesPath: AbsoluteFilePath; - master: Master; + runtimeModulesPath: AbsoluteFilePath; + master: Master; - async init() { - const {runtimeModulesPath} = this; + async init() { + const {runtimeModulesPath} = this; - // Materalize virtual files to disk - // We could technically keep these in memory and never materialize them but - // this way we can have something to point at on disk for errors etc - await createDirectory(runtimeModulesPath, {recursive: true}); - for (const [name, files] of modules) { - const modulePath = runtimeModulesPath.append(name); - await createDirectory(modulePath, {recursive: true}); - for (const [basename, content] of files) { - await writeFile(modulePath.append(basename), content); - } - } + // Materalize virtual files to disk + // We could technically keep these in memory and never materialize them but + // this way we can have something to point at on disk for errors etc + await createDirectory(runtimeModulesPath, {recursive: true}); + for (const [name, files] of modules) { + const modulePath = runtimeModulesPath.append(name); + await createDirectory(modulePath, {recursive: true}); + for (const [basename, content] of files) { + await writeFile(modulePath.append(basename), content); + } + } - // Initialize as project - const projectConfig: ProjectConfig = { - ...DEFAULT_PROJECT_CONFIG, - name: 'rome-runtime', - }; - await this.master.projectManager.addProjectWithConfig({ - projectFolder: runtimeModulesPath, - meta: DEFAULT_PROJECT_CONFIG_META, - config: projectConfig, - }); - await this.master.memoryFs.watch(runtimeModulesPath, projectConfig); - } + // Initialize as project + const projectConfig: ProjectConfig = { + ...DEFAULT_PROJECT_CONFIG, + name: 'rome-runtime', + }; + await this.master.projectManager.addProjectWithConfig({ + projectFolder: runtimeModulesPath, + meta: DEFAULT_PROJECT_CONFIG_META, + config: projectConfig, + }); + await this.master.memoryFs.watch(runtimeModulesPath, projectConfig); + } - resolve(name: string): undefined | AbsoluteFilePath { - if (modules.has(name)) { - return this.runtimeModulesPath.append(name); - } else { - return undefined; - } - } + resolve(name: string): undefined | AbsoluteFilePath { + if (modules.has(name)) { + return this.runtimeModulesPath.append(name); + } else { + return undefined; + } + } } diff --git a/packages/@romejs/core/master/fs/resolverSuggest.ts b/packages/@romejs/core/master/fs/resolverSuggest.ts index 01f8a803aca..5d21f7329d0 100644 --- a/packages/@romejs/core/master/fs/resolverSuggest.ts +++ b/packages/@romejs/core/master/fs/resolverSuggest.ts @@ -6,17 +6,17 @@ */ import Resolver, { - ResolverLocalQuery, - ResolverQueryResponseNotFound, - ResolverQuerySource, - ResolverRemoteQuery, - isPathLike, + ResolverLocalQuery, + ResolverQueryResponseNotFound, + ResolverQuerySource, + ResolverRemoteQuery, + isPathLike, } from './Resolver'; import { - DiagnosticAdvice, - buildSuggestionAdvice, - createSingleDiagnosticError, - descriptions, + DiagnosticAdvice, + buildSuggestionAdvice, + createSingleDiagnosticError, + descriptions, } from '@romejs/diagnostics'; import {orderBySimilarity} from '@romejs/string-utils'; import {AbsoluteFilePath, createUnknownFilePath} from '@romejs/path'; @@ -24,352 +24,352 @@ import {PLATFORMS} from '../../common/types/platform'; import {markup} from '@romejs/string-markup'; export default function resolverSuggest( - resolver: Resolver, - query: ResolverRemoteQuery, - resolved: ResolverQueryResponseNotFound, - origQuerySource?: ResolverQuerySource, + resolver: Resolver, + query: ResolverRemoteQuery, + resolved: ResolverQueryResponseNotFound, + origQuerySource?: ResolverQuerySource, ): Error { - let errMsg = ''; - if (resolved.type === 'UNSUPPORTED') { - errMsg = `Unsupported path format`; - } else if (resolved.type === 'MISSING') { - errMsg = `Cannot find`; - } else if (resolved.type === 'FETCH_ERROR') { - errMsg = 'Failed to fetch'; - } - - errMsg += ` "${query.source.join()}" from "${query.origin.join()}"`; - - // Use the querySource returned by the resolution which will be the one that actually triggered this error, otherwise use the query source provided to us - const querySource = - resolved.source === undefined ? origQuerySource : resolved.source; - if (querySource === undefined || querySource.location === undefined) { - // TODO do something about the `advice` on some `resolved` that may contain metadata? - throw new Error(errMsg); - } - - const {location} = querySource; - - let advice: DiagnosticAdvice = []; - - if (query.origin.isAbsolute()) { - const localQuery: ResolverLocalQuery = { - ...query, - origin: query.origin.assertAbsolute(), - }; - - // Provide advice in strict-mode if a non-strict version existed - if (query.strict) { - const nonStrictResolved = resolver.resolveLocal({ - ...localQuery, - strict: false, - }); - - if (nonStrictResolved.type === 'FOUND') { - if (nonStrictResolved.types.includes('implicitIndex')) { - advice.push({ - type: 'log', - category: 'info', - text: `This successfully resolves as an implicit index file. Trying adding /index${nonStrictResolved.path.getExtensions()} to the end of the import source`, - }); - } else if (nonStrictResolved.types.includes('implicitExtension')) { - advice.push({ - type: 'log', - category: 'info', - text: `This successfully resolves as an implicit extension. Try adding the extension ${nonStrictResolved.path.getExtensions()}`, - }); - } - } - } - - // We may set this to `true` for stuff like forgetting a platform - let skipSimilaritySuggestions = false; - - // Try other platforms - const validPlatforms: Array = []; - for (const PLATFORM of PLATFORMS) { - if (PLATFORM === query.platform) { - continue; - } - - const resolved = resolver.resolveLocal({ - ...localQuery, - platform: PLATFORM, - }); - - if (resolved.type === 'FOUND') { - validPlatforms.push( - markup`${PLATFORM} at `, - ); - } - } - if (validPlatforms.length > 0) { - if (query.platform === undefined) { - advice.push({ - type: 'log', - category: 'info', - text: 'No platform was specified but we found modules for the following platforms', - }); - } else { - advice.push({ - type: 'log', - category: 'info', - text: markup`No module found for the platform ${query.platform} but we found these others`, - }); - } - - skipSimilaritySuggestions = true; - - advice.push({ - type: 'list', - list: validPlatforms, - }); - } - - // Hint on any indirection - if ( - origQuerySource !== undefined && - origQuerySource.location !== undefined && - resolved.source !== undefined - ) { - advice.push({ - type: 'log', - category: 'info', - text: `Found while resolving ${query.source} from `, - }); - - const origPointer = origQuerySource.location; - - advice.push({ - type: 'frame', - location: origPointer, - }); - } - - // Suggestions based on similarity to paths and packages on disk - if (!skipSimilaritySuggestions) { - const suggestions = getSuggestions(resolver, localQuery); - if (suggestions.size > 0) { - const originFolder = resolver.getOriginFolder(localQuery); - - // Relative paths to absolute - const relativeToAbsolute: Map = new Map(); - - const relativeSuggestions = Array.from( - suggestions, - ([human, absolute]) => { - if (human !== absolute) { - relativeToAbsolute.set(human, absolute); - return human; - } - - let relativePath = originFolder.relative(absolute); - - // If the user didn't use extensions, then neither should we - if (!query.source.hasExtensions()) { - // TODO only do this if it's an implicit extension - relativePath = relativePath.changeBasename( - relativePath.getExtensionlessBasename(), - ); - } - - const relativeStr = relativePath.toExplicitRelative().join(); - relativeToAbsolute.set(relativeStr, absolute); - return relativeStr; - }, - ); - - advice = [ - ...advice, - ...buildSuggestionAdvice( - query.source.join(), - relativeSuggestions, - { - formatItem: (relative) => { - const absolute = relativeToAbsolute.get(relative); - if (absolute === undefined) { - throw new Error('Should be valid'); - } - - return markup`${relative}`; - }, - }, - ), - ]; - } - } - - // Hint if this was an entry resolve and the cwd wasn't a project - if ( - query.entry === true && - resolver.master.projectManager.findProjectExisting(localQuery.origin) === - undefined - ) { - advice.push({ - type: 'log', - category: 'warn', - text: "You aren't in a Rome project", - }); - } - } - - // TODO check if this would have been successful if not for exports access control - const source = - querySource.source === undefined ? query.source.join() : querySource.source; - - if (resolved.advice !== undefined) { - advice = advice.concat(resolved.advice); - } - - throw createSingleDiagnosticError({ - location, - description: { - ...descriptions.RESOLVER.NOT_FOUND(resolved.type, source, location), - advice, - }, - }); + let errMsg = ''; + if (resolved.type === 'UNSUPPORTED') { + errMsg = `Unsupported path format`; + } else if (resolved.type === 'MISSING') { + errMsg = `Cannot find`; + } else if (resolved.type === 'FETCH_ERROR') { + errMsg = 'Failed to fetch'; + } + + errMsg += ` "${query.source.join()}" from "${query.origin.join()}"`; + + // Use the querySource returned by the resolution which will be the one that actually triggered this error, otherwise use the query source provided to us + const querySource = + resolved.source === undefined ? origQuerySource : resolved.source; + if (querySource === undefined || querySource.location === undefined) { + // TODO do something about the `advice` on some `resolved` that may contain metadata? + throw new Error(errMsg); + } + + const {location} = querySource; + + let advice: DiagnosticAdvice = []; + + if (query.origin.isAbsolute()) { + const localQuery: ResolverLocalQuery = { + ...query, + origin: query.origin.assertAbsolute(), + }; + + // Provide advice in strict-mode if a non-strict version existed + if (query.strict) { + const nonStrictResolved = resolver.resolveLocal({ + ...localQuery, + strict: false, + }); + + if (nonStrictResolved.type === 'FOUND') { + if (nonStrictResolved.types.includes('implicitIndex')) { + advice.push({ + type: 'log', + category: 'info', + text: `This successfully resolves as an implicit index file. Trying adding /index${nonStrictResolved.path.getExtensions()} to the end of the import source`, + }); + } else if (nonStrictResolved.types.includes('implicitExtension')) { + advice.push({ + type: 'log', + category: 'info', + text: `This successfully resolves as an implicit extension. Try adding the extension ${nonStrictResolved.path.getExtensions()}`, + }); + } + } + } + + // We may set this to `true` for stuff like forgetting a platform + let skipSimilaritySuggestions = false; + + // Try other platforms + const validPlatforms: Array = []; + for (const PLATFORM of PLATFORMS) { + if (PLATFORM === query.platform) { + continue; + } + + const resolved = resolver.resolveLocal({ + ...localQuery, + platform: PLATFORM, + }); + + if (resolved.type === 'FOUND') { + validPlatforms.push( + markup`${PLATFORM} at `, + ); + } + } + if (validPlatforms.length > 0) { + if (query.platform === undefined) { + advice.push({ + type: 'log', + category: 'info', + text: 'No platform was specified but we found modules for the following platforms', + }); + } else { + advice.push({ + type: 'log', + category: 'info', + text: markup`No module found for the platform ${query.platform} but we found these others`, + }); + } + + skipSimilaritySuggestions = true; + + advice.push({ + type: 'list', + list: validPlatforms, + }); + } + + // Hint on any indirection + if ( + origQuerySource !== undefined && + origQuerySource.location !== undefined && + resolved.source !== undefined + ) { + advice.push({ + type: 'log', + category: 'info', + text: `Found while resolving ${query.source} from `, + }); + + const origPointer = origQuerySource.location; + + advice.push({ + type: 'frame', + location: origPointer, + }); + } + + // Suggestions based on similarity to paths and packages on disk + if (!skipSimilaritySuggestions) { + const suggestions = getSuggestions(resolver, localQuery); + if (suggestions.size > 0) { + const originFolder = resolver.getOriginFolder(localQuery); + + // Relative paths to absolute + const relativeToAbsolute: Map = new Map(); + + const relativeSuggestions = Array.from( + suggestions, + ([human, absolute]) => { + if (human !== absolute) { + relativeToAbsolute.set(human, absolute); + return human; + } + + let relativePath = originFolder.relative(absolute); + + // If the user didn't use extensions, then neither should we + if (!query.source.hasExtensions()) { + // TODO only do this if it's an implicit extension + relativePath = relativePath.changeBasename( + relativePath.getExtensionlessBasename(), + ); + } + + const relativeStr = relativePath.toExplicitRelative().join(); + relativeToAbsolute.set(relativeStr, absolute); + return relativeStr; + }, + ); + + advice = [ + ...advice, + ...buildSuggestionAdvice( + query.source.join(), + relativeSuggestions, + { + formatItem: (relative) => { + const absolute = relativeToAbsolute.get(relative); + if (absolute === undefined) { + throw new Error('Should be valid'); + } + + return markup`${relative}`; + }, + }, + ), + ]; + } + } + + // Hint if this was an entry resolve and the cwd wasn't a project + if ( + query.entry === true && + resolver.master.projectManager.findProjectExisting(localQuery.origin) === + undefined + ) { + advice.push({ + type: 'log', + category: 'warn', + text: "You aren't in a Rome project", + }); + } + } + + // TODO check if this would have been successful if not for exports access control + const source = + querySource.source === undefined ? query.source.join() : querySource.source; + + if (resolved.advice !== undefined) { + advice = advice.concat(resolved.advice); + } + + throw createSingleDiagnosticError({ + location, + description: { + ...descriptions.RESOLVER.NOT_FOUND(resolved.type, source, location), + advice, + }, + }); } type Suggestions = Map; function getPathSuggestions( - resolver: Resolver, - query: ResolverLocalQuery, + resolver: Resolver, + query: ResolverLocalQuery, ): Suggestions { - const {source} = query; - const originFolder = resolver.getOriginFolder(query); - const suggestions: Suggestions = new Map(); + const {source} = query; + const originFolder = resolver.getOriginFolder(query); + const suggestions: Suggestions = new Map(); - // Try normal resolved - tryPathSuggestions(resolver, suggestions, originFolder.resolve(source)); + // Try normal resolved + tryPathSuggestions(resolver, suggestions, originFolder.resolve(source)); - // Remove . and .. entries from beginning - const sourceParts = [...source.getSegments()]; - while (sourceParts[0] === '.' || sourceParts[0] === '..') { - sourceParts.shift(); - } + // Remove . and .. entries from beginning + const sourceParts = [...source.getSegments()]; + while (sourceParts[0] === '.' || sourceParts[0] === '..') { + sourceParts.shift(); + } - // Try parent directories of the origin + // Try parent directories of the origin - for (const path of originFolder.getChain()) { - tryPathSuggestions(resolver, suggestions, path.append(sourceParts)); - } + for (const path of originFolder.getChain()) { + tryPathSuggestions(resolver, suggestions, path.append(sourceParts)); + } - return suggestions; + return suggestions; } const MIN_SIMILARITY = 0.8; function tryPathSuggestions( - resolver: Resolver, - suggestions: Suggestions, - path: AbsoluteFilePath, + resolver: Resolver, + suggestions: Suggestions, + path: AbsoluteFilePath, ) { - const {memoryFs} = resolver.master; - - const segments = path.getSegments(); - const chain = path.getChain(); - - // Get all segments that are unknown - for (let i = chain.length - 1; i >= 0; i--) { - const path = chain[i]; - - if (memoryFs.exists(path)) { - // If this is an absolute match then we should be a suggestion - if (i === chain.length) { - const filename = path.join(); - suggestions.set(filename, filename); - } - - // Otherwise this segment exists and should have been dealt with previously in the loop - break; - } - - const parentPath = path.getParent(); - - // Our basename isn't valid, but our parent exists - if (!memoryFs.exists(path) && memoryFs.exists(parentPath)) { - const entries = Array.from( - memoryFs.readdir(parentPath), - (path) => path.join(), - ); - if (entries.length === 0) { - continue; - } - - const ratings = orderBySimilarity( - path.getExtensionlessBasename(), - entries, - { - minRating: MIN_SIMILARITY, - formatItem: (target) => { - return createUnknownFilePath(target).getExtensionlessBasename(); - }, - }, - ); - - for (const rating of ratings) { - tryPathSuggestions( - resolver, - suggestions, - createUnknownFilePath(rating.target).append(segments.slice(1)).assertAbsolute(), - ); - } - } - } + const {memoryFs} = resolver.master; + + const segments = path.getSegments(); + const chain = path.getChain(); + + // Get all segments that are unknown + for (let i = chain.length - 1; i >= 0; i--) { + const path = chain[i]; + + if (memoryFs.exists(path)) { + // If this is an absolute match then we should be a suggestion + if (i === chain.length) { + const filename = path.join(); + suggestions.set(filename, filename); + } + + // Otherwise this segment exists and should have been dealt with previously in the loop + break; + } + + const parentPath = path.getParent(); + + // Our basename isn't valid, but our parent exists + if (!memoryFs.exists(path) && memoryFs.exists(parentPath)) { + const entries = Array.from( + memoryFs.readdir(parentPath), + (path) => path.join(), + ); + if (entries.length === 0) { + continue; + } + + const ratings = orderBySimilarity( + path.getExtensionlessBasename(), + entries, + { + minRating: MIN_SIMILARITY, + formatItem: (target) => { + return createUnknownFilePath(target).getExtensionlessBasename(); + }, + }, + ); + + for (const rating of ratings) { + tryPathSuggestions( + resolver, + suggestions, + createUnknownFilePath(rating.target).append(segments.slice(1)).assertAbsolute(), + ); + } + } + } } function getPackageSuggestions( - resolver: Resolver, - query: ResolverLocalQuery, + resolver: Resolver, + query: ResolverLocalQuery, ): Suggestions { - const possibleGlobalPackages: Map = new Map(); - - const mainProject = resolver.master.projectManager.findProjectExisting( - query.origin, - ); - if (mainProject !== undefined) { - const projects = resolver.master.projectManager.getHierarchyFromProject( - mainProject, - ); - - for (const project of projects) { - for (const [name, value] of project.packages) { - possibleGlobalPackages.set(name, value.folder.join()); - } - } - } - - // TODO Add node_modules - const matches: Array<[string, string]> = orderBySimilarity( - query.source.join(), - Array.from(possibleGlobalPackages.keys()), - {minRating: MIN_SIMILARITY}, - ).map((item) => { - const name = item.target; - - const absolute = possibleGlobalPackages.get(name); - if (absolute === undefined) { - throw new Error('Should exist'); - } - - return [name, absolute]; - }); - return new Map(matches); + const possibleGlobalPackages: Map = new Map(); + + const mainProject = resolver.master.projectManager.findProjectExisting( + query.origin, + ); + if (mainProject !== undefined) { + const projects = resolver.master.projectManager.getHierarchyFromProject( + mainProject, + ); + + for (const project of projects) { + for (const [name, value] of project.packages) { + possibleGlobalPackages.set(name, value.folder.join()); + } + } + } + + // TODO Add node_modules + const matches: Array<[string, string]> = orderBySimilarity( + query.source.join(), + Array.from(possibleGlobalPackages.keys()), + {minRating: MIN_SIMILARITY}, + ).map((item) => { + const name = item.target; + + const absolute = possibleGlobalPackages.get(name); + if (absolute === undefined) { + throw new Error('Should exist'); + } + + return [name, absolute]; + }); + return new Map(matches); } function getSuggestions( - resolver: Resolver, - query: ResolverLocalQuery, + resolver: Resolver, + query: ResolverLocalQuery, ): Suggestions { - if (query.entry === true) { - return new Map([ - ...getPathSuggestions(resolver, query), - ...getPackageSuggestions(resolver, query), - ]); - } else if (isPathLike(query.source)) { - return getPathSuggestions(resolver, query); - } else { - return getPackageSuggestions(resolver, query); - } + if (query.entry === true) { + return new Map([ + ...getPathSuggestions(resolver, query), + ...getPackageSuggestions(resolver, query), + ]); + } else if (isPathLike(query.source)) { + return getPathSuggestions(resolver, query); + } else { + return getPackageSuggestions(resolver, query); + } } diff --git a/packages/@romejs/core/master/fs/runtime-modules.ts b/packages/@romejs/core/master/fs/runtime-modules.ts index 6e8d27e02de..f8dbc26e0f9 100644 --- a/packages/@romejs/core/master/fs/runtime-modules.ts +++ b/packages/@romejs/core/master/fs/runtime-modules.ts @@ -10,23 +10,23 @@ export const modules: Map> = new Map(); // EVERYTHING BELOW IS AUTOGENERATED. SEE SCRIPTS FOLDER FOR UPDATE SCRIPTS modules.set( - 'rome', - new Map([ - [ - 'index.ts', - "/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nexport {TestHelper, test, testOptions} from './test';\n", - ], - [ - 'package.json', - '{\n "name": "@romejs-runtime/rome",\n "type": "module",\n "private": true,\n "main": "index.ts"\n}\n', - ], - [ - 'test.ts', - "/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport {JSONPropertyValue} from './types';\n\nexport type AsyncFunc = () => void | undefined | Promise;\n\nexport type SyncThrower = () => void;\n\nexport type ExpectedError = undefined | string | RegExp | Function;\n\nexport type TestSnapshotOptions = {\n filename?: string;\n language?: string;\n};\n\n// These diagnostics are subsets of the official diagnostics\n// We can potentially normalize these and ensure backwards compatibility with the official diagnostics\n\nexport type TestDiagnosticLogCategory = 'none' | 'info' | 'warn' | 'error';\n\nexport type TestDiagnosticAdviceInspect = {\n type: 'inspect';\n data: JSONPropertyValue;\n};\n\nexport type TestDiagnosticAdviceList = {\n type: 'list';\n list: Array;\n};\n\nexport type TestDiagnosticAdviceCode = {\n type: 'code';\n code: string;\n};\n\nexport type TestDiagnosticAdviceLog = {\n type: 'log';\n category: TestDiagnosticLogCategory;\n text: string;\n};\n\nexport type TestDiagnosticAdviceItem =\n | TestDiagnosticAdviceInspect\n | TestDiagnosticAdviceCode\n | TestDiagnosticAdviceLog\n | TestDiagnosticAdviceList;\n\nexport interface TestHelper {\n addToAdvice(item: TestDiagnosticAdviceItem): void;\n clearAdvice(): void;\n onTeardown(callback: AsyncFunc): void;\n clearTimeout(): void;\n extendTimeout(time: number): void;\n setTimeout(time: number): void;\n checkTimeout(): void;\n truthy(value: unknown, message?: string): void;\n falsy(value: unknown, message?: string): void;\n true(value: unknown, message?: string): void;\n false(value: unknown, message?: string): void;\n is(received: unknown, expected: unknown, message?: string): void;\n not(received: unknown, expected: unknown, message?: string): void;\n looksLike(received: unknown, expected: unknown, message?: string): void;\n notLooksLike(received: unknown, expected: unknown, message?: string): void;\n throws(\n thrower: SyncThrower,\n expected?: ExpectedError,\n message?: string,\n ): void;\n throwsAsync(\n thrower: AsyncFunc,\n expected?: ExpectedError,\n message?: string,\n ): Promise;\n notThrows(nonThrower: SyncThrower, message?: string): void;\n notThrowsAsync(nonThrower: AsyncFunc, message?: string): Promise;\n regex(contents: string, regex: RegExp, message?: string): void;\n notRegex(contents: string, regex: RegExp, message?: string): void;\n snapshot(\n expected: unknown,\n message?: string,\n opts?: TestSnapshotOptions,\n ): string;\n inlineSnapshot(received: unknown, expected?: string | boolean | number): void;\n namedSnapshot(\n name: string,\n expected: unknown,\n message?: string,\n opts?: TestSnapshotOptions,\n ): string;\n}\n\nexport type TestName = string | Array;\n\ndeclare const __ROME__TEST_OPTIONS__: GlobalTestOptions;\n\nexport type GlobalTestOptions =\n | undefined\n | {\n dirname?: string;\n register?: (err: Error, opts: TestOptions, callback: TestCallback) => void;\n };\n\ntype NamelessTestOptions = {\n timeout?: number;\n only?: boolean;\n};\n\nexport type TestCallback = (t: TestHelper) => void | undefined | Promise;\n\nexport type TestOptions = NamelessTestOptions & {\n name: TestName;\n};\n\ntype TestArg = TestName | NamelessTestOptions | TestCallback | undefined;\n\nexport const testOptions: NonNullable =\n __ROME__TEST_OPTIONS__ === undefined ? {} : __ROME__TEST_OPTIONS__;\n\nfunction registerTest(\n callsiteError: Error,\n opts: TestOptions,\n callback: TestCallback,\n) {\n const register = testOptions.register;\n\n if (typeof register !== 'function') {\n throw new Error('Test harness does not exist');\n }\n\n register(callsiteError, opts, callback);\n}\n\nfunction isOptionsObject(arg: TestArg): arg is NamelessTestOptions {\n return typeof arg === 'object' && arg != null && !Array.isArray(arg);\n}\n\nfunction splitArgs(\n args: TestRegisterFunctionArgs,\n): {\n options: TestOptions;\n callback: TestCallback;\n} {\n const name = args.shift();\n if (typeof name !== 'string' && !Array.isArray(name)) {\n throw new Error('Expected test name to be a string or an array of strings');\n }\n\n const callback = args.pop();\n if (typeof callback !== 'function') {\n throw new Error('Expected options callback');\n }\n\n const options = args.pop();\n if (options !== undefined && !isOptionsObject(options)) {\n throw new Error('Expected options object');\n }\n\n if (args.length > 0) {\n throw new Error('Expected to have exhausted test register arguments');\n }\n\n return {\n options: {\n ...options,\n name,\n },\n callback,\n };\n}\n\ntype TestRegisterFunctionArgs =\n | [TestName, TestCallback]\n | [TestName, NamelessTestOptions, TestCallback];\n\ntype TestRegisterFunction = (...args: TestRegisterFunctionArgs) => void;\n\nexport const test: TestRegisterFunction & {\n only: TestRegisterFunction;\n} = function(...args: TestRegisterFunctionArgs) {\n const {options, callback} = splitArgs(args);\n registerTest(new Error(), options, callback);\n};\n\ntest.only = function(...args: TestRegisterFunctionArgs) {\n const {options, callback} = splitArgs(args);\n registerTest(\n new Error(),\n {\n ...options,\n only: true,\n },\n callback,\n );\n};\n", - ], - [ - 'types.ts', - '/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// These are copied from packages/@romejs/codec-json/types.ts\nexport type JSONValue =\n | null\n | string\n | number\n | boolean\n | JSONObject\n | JSONArray;\n\nexport type JSONPropertyValue = undefined | void | JSONValue;\n\nexport type JSONObject = {\n [x: string]: JSONPropertyValue;\n};\n\nexport type JSONArray = Array;\n', - ], - ]), + 'rome', + new Map([ + [ + 'index.ts', + "/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nexport {TestHelper, test, testOptions} from './test';\n", + ], + [ + 'package.json', + '{\n "name": "@romejs-runtime/rome",\n "type": "module",\n "private": true,\n "main": "index.ts"\n}\n', + ], + [ + 'test.ts', + "/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport {JSONPropertyValue} from './types';\n\nexport type AsyncFunc = () => void | undefined | Promise;\n\nexport type SyncThrower = () => void;\n\nexport type ExpectedError = undefined | string | RegExp | Function;\n\nexport type TestSnapshotOptions = {\n\tfilename?: string;\n\tlanguage?: string;\n};\n\n// These diagnostics are subsets of the official diagnostics\n// We can potentially normalize these and ensure backwards compatibility with the official diagnostics\n\nexport type TestDiagnosticLogCategory = 'none' | 'info' | 'warn' | 'error';\n\nexport type TestDiagnosticAdviceInspect = {\n\ttype: 'inspect';\n\tdata: JSONPropertyValue;\n};\n\nexport type TestDiagnosticAdviceList = {\n\ttype: 'list';\n\tlist: Array;\n};\n\nexport type TestDiagnosticAdviceCode = {\n\ttype: 'code';\n\tcode: string;\n};\n\nexport type TestDiagnosticAdviceLog = {\n\ttype: 'log';\n\tcategory: TestDiagnosticLogCategory;\n\ttext: string;\n};\n\nexport type TestDiagnosticAdviceItem =\n\t | TestDiagnosticAdviceInspect\n\t| TestDiagnosticAdviceCode\n\t| TestDiagnosticAdviceLog\n\t| TestDiagnosticAdviceList;\n\nexport interface TestHelper {\n\taddToAdvice(item: TestDiagnosticAdviceItem): void;\n\tclearAdvice(): void;\n\tonTeardown(callback: AsyncFunc): void;\n\tclearTimeout(): void;\n\textendTimeout(time: number): void;\n\tsetTimeout(time: number): void;\n\tcheckTimeout(): void;\n\ttruthy(value: unknown, message?: string): void;\n\tfalsy(value: unknown, message?: string): void;\n\ttrue(value: unknown, message?: string): void;\n\tfalse(value: unknown, message?: string): void;\n\tis(received: unknown, expected: unknown, message?: string): void;\n\tnot(received: unknown, expected: unknown, message?: string): void;\n\tlooksLike(received: unknown, expected: unknown, message?: string): void;\n\tnotLooksLike(received: unknown, expected: unknown, message?: string): void;\n\tthrows(thrower: SyncThrower, expected?: ExpectedError, message?: string): void;\n\tthrowsAsync(\n\t\tthrower: AsyncFunc,\n\t\texpected?: ExpectedError,\n\t\tmessage?: string,\n\t): Promise;\n\tnotThrows(nonThrower: SyncThrower, message?: string): void;\n\tnotThrowsAsync(nonThrower: AsyncFunc, message?: string): Promise;\n\tregex(contents: string, regex: RegExp, message?: string): void;\n\tnotRegex(contents: string, regex: RegExp, message?: string): void;\n\tsnapshot(\n\t\texpected: unknown,\n\t\tmessage?: string,\n\t\topts?: TestSnapshotOptions,\n\t): string;\n\tinlineSnapshot(received: unknown, expected?: string | boolean | number): void;\n\tnamedSnapshot(\n\t\tname: string,\n\t\texpected: unknown,\n\t\tmessage?: string,\n\t\topts?: TestSnapshotOptions,\n\t): string;\n}\n\nexport type TestName = string | Array;\n\ndeclare const __ROME__TEST_OPTIONS__: GlobalTestOptions;\n\nexport type GlobalTestOptions =\n\t | undefined\n\t| {\n\t\t\tdirname?: string;\n\t\t\tregister?: (err: Error, opts: TestOptions, callback: TestCallback) => void;\n\t\t};\n\ntype NamelessTestOptions = {\n\ttimeout?: number;\n\tonly?: boolean;\n};\n\nexport type TestCallback = (t: TestHelper) => void | undefined | Promise;\n\nexport type TestOptions = NamelessTestOptions & {\n\tname: TestName;\n};\n\ntype TestArg = TestName | NamelessTestOptions | TestCallback | undefined;\n\nexport const testOptions: NonNullable =\n\t__ROME__TEST_OPTIONS__ === undefined ? {} : __ROME__TEST_OPTIONS__;\n\nfunction registerTest(\n\tcallsiteError: Error,\n\topts: TestOptions,\n\tcallback: TestCallback,\n) {\n\tconst register = testOptions.register;\n\n\tif (typeof register !== 'function') {\n\t\tthrow new Error('Test harness does not exist');\n\t}\n\n\tregister(callsiteError, opts, callback);\n}\n\nfunction isOptionsObject(arg: TestArg): arg is NamelessTestOptions {\n\treturn typeof arg === 'object' && arg != null && !Array.isArray(arg);\n}\n\nfunction splitArgs(\n\targs: TestRegisterFunctionArgs,\n): {\n\toptions: TestOptions;\n\tcallback: TestCallback;\n} {\n\tconst name = args.shift();\n\tif (typeof name !== 'string' && !Array.isArray(name)) {\n\t\tthrow new Error('Expected test name to be a string or an array of strings');\n\t}\n\n\tconst callback = args.pop();\n\tif (typeof callback !== 'function') {\n\t\tthrow new Error('Expected options callback');\n\t}\n\n\tconst options = args.pop();\n\tif (options !== undefined && !isOptionsObject(options)) {\n\t\tthrow new Error('Expected options object');\n\t}\n\n\tif (args.length > 0) {\n\t\tthrow new Error('Expected to have exhausted test register arguments');\n\t}\n\n\treturn {\n\t\toptions: {\n\t\t\t...options,\n\t\t\tname,\n\t\t},\n\t\tcallback,\n\t};\n}\n\ntype TestRegisterFunctionArgs =\n\t | [TestName, TestCallback]\n\t| [TestName, NamelessTestOptions, TestCallback];\n\ntype TestRegisterFunction = (...args: TestRegisterFunctionArgs) => void;\n\nexport const test: TestRegisterFunction & {\n\tonly: TestRegisterFunction;\n} = function(...args: TestRegisterFunctionArgs) {\n\tconst {options, callback} = splitArgs(args);\n\tregisterTest(new Error(), options, callback);\n};\n\ntest.only = function(...args: TestRegisterFunctionArgs) {\n\tconst {options, callback} = splitArgs(args);\n\tregisterTest(\n\t\tnew Error(),\n\t\t{\n\t\t\t...options,\n\t\t\tonly: true,\n\t\t},\n\t\tcallback,\n\t);\n};\n", + ], + [ + 'types.ts', + '/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// These are copied from packages/@romejs/codec-json/types.ts\nexport type JSONValue =\n\t | null\n\t| string\n\t| number\n\t| boolean\n\t| JSONObject\n\t| JSONArray;\n\nexport type JSONPropertyValue = undefined | void | JSONValue;\n\nexport type JSONObject = {\n\t[x: string]: JSONPropertyValue;\n};\n\nexport type JSONArray = Array;\n', + ], + ]), ); diff --git a/packages/@romejs/core/master/linter/Linter.ts b/packages/@romejs/core/master/linter/Linter.ts index 2daf2d6058c..4486bc92931 100644 --- a/packages/@romejs/core/master/linter/Linter.ts +++ b/packages/@romejs/core/master/linter/Linter.ts @@ -8,9 +8,9 @@ import {Master, MasterRequest} from '@romejs/core'; import {LINTABLE_EXTENSIONS} from '@romejs/core/common/file-handlers/index'; import { - DiagnosticSuppressions, - Diagnostics, - DiagnosticsProcessor, + DiagnosticSuppressions, + Diagnostics, + DiagnosticsProcessor, } from '@romejs/diagnostics'; import {FileReference} from '@romejs/core/common/types/files'; import {EventSubscription} from '@romejs/events'; @@ -21,538 +21,534 @@ import DependencyGraph from '../dependencies/DependencyGraph'; import {ReporterProgress, ReporterProgressOptions} from '@romejs/cli-reporter'; import DependencyNode from '../dependencies/DependencyNode'; import { - LintCompilerOptions, - LintCompilerOptionsDecisions, - areAnalyzeDependencyResultsEqual, + LintCompilerOptions, + LintCompilerOptionsDecisions, + areAnalyzeDependencyResultsEqual, } from '@romejs/js-compiler'; import {markup} from '@romejs/string-markup'; import WorkerQueue from '../WorkerQueue'; import {Dict} from '@romejs/typescript-helpers'; type LintWatchChanges = Array<{ - filename: undefined | string; - ref: undefined | FileReference; - diagnostics: Diagnostics; + filename: undefined | string; + ref: undefined | FileReference; + diagnostics: Diagnostics; }>; export type LinterCompilerOptionsPerFile = Dict>; export type LinterOptions = { - save: boolean; - args?: Array; - hasDecisions: boolean; - formatOnly: boolean; - globalDecisions?: LintCompilerOptionsDecisions; - lintCompilerOptionsPerFile?: LinterCompilerOptionsPerFile; + save: boolean; + args?: Array; + hasDecisions: boolean; + formatOnly: boolean; + globalDecisions?: LintCompilerOptionsDecisions; + lintCompilerOptionsPerFile?: LinterCompilerOptionsPerFile; }; type ProgressFactory = (opts: ReporterProgressOptions) => ReporterProgress; type WatchEvents = { - onRunStart: () => void; - createProgress: ProgressFactory; - onChanges: ( - result: WatchResults, - initial: boolean, - runner: LintRunner, - ) => void; + onRunStart: () => void; + createProgress: ProgressFactory; + onChanges: (result: WatchResults, initial: boolean, runner: LintRunner) => void; }; type WatchResults = { - runner: LintRunner; - evictedPaths: AbsoluteFilePathSet; - changes: LintWatchChanges; - savedCount: number; - totalCount: number; + runner: LintRunner; + evictedPaths: AbsoluteFilePathSet; + changes: LintWatchChanges; + savedCount: number; + totalCount: number; }; type LintRunOptions = { - firstRun: boolean; - evictedPaths: AbsoluteFilePathSet; - processor: DiagnosticsProcessor; + firstRun: boolean; + evictedPaths: AbsoluteFilePathSet; + processor: DiagnosticsProcessor; }; function createDiagnosticsPrinter( - request: MasterRequest, - processor: DiagnosticsProcessor, - totalCount: number, - savedCount: number, + request: MasterRequest, + processor: DiagnosticsProcessor, + totalCount: number, + savedCount: number, ): DiagnosticsPrinter { - const printer = request.createDiagnosticsPrinter(processor); - - printer.onFooterPrint((reporter, isError) => { - if (isError) { - let hasPendingFixes = false; - - for (const {fixable} of processor.getDiagnostics()) { - if (fixable) { - hasPendingFixes = true; - } - } - - if (hasPendingFixes) { - reporter.info( - 'Fixes available. To apply recommended fixes and formatting run', - ); - reporter.command('rome lint --save'); - reporter.info('To choose fix suggestions run'); - reporter.command('rome lint --review'); - } - } - - if (savedCount > 0) { - reporter.success( - `${savedCount} ${savedCount} updated`, - ); - } - - if (!isError) { - if (totalCount === 0) { - reporter.warn('No files linted'); - } else { - reporter.info( - `${totalCount} ${totalCount} linted`, - ); - } - } - }); - - return printer; + const printer = request.createDiagnosticsPrinter(processor); + + printer.onFooterPrint((reporter, isError) => { + if (isError) { + let hasPendingFixes = false; + + for (const {fixable} of processor.getDiagnostics()) { + if (fixable) { + hasPendingFixes = true; + } + } + + if (hasPendingFixes) { + reporter.info( + 'Fixes available. To apply recommended fixes and formatting run', + ); + reporter.command('rome lint --save'); + reporter.info('To choose fix suggestions run'); + reporter.command('rome lint --review'); + } + } + + if (savedCount > 0) { + reporter.success( + `${savedCount} ${savedCount} updated`, + ); + } + + if (!isError) { + if (totalCount === 0) { + reporter.warn('No files linted'); + } else { + reporter.info( + `${totalCount} ${totalCount} linted`, + ); + } + } + }); + + return printer; } class LintRunner { - constructor( - linter: Linter, - { - graph, - events, - }: { - events: WatchEvents; - graph: DependencyGraph; - }, - ) { - this.linter = linter; - this.master = linter.request.master; - this.graph = graph; - this.request = linter.request; - this.options = linter.options; - this.events = events; - this.compilerDiagnosticsCache = new AbsoluteFilePathMap(); - this.hadDependencyValidationErrors = new AbsoluteFilePathMap(); - } - - hadDependencyValidationErrors: AbsoluteFilePathMap; - compilerDiagnosticsCache: AbsoluteFilePathMap<{ - diagnostics: Diagnostics; - suppressions: DiagnosticSuppressions; - }>; - linter: Linter; - events: WatchEvents; - master: Master; - request: MasterRequest; - graph: DependencyGraph; - options: LinterOptions; - - async runLint( - { - evictedPaths, - processor, - }: LintRunOptions, - ): Promise<{ - savedCount: number; - }> { - let savedCount = 0; - const {master} = this.request; - - const { - lintCompilerOptionsPerFile = {}, - globalDecisions = [], - hasDecisions, - } = this.options; - const shouldSave = this.linter.shouldSave(); - const shouldApplyFixes = !this.linter.shouldOnlyFormat(); - - const queue: WorkerQueue = new WorkerQueue(master); - - const progress = this.events.createProgress({title: 'Linting'}); - progress.setTotal(evictedPaths.size); - - queue.addCallback(async (path) => { - const filename = path.join(); - const text = markup``; - progress.pushText(text); - - let compilerOptions = lintCompilerOptionsPerFile[filename]; - - // If we have decisions then make sure it's declared on all files - if (hasDecisions) { - if (compilerOptions === undefined) { - compilerOptions = { - hasDecisions: true, - globalDecisions, - decisionsByPosition: {}, - }; - } else { - compilerOptions = { - ...compilerOptions, - hasDecisions: true, - globalDecisions: [ - ...(compilerOptions.globalDecisions || []), - ...globalDecisions, - ], - }; - } - } - - const { - diagnostics, - suppressions, - saved, - } = await this.request.requestWorkerLint( - path, - { - save: shouldSave, - applyFixes: shouldApplyFixes, - compilerOptions, - }, - ); - processor.addSuppressions(suppressions); - processor.addDiagnostics(diagnostics); - this.compilerDiagnosticsCache.set(path, {suppressions, diagnostics}); - if (saved) { - savedCount++; - } - - progress.popText(text); - progress.tick(); - }); - - for (const path of evictedPaths) { - await queue.pushQueue(path); - } - - await queue.spin(); - progress.end(); - - return {savedCount}; - } - - async runGraph( - { - evictedPaths, - processor, - firstRun, - }: LintRunOptions, - ): Promise { - const {graph} = this; - - // Get all the current dependency nodes for the evicted files, and invalidate their nodes - const oldEvictedNodes: AbsoluteFilePathMap = new AbsoluteFilePathMap(); - for (const path of evictedPaths) { - const node = graph.maybeGetNode(path); - if (node !== undefined) { - oldEvictedNodes.set(path, node); - graph.deleteNode(path); - } - } - - // Refresh only the evicted paths - const progress = this.events.createProgress({ - title: firstRun ? 'Analyzing files' : 'Analyzing changed files', - }); - await graph.seed({ - paths: Array.from(evictedPaths), - diagnosticsProcessor: processor, - validate: false, - analyzeProgress: progress, - }); - progress.end(); - - // Maintain a list of all the dependencies we revalidated - const validatedDependencyPaths: AbsoluteFilePathSet = new AbsoluteFilePathSet(); - - // Maintain a list of all the dependents that need to be revalidated - const validatedDependencyPathDependents: AbsoluteFilePathSet = new AbsoluteFilePathSet(); - - // Build a list of dependents to recheck - for (const path of evictedPaths) { - validatedDependencyPaths.add(path); - - const newNode = graph.getNode(path); - - // Get the previous node and see if the exports have actually changed - const oldNode = oldEvictedNodes.get(path); - const sameShape = - oldNode !== undefined && - areAnalyzeDependencyResultsEqual(oldNode.analyze, newNode.analyze); - - for (const depNode of newNode.getDependents()) { - // If the old node has the same shape as the new one, only revalidate the dependent if it had dependency errors - if ( - sameShape && - this.hadDependencyValidationErrors.get(depNode.path) === false - ) { - continue; - } - - validatedDependencyPaths.add(depNode.path); - validatedDependencyPathDependents.add(depNode.path); - } - } - - // Revalidate dependents - if (validatedDependencyPathDependents.size > 0) { - const progress = this.events.createProgress({ - title: 'Analyzing dependents', - }); - - await graph.seed({ - paths: Array.from(validatedDependencyPaths), - diagnosticsProcessor: processor, - validate: false, - analyzeProgress: progress, - }); - - progress.end(); - } - - // Validate connections - for (const path of validatedDependencyPaths) { - const hasValidationErrors = graph.validate(graph.getNode(path), processor); - this.hadDependencyValidationErrors.set(path, hasValidationErrors); - } - - return validatedDependencyPaths; - } - - computeChanges( - {evictedPaths, processor}: LintRunOptions, - validatedDependencyPaths: AbsoluteFilePathSet, - ): LintWatchChanges { - const {master} = this; - const changes: LintWatchChanges = []; - - const updatedPaths: AbsoluteFilePathSet = new AbsoluteFilePathSet([ - ...validatedDependencyPaths, - ]); - - const diagnosticsByFilename = processor.getDiagnosticsByFilename(); - - // In case we pushed on any diagnostics that aren't from the input paths, try to resolve them - const includedFilenamesInDiagnostics = master.projectManager.normalizeFilenamesToFilePaths( - diagnosticsByFilename.keys(), - ); - for (const path of includedFilenamesInDiagnostics.absolutes) { - updatedPaths.add(path); - } - - // If we validated the diagnostics of the dependents, then we need to also push their previous compiler diagnostics - for (const path of validatedDependencyPaths) { - if (!evictedPaths.has(path)) { - const compilerDiagnostics = this.compilerDiagnosticsCache.get(path); - if (compilerDiagnostics !== undefined) { - processor.addSuppressions(compilerDiagnostics.suppressions); - processor.addDiagnostics(compilerDiagnostics.diagnostics); - } - } - } - - // We can't just use getDiagnosticFilenames as we need to produce empty arrays for removed diagnostics - for (const path of updatedPaths) { - const ref = this.request.master.projectManager.getFileReference(path); - const diagnostics: Diagnostics = [ - ...(diagnosticsByFilename.get(ref.uid) || []), - ...(diagnosticsByFilename.get(ref.real.join()) || []), - ]; - - changes.push({ - filename: ref.uid, - ref, - diagnostics, - }); - } - - // We can produce diagnostics that don't actually point at a file. For LSP we will just throw these away, - - // otherwise inside of Rome we can display them. - - // These filenames may be relative or undefined - for (const filename of includedFilenamesInDiagnostics.others) { - changes.push({ - filename, - ref: undefined, - diagnostics: diagnosticsByFilename.get(filename) || [], - }); - } - - return changes; - } - - async run(opts: LintRunOptions): Promise { - this.events.onRunStart(); - const {savedCount} = await this.runLint(opts); - const validatedDependencyPaths = await this.runGraph(opts); - const changes = await this.computeChanges(opts, validatedDependencyPaths); - return { - evictedPaths: opts.evictedPaths, - changes, - savedCount, - totalCount: opts.evictedPaths.size, - runner: this, - }; - } + constructor( + linter: Linter, + { + graph, + events, + }: { + events: WatchEvents; + graph: DependencyGraph; + }, + ) { + this.linter = linter; + this.master = linter.request.master; + this.graph = graph; + this.request = linter.request; + this.options = linter.options; + this.events = events; + this.compilerDiagnosticsCache = new AbsoluteFilePathMap(); + this.hadDependencyValidationErrors = new AbsoluteFilePathMap(); + } + + hadDependencyValidationErrors: AbsoluteFilePathMap; + compilerDiagnosticsCache: AbsoluteFilePathMap<{ + diagnostics: Diagnostics; + suppressions: DiagnosticSuppressions; + }>; + linter: Linter; + events: WatchEvents; + master: Master; + request: MasterRequest; + graph: DependencyGraph; + options: LinterOptions; + + async runLint( + { + evictedPaths, + processor, + }: LintRunOptions, + ): Promise<{ + savedCount: number; + }> { + let savedCount = 0; + const {master} = this.request; + + const { + lintCompilerOptionsPerFile = {}, + globalDecisions = [], + hasDecisions, + } = this.options; + const shouldSave = this.linter.shouldSave(); + const shouldApplyFixes = !this.linter.shouldOnlyFormat(); + + const queue: WorkerQueue = new WorkerQueue(master); + + const progress = this.events.createProgress({title: 'Linting'}); + progress.setTotal(evictedPaths.size); + + queue.addCallback(async (path) => { + const filename = path.join(); + const text = markup``; + progress.pushText(text); + + let compilerOptions = lintCompilerOptionsPerFile[filename]; + + // If we have decisions then make sure it's declared on all files + if (hasDecisions) { + if (compilerOptions === undefined) { + compilerOptions = { + hasDecisions: true, + globalDecisions, + decisionsByPosition: {}, + }; + } else { + compilerOptions = { + ...compilerOptions, + hasDecisions: true, + globalDecisions: [ + ...(compilerOptions.globalDecisions || []), + ...globalDecisions, + ], + }; + } + } + + const { + diagnostics, + suppressions, + saved, + } = await this.request.requestWorkerLint( + path, + { + save: shouldSave, + applyFixes: shouldApplyFixes, + compilerOptions, + }, + ); + processor.addSuppressions(suppressions); + processor.addDiagnostics(diagnostics); + this.compilerDiagnosticsCache.set(path, {suppressions, diagnostics}); + if (saved) { + savedCount++; + } + + progress.popText(text); + progress.tick(); + }); + + for (const path of evictedPaths) { + await queue.pushQueue(path); + } + + await queue.spin(); + progress.end(); + + return {savedCount}; + } + + async runGraph( + { + evictedPaths, + processor, + firstRun, + }: LintRunOptions, + ): Promise { + const {graph} = this; + + // Get all the current dependency nodes for the evicted files, and invalidate their nodes + const oldEvictedNodes: AbsoluteFilePathMap = new AbsoluteFilePathMap(); + for (const path of evictedPaths) { + const node = graph.maybeGetNode(path); + if (node !== undefined) { + oldEvictedNodes.set(path, node); + graph.deleteNode(path); + } + } + + // Refresh only the evicted paths + const progress = this.events.createProgress({ + title: firstRun ? 'Analyzing files' : 'Analyzing changed files', + }); + await graph.seed({ + paths: Array.from(evictedPaths), + diagnosticsProcessor: processor, + validate: false, + analyzeProgress: progress, + }); + progress.end(); + + // Maintain a list of all the dependencies we revalidated + const validatedDependencyPaths: AbsoluteFilePathSet = new AbsoluteFilePathSet(); + + // Maintain a list of all the dependents that need to be revalidated + const validatedDependencyPathDependents: AbsoluteFilePathSet = new AbsoluteFilePathSet(); + + // Build a list of dependents to recheck + for (const path of evictedPaths) { + validatedDependencyPaths.add(path); + + const newNode = graph.getNode(path); + + // Get the previous node and see if the exports have actually changed + const oldNode = oldEvictedNodes.get(path); + const sameShape = + oldNode !== undefined && + areAnalyzeDependencyResultsEqual(oldNode.analyze, newNode.analyze); + + for (const depNode of newNode.getDependents()) { + // If the old node has the same shape as the new one, only revalidate the dependent if it had dependency errors + if ( + sameShape && + this.hadDependencyValidationErrors.get(depNode.path) === false + ) { + continue; + } + + validatedDependencyPaths.add(depNode.path); + validatedDependencyPathDependents.add(depNode.path); + } + } + + // Revalidate dependents + if (validatedDependencyPathDependents.size > 0) { + const progress = this.events.createProgress({ + title: 'Analyzing dependents', + }); + + await graph.seed({ + paths: Array.from(validatedDependencyPaths), + diagnosticsProcessor: processor, + validate: false, + analyzeProgress: progress, + }); + + progress.end(); + } + + // Validate connections + for (const path of validatedDependencyPaths) { + const hasValidationErrors = graph.validate(graph.getNode(path), processor); + this.hadDependencyValidationErrors.set(path, hasValidationErrors); + } + + return validatedDependencyPaths; + } + + computeChanges( + {evictedPaths, processor}: LintRunOptions, + validatedDependencyPaths: AbsoluteFilePathSet, + ): LintWatchChanges { + const {master} = this; + const changes: LintWatchChanges = []; + + const updatedPaths: AbsoluteFilePathSet = new AbsoluteFilePathSet([ + ...validatedDependencyPaths, + ]); + + const diagnosticsByFilename = processor.getDiagnosticsByFilename(); + + // In case we pushed on any diagnostics that aren't from the input paths, try to resolve them + const includedFilenamesInDiagnostics = master.projectManager.normalizeFilenamesToFilePaths( + diagnosticsByFilename.keys(), + ); + for (const path of includedFilenamesInDiagnostics.absolutes) { + updatedPaths.add(path); + } + + // If we validated the diagnostics of the dependents, then we need to also push their previous compiler diagnostics + for (const path of validatedDependencyPaths) { + if (!evictedPaths.has(path)) { + const compilerDiagnostics = this.compilerDiagnosticsCache.get(path); + if (compilerDiagnostics !== undefined) { + processor.addSuppressions(compilerDiagnostics.suppressions); + processor.addDiagnostics(compilerDiagnostics.diagnostics); + } + } + } + + // We can't just use getDiagnosticFilenames as we need to produce empty arrays for removed diagnostics + for (const path of updatedPaths) { + const ref = this.request.master.projectManager.getFileReference(path); + const diagnostics: Diagnostics = [ + ...(diagnosticsByFilename.get(ref.uid) || []), + ...(diagnosticsByFilename.get(ref.real.join()) || []), + ]; + + changes.push({ + filename: ref.uid, + ref, + diagnostics, + }); + } + + // We can produce diagnostics that don't actually point at a file. For LSP we will just throw these away, + + // otherwise inside of Rome we can display them. + + // These filenames may be relative or undefined + for (const filename of includedFilenamesInDiagnostics.others) { + changes.push({ + filename, + ref: undefined, + diagnostics: diagnosticsByFilename.get(filename) || [], + }); + } + + return changes; + } + + async run(opts: LintRunOptions): Promise { + this.events.onRunStart(); + const {savedCount} = await this.runLint(opts); + const validatedDependencyPaths = await this.runGraph(opts); + const changes = await this.computeChanges(opts, validatedDependencyPaths); + return { + evictedPaths: opts.evictedPaths, + changes, + savedCount, + totalCount: opts.evictedPaths.size, + runner: this, + }; + } } export default class Linter { - constructor(req: MasterRequest, opts: LinterOptions) { - this.request = req; - this.options = opts; - } - - request: MasterRequest; - options: LinterOptions; - - shouldOnlyFormat(): boolean { - const {formatOnly} = this.options; - const {review} = this.request.query.requestFlags; - return formatOnly || review; - } - - shouldSave(): boolean { - const {save, hasDecisions} = this.options; - return save || hasDecisions || this.shouldOnlyFormat(); - } - - getFileArgOptions(): MasterRequestGetFilesOptions { - return { - args: this.options.args, - noun: 'lint', - verb: 'linting', - configCategory: 'lint', - extensions: LINTABLE_EXTENSIONS, - disabledDiagnosticCategory: 'lint/disabled', - }; - } - - createDiagnosticsProcessor( - evictedPaths: AbsoluteFilePathSet, - runner: LintRunner, - ): DiagnosticsProcessor { - const processor = this.request.createDiagnosticsProcessor({ - origins: [ - { - category: 'lint', - message: 'Dispatched', - }, - ], - }); - - processor.addAllowedUnusedSuppressionPrefix('bundler'); - - // Only display files that aren't absolute, are in the changed paths, or have had previous compiler diagnostics - - // This hides errors that have been lint ignored but may have been produced by dependency analysis - processor.addFilter({ - test: (diag) => { - const absolute = this.request.master.projectManager.getFilePathFromUidOrAbsolute( - diag.location.filename, - ); - return ( - absolute === undefined || - evictedPaths.has(absolute) || - runner.compilerDiagnosticsCache.has(absolute) - ); - }, - }); - - return processor; - } - - async watch(events: WatchEvents): Promise { - const graph = new DependencyGraph( - this.request, - this.request.getResolverOptionsFromFlags(), - ); - - const runner = new LintRunner( - this, - { - events, - graph, - }, - ); - - let firstRun = true; - - return this.request.watchFilesFromArgs( - this.getFileArgOptions(), - async ({paths: evictedPaths}, initial) => { - const processor = this.createDiagnosticsProcessor(evictedPaths, runner); - - const result = await runner.run({firstRun, evictedPaths, processor}); - events.onChanges(result, initial, runner); - firstRun = false; - }, - ); - } - - async run(watch: boolean) { - const {request} = this; - const {reporter} = request; - - let printer: undefined | DiagnosticsPrinter; - - const diagnosticsByFilename: Map = new Map(); - - const watchEvent = await this.watch({ - onRunStart: () => { - if (watch) { - reporter.clearScreen(); - } - }, - createProgress: (opts) => { - return reporter.progress(opts); - }, - onChanges: ({evictedPaths, changes, totalCount, savedCount, runner}) => { - printer = createDiagnosticsPrinter( - request, - this.createDiagnosticsProcessor(evictedPaths, runner), - totalCount, - savedCount, - ); - - // Update our diagnostics with the changes - for (const {filename, diagnostics} of changes) { - if (diagnostics.length === 0) { - diagnosticsByFilename.delete(filename); - } else { - diagnosticsByFilename.set(filename, diagnostics); - } - } - - // Print all diagnostics - for (const diagnostics of diagnosticsByFilename.values()) { - printer.processor.addDiagnostics(diagnostics); - } - - if (watch) { - reporter.clearScreen(); - printer.print(); - printer.footer(); - } - }, - }); - - if (watch) { - await request.endEvent.wait(); - } else { - watchEvent.unsubscribe(); - - if (printer === undefined) { - throw new Error('Expected a printer'); - } - - throw printer; - } - } + constructor(req: MasterRequest, opts: LinterOptions) { + this.request = req; + this.options = opts; + } + + request: MasterRequest; + options: LinterOptions; + + shouldOnlyFormat(): boolean { + const {formatOnly} = this.options; + const {review} = this.request.query.requestFlags; + return formatOnly || review; + } + + shouldSave(): boolean { + const {save, hasDecisions} = this.options; + return save || hasDecisions || this.shouldOnlyFormat(); + } + + getFileArgOptions(): MasterRequestGetFilesOptions { + return { + args: this.options.args, + noun: 'lint', + verb: 'linting', + configCategory: 'lint', + extensions: LINTABLE_EXTENSIONS, + disabledDiagnosticCategory: 'lint/disabled', + }; + } + + createDiagnosticsProcessor( + evictedPaths: AbsoluteFilePathSet, + runner: LintRunner, + ): DiagnosticsProcessor { + const processor = this.request.createDiagnosticsProcessor({ + origins: [ + { + category: 'lint', + message: 'Dispatched', + }, + ], + }); + + processor.addAllowedUnusedSuppressionPrefix('bundler'); + + // Only display files that aren't absolute, are in the changed paths, or have had previous compiler diagnostics + + // This hides errors that have been lint ignored but may have been produced by dependency analysis + processor.addFilter({ + test: (diag) => { + const absolute = this.request.master.projectManager.getFilePathFromUidOrAbsolute( + diag.location.filename, + ); + return ( + absolute === undefined || + evictedPaths.has(absolute) || + runner.compilerDiagnosticsCache.has(absolute) + ); + }, + }); + + return processor; + } + + async watch(events: WatchEvents): Promise { + const graph = new DependencyGraph( + this.request, + this.request.getResolverOptionsFromFlags(), + ); + + const runner = new LintRunner( + this, + { + events, + graph, + }, + ); + + let firstRun = true; + + return this.request.watchFilesFromArgs( + this.getFileArgOptions(), + async ({paths: evictedPaths}, initial) => { + const processor = this.createDiagnosticsProcessor(evictedPaths, runner); + + const result = await runner.run({firstRun, evictedPaths, processor}); + events.onChanges(result, initial, runner); + firstRun = false; + }, + ); + } + + async run(watch: boolean) { + const {request} = this; + const {reporter} = request; + + let printer: undefined | DiagnosticsPrinter; + + const diagnosticsByFilename: Map = new Map(); + + const watchEvent = await this.watch({ + onRunStart: () => { + if (watch) { + reporter.clearScreen(); + } + }, + createProgress: (opts) => { + return reporter.progress(opts); + }, + onChanges: ({evictedPaths, changes, totalCount, savedCount, runner}) => { + printer = createDiagnosticsPrinter( + request, + this.createDiagnosticsProcessor(evictedPaths, runner), + totalCount, + savedCount, + ); + + // Update our diagnostics with the changes + for (const {filename, diagnostics} of changes) { + if (diagnostics.length === 0) { + diagnosticsByFilename.delete(filename); + } else { + diagnosticsByFilename.set(filename, diagnostics); + } + } + + // Print all diagnostics + for (const diagnostics of diagnosticsByFilename.values()) { + printer.processor.addDiagnostics(diagnostics); + } + + if (watch) { + reporter.clearScreen(); + printer.print(); + printer.footer(); + } + }, + }); + + if (watch) { + await request.endEvent.wait(); + } else { + watchEvent.unsubscribe(); + + if (printer === undefined) { + throw new Error('Expected a printer'); + } + + throw printer; + } + } } diff --git a/packages/@romejs/core/master/lsp/LSPServer.ts b/packages/@romejs/core/master/lsp/LSPServer.ts index 20c0a9a1c72..4714a82cfd0 100644 --- a/packages/@romejs/core/master/lsp/LSPServer.ts +++ b/packages/@romejs/core/master/lsp/LSPServer.ts @@ -7,27 +7,27 @@ import {Consumer, consumeUnknown} from '@romejs/consume'; import { - LSPDiagnostic, - LSPDiagnosticRelatedInformation, - LSPPosition, - LSPRange, - LSPResponseMessage, - LSPTextEdit, + LSPDiagnostic, + LSPDiagnosticRelatedInformation, + LSPPosition, + LSPRange, + LSPResponseMessage, + LSPTextEdit, } from './types'; import Master, {MasterClient} from '../Master'; import { - AbsoluteFilePath, - AbsoluteFilePathMap, - AbsoluteFilePathSet, - createAbsoluteFilePath, + AbsoluteFilePath, + AbsoluteFilePathMap, + AbsoluteFilePathSet, + createAbsoluteFilePath, } from '@romejs/path'; import {DiagnosticLocation, Diagnostics} from '@romejs/diagnostics'; import {Position} from '@romejs/parser-core'; import {Number0, ob1Coerce1To0, ob1Inc, ob1Number0} from '@romejs/ob1'; import {markupToPlainTextString} from '@romejs/string-markup'; import { - MasterQueryResponse, - PartialMasterQueryRequest, + MasterQueryResponse, + PartialMasterQueryRequest, } from '@romejs/core/common/bridges/MasterBridge'; import Linter from '../linter/Linter'; import MasterRequest, {EMPTY_SUCCESS_RESPONSE} from '../MasterRequest'; @@ -35,592 +35,592 @@ import {DEFAULT_CLIENT_REQUEST_FLAGS} from '@romejs/core/common/types/client'; import stringDiff, {Diffs, diffConstants} from '@romejs/string-diff'; import {JSONObject, JSONPropertyValue} from '@romejs/codec-json'; import { - Reporter, - ReporterProgress, - ReporterProgressBase, - ReporterProgressOptions, + Reporter, + ReporterProgress, + ReporterProgressBase, + ReporterProgressOptions, } from '@romejs/cli-reporter'; type Status = 'IDLE' | 'WAITING_FOR_HEADERS_END' | 'WAITING_FOR_RESPONSE_END'; type Headers = { - length: number; - extra: Map; + length: number; + extra: Map; }; const HEADERS_END = '\r\n\r\n'; function parseHeaders(buffer: string): Headers { - const headers: Map = new Map(); - - for (const line of buffer.split('\n')) { - const clean = line.trim(); - const match = clean.match(/^(.*?): (.*?)$/); - if (match == null) { - throw new Error(`Invalid header: ${clean}`); - } - - const [, key, value] = match; - headers.set(key.toLowerCase(), value); - } - - const length = headers.get('content-length'); - if (length === undefined) { - throw new Error('Expected Content-Length'); - } - headers.delete('content-length'); - - return { - length: Number(length), - extra: headers, - }; + const headers: Map = new Map(); + + for (const line of buffer.split('\n')) { + const clean = line.trim(); + const match = clean.match(/^(.*?): (.*?)$/); + if (match == null) { + throw new Error(`Invalid header: ${clean}`); + } + + const [, key, value] = match; + headers.set(key.toLowerCase(), value); + } + + const length = headers.get('content-length'); + if (length === undefined) { + throw new Error('Expected Content-Length'); + } + headers.delete('content-length'); + + return { + length: Number(length), + extra: headers, + }; } function convertPositionToLSP(pos: undefined | Position): LSPPosition { - if (pos === undefined) { - return { - line: ob1Number0, - character: ob1Number0, - }; - } else { - return { - line: ob1Coerce1To0(pos.line), - character: pos.column, - }; - } + if (pos === undefined) { + return { + line: ob1Number0, + character: ob1Number0, + }; + } else { + return { + line: ob1Coerce1To0(pos.line), + character: pos.column, + }; + } } function convertDiagnosticLocationToLSPRange( - location: DiagnosticLocation, + location: DiagnosticLocation, ): LSPRange { - return { - start: convertPositionToLSP(location.start), - end: convertPositionToLSP(location.end), - }; + return { + start: convertPositionToLSP(location.start), + end: convertPositionToLSP(location.end), + }; } function convertDiagnosticsToLSP( - diagnostics: Diagnostics, - master: Master, + diagnostics: Diagnostics, + master: Master, ): Array { - const lspDiagnostics: Array = []; - - for (const {description, location} of diagnostics) { - // Infer relatedInformation from log messages followed by frames - let relatedInformation: Array = []; - const {advice} = description; - for (let i = 0; i < advice.length; i++) { - const item = advice[i]; - const nextItem = advice[i + 1]; - if ( - item.type === 'log' && - nextItem !== undefined && - nextItem.type === 'frame' - ) { - const abs = master.projectManager.getFilePathFromUidOrAbsolute( - nextItem.location.filename, - ); - if (abs !== undefined) { - relatedInformation.push({ - message: markupToPlainTextString(item.text), - location: { - uri: `file://${abs.join()}`, - range: convertDiagnosticLocationToLSPRange(nextItem.location), - }, - }); - } - } - } - - lspDiagnostics.push({ - severity: 1, - range: convertDiagnosticLocationToLSPRange(location), - message: markupToPlainTextString(description.message.value), - code: description.category, - source: 'rome', - relatedInformation, - }); - } - - return lspDiagnostics; + const lspDiagnostics: Array = []; + + for (const {description, location} of diagnostics) { + // Infer relatedInformation from log messages followed by frames + let relatedInformation: Array = []; + const {advice} = description; + for (let i = 0; i < advice.length; i++) { + const item = advice[i]; + const nextItem = advice[i + 1]; + if ( + item.type === 'log' && + nextItem !== undefined && + nextItem.type === 'frame' + ) { + const abs = master.projectManager.getFilePathFromUidOrAbsolute( + nextItem.location.filename, + ); + if (abs !== undefined) { + relatedInformation.push({ + message: markupToPlainTextString(item.text), + location: { + uri: `file://${abs.join()}`, + range: convertDiagnosticLocationToLSPRange(nextItem.location), + }, + }); + } + } + } + + lspDiagnostics.push({ + severity: 1, + range: convertDiagnosticLocationToLSPRange(location), + message: markupToPlainTextString(description.message.value), + code: description.category, + source: 'rome', + relatedInformation, + }); + } + + return lspDiagnostics; } function getPathFromTextDocument(consumer: Consumer): AbsoluteFilePath { - return createAbsoluteFilePath(consumer.get('uri').asString()); + return createAbsoluteFilePath(consumer.get('uri').asString()); } function diffTextEdits(original: string, desired: string): Array { - const edits: Array = []; - - const diffs: Diffs = stringDiff(original, desired); - - let currLine: Number0 = ob1Number0; - let currChar: Number0 = ob1Number0; - - function advance(str: string) { - for (const char of str) { - if (char === '\n') { - currLine = ob1Inc(currLine); - currChar = ob1Number0; - } else { - currChar = ob1Inc(currChar); - } - } - } - - function getPosition(): LSPPosition { - return { - line: currLine, - character: currChar, - }; - } - - for (const [type, text] of diffs) { - switch (type) { - case diffConstants.ADD: { - const pos = getPosition(); - edits.push({ - range: { - start: pos, - end: pos, - }, - newText: text, - }); - break; - } - - case diffConstants.DELETE: { - const start: LSPPosition = getPosition(); - advance(text); - const end: LSPPosition = getPosition(); - edits.push({ - range: { - start, - end, - }, - newText: '', - }); - break; - } - - case diffConstants.EQUAL: { - advance(text); - break; - } - } - } - - return edits; + const edits: Array = []; + + const diffs: Diffs = stringDiff(original, desired); + + let currLine: Number0 = ob1Number0; + let currChar: Number0 = ob1Number0; + + function advance(str: string) { + for (const char of str) { + if (char === '\n') { + currLine = ob1Inc(currLine); + currChar = ob1Number0; + } else { + currChar = ob1Inc(currChar); + } + } + } + + function getPosition(): LSPPosition { + return { + line: currLine, + character: currChar, + }; + } + + for (const [type, text] of diffs) { + switch (type) { + case diffConstants.ADD: { + const pos = getPosition(); + edits.push({ + range: { + start: pos, + end: pos, + }, + newText: text, + }); + break; + } + + case diffConstants.DELETE: { + const start: LSPPosition = getPosition(); + advance(text); + const end: LSPPosition = getPosition(); + edits.push({ + range: { + start, + end, + }, + newText: '', + }); + break; + } + + case diffConstants.EQUAL: { + advance(text); + break; + } + } + } + + return edits; } let progressTokenCounter = 0; class LSPProgress extends ReporterProgressBase { - constructor( - server: LSPServer, - reporter: Reporter, - opts?: ReporterProgressOptions, - ) { - super(reporter, opts); - this.server = server; - this.token = progressTokenCounter++; - this.lastRenderKey = ''; - - server.write({ - type: '$/progress', - params: { - token: this.token, - value: { - kind: 'begin', - cancellable: false, - title: this.title, - percentage: 0, - }, - }, - }); - } - - lastRenderKey: string; - token: number; - server: LSPServer; - - render() { - const total = this.total === undefined ? 0 : this.total; - const percentage = Math.floor(100 / total * this.current); - - // Make sure we don't send pointless duplicate messages - const renderKey = `percent:${percentage},text:${this.text}`; - if (this.lastRenderKey === renderKey) { - return; - } - - this.lastRenderKey = renderKey; - this.server.write({ - type: '$/progress', - params: { - token: this.token, - value: { - kind: 'report', - cancellable: false, - message: this.text, - percentage, - }, - }, - }); - } - - end() { - this.server.write({ - type: '$/progress', - params: { - token: this.token, - value: { - kind: 'end', - }, - }, - }); - } + constructor( + server: LSPServer, + reporter: Reporter, + opts?: ReporterProgressOptions, + ) { + super(reporter, opts); + this.server = server; + this.token = progressTokenCounter++; + this.lastRenderKey = ''; + + server.write({ + type: '$/progress', + params: { + token: this.token, + value: { + kind: 'begin', + cancellable: false, + title: this.title, + percentage: 0, + }, + }, + }); + } + + lastRenderKey: string; + token: number; + server: LSPServer; + + render() { + const total = this.total === undefined ? 0 : this.total; + const percentage = Math.floor(100 / total * this.current); + + // Make sure we don't send pointless duplicate messages + const renderKey = `percent:${percentage},text:${this.text}`; + if (this.lastRenderKey === renderKey) { + return; + } + + this.lastRenderKey = renderKey; + this.server.write({ + type: '$/progress', + params: { + token: this.token, + value: { + kind: 'report', + cancellable: false, + message: this.text, + percentage, + }, + }, + }); + } + + end() { + this.server.write({ + type: '$/progress', + params: { + token: this.token, + value: { + kind: 'end', + }, + }, + }); + } } export default class LSPServer { - constructor(request: MasterRequest) { - this.status = 'IDLE'; - this.buffer = ''; - this.nextHeaders = undefined; - - this.request = request; - this.master = request.master; - this.client = request.client; - - this.lintSessionsPending = new AbsoluteFilePathSet(); - this.lintSessions = new AbsoluteFilePathMap(); - - request.endEvent.subscribe(() => { - this.shutdown(); - }); - } - - request: MasterRequest; - client: MasterClient; - master: Master; - nextHeaders: undefined | Headers; - status: Status; - buffer: string; - lintSessionsPending: AbsoluteFilePathSet; - lintSessions: AbsoluteFilePathMap; - - write(res: JSONObject) { - const json = JSON.stringify(res); - const out = `Content-Length: ${String(json.length)}${HEADERS_END}${json}`; - this.client.bridge.lspFromServerBuffer.send(out); - } - - createFakeMasterRequest( - commandName: string, - args: Array = [], - ): MasterRequest { - return new MasterRequest({ - client: this.client, - master: this.master, - query: { - requestFlags: DEFAULT_CLIENT_REQUEST_FLAGS, - commandFlags: {}, - args, - commandName, - silent: true, - noData: false, - terminateWhenIdle: false, - }, - }); - } - - unwatchProject(path: AbsoluteFilePath) { - // TODO maybe unset all buffers? - const req = this.lintSessions.get(path); - if (req !== undefined) { - req.teardown(EMPTY_SUCCESS_RESPONSE); - this.lintSessions.delete(path); - } - } - - createProgress(opts?: ReporterProgressOptions): ReporterProgress { - return new LSPProgress(this, this.request.reporter, opts); - } - - async watchProject(path: AbsoluteFilePath) { - if (this.lintSessions.has(path) || this.lintSessionsPending.has(path)) { - return; - } - - this.lintSessionsPending.add(path); - - const project = await this.master.projectManager.findProject(path); - - if (project === undefined) { - // Not a Rome project - this.lintSessionsPending.delete(path); - return; - } - - const req = this.createFakeMasterRequest('lsp_project', [path.join()]); - await req.init(); - - const linter = new Linter( - req, - { - save: false, - hasDecisions: false, - formatOnly: false, - }, - ); - - const subscription = await linter.watch({ - onRunStart: () => {}, - createProgress: () => { - return this.createProgress(); - }, - onChanges: ({changes}) => { - for (const {ref, diagnostics} of changes) { - if (ref === undefined) { - // Cannot display diagnostics without a reference - continue; - } - - // We want to filter pendingFixes because we'll autoformat the file on save if necessary and it's just noise - const processor = this.request.createDiagnosticsProcessor(); - processor.addFilter({ - category: 'lint/pendingFixes', - }); - processor.addDiagnostics(diagnostics); - - this.write({ - method: 'textDocument/publishDiagnostics', - params: { - uri: `file://${ref.real.join()}`, - diagnostics: convertDiagnosticsToLSP( - processor.getDiagnostics(), - this.master, - ), - }, - }); - } - }, - }); - - req.endEvent.subscribe(() => { - subscription.unsubscribe(); - }); - - this.lintSessions.set(path, req); - this.lintSessionsPending.delete(path); - } - - shutdown() { - for (const path of this.lintSessions.keys()) { - this.unwatchProject(path); - } - this.lintSessions.clear(); - } - - async sendClientRequest( - req: PartialMasterQueryRequest, - ): Promise { - return this.master.handleRequest( - this.client, - { - silent: true, - ...req, - }, - ); - } - - async handleRequest( - method: string, - params: Consumer, - ): Promise { - switch (method) { - case 'initialize': { - const rootUri = params.get('rootUri'); - if (rootUri.exists()) { - this.watchProject(createAbsoluteFilePath(rootUri.asString())); - } - - const workspaceFolders = params.get('workspaceFolders'); - if (workspaceFolders.exists()) { - for (const elem of workspaceFolders.asArray()) { - this.watchProject(getPathFromTextDocument(elem)); - } - } - - return { - capabilities: { - textDocumentSync: { - openClose: true, - // This sends over the full text on change. We should make this incremental later - change: 1, - }, - documentFormattingProvider: true, - workspaceFolders: { - supported: true, - changeNotifications: true, - }, - }, - serverInfo: { - name: 'rome', - }, - }; - } - - case 'textDocument/formatting': { - const path = getPathFromTextDocument(params.get('textDocument')); - - const project = this.master.projectManager.findProjectExisting(path); - if (project === undefined) { - // Not in a Rome project - return null; - } - - const res = await this.request.requestWorkerFormat(path, {}); - if (res === undefined) { - // Not a file we support formatting - return null; - } - - return diffTextEdits(res.original, res.formatted); - } - - case 'shutdown': { - this.shutdown(); - break; - } - } - - return null; - } - - async handleNotification(method: string, params: Consumer): Promise { - switch (method) { - case 'workspace/didChangeWorkspaceFolders': { - for (const elem of params.get('added').asArray()) { - this.watchProject(getPathFromTextDocument(elem)); - } - for (const elem of params.get('removed').asArray()) { - this.unwatchProject(getPathFromTextDocument(elem)); - } - break; - } - - case 'textDocument/didChange': { - const path = getPathFromTextDocument(params.get('textDocument')); - const content = params.get('contentChanges').asArray()[0].get('text').asString(); - await this.request.requestWorkerUpdateBuffer(path, content); - break; - } - } - } - - normalizeMessage(content: string): undefined | Consumer { - try { - const data = JSON.parse(content); - const consumer = consumeUnknown(data, 'lsp/parse'); - return consumer; - } catch (err) { - if (err instanceof SyntaxError) { - console.error('JSON parse error', content); - return undefined; - } else { - throw err; - } - } - } - - async onMessage(headers: Headers, content: string) { - const consumer = this.normalizeMessage(content); - if (consumer === undefined) { - return; - } - - if (!consumer.has('method')) { - console.error('NO METHOD', content); - return; - } - - const method: string = consumer.get('method').asString(); - const params = consumer.get('params'); - - if (consumer.has('id')) { - const id = consumer.get('id').asNumber(); - - try { - const res: LSPResponseMessage = { - id, - result: await this.handleRequest(method, params), - }; - this.write(res); - } catch (err) { - const res: LSPResponseMessage = { - id, - error: { - code: -32_603, - message: err.message, - }, - }; - this.write(res); - } - } else { - await this.handleNotification(method, params); - } - } - - process() { - switch (this.status) { - case 'IDLE': { - if (this.buffer.length > 0) { - this.status = 'WAITING_FOR_HEADERS_END'; - this.process(); - } - break; - } - - case 'WAITING_FOR_HEADERS_END': { - const endIndex = this.buffer.indexOf(HEADERS_END); - if (endIndex !== -1) { - // Parse headers - const rawHeaders = this.buffer.slice(0, endIndex); - this.nextHeaders = parseHeaders(rawHeaders); - - // Process rest of the buffer - this.status = 'WAITING_FOR_RESPONSE_END'; - this.buffer = this.buffer.slice(endIndex + HEADERS_END.length); - this.process(); - } - break; - } - - case 'WAITING_FOR_RESPONSE_END': { - const headers = this.nextHeaders; - if (headers === undefined) { - throw new Error('Expected headers due to our status'); - } - if (this.buffer.length >= headers.length) { - const content = this.buffer.slice(0, headers.length); - this.onMessage(headers, content); - - // Reset headers and trim content - this.nextHeaders = undefined; - this.buffer = this.buffer.slice(headers.length); - - // Process rest of the buffer - this.status = 'IDLE'; - this.process(); - } - break; - } - } - } - - append(data: string) { - this.buffer += data; - this.process(); - } + constructor(request: MasterRequest) { + this.status = 'IDLE'; + this.buffer = ''; + this.nextHeaders = undefined; + + this.request = request; + this.master = request.master; + this.client = request.client; + + this.lintSessionsPending = new AbsoluteFilePathSet(); + this.lintSessions = new AbsoluteFilePathMap(); + + request.endEvent.subscribe(() => { + this.shutdown(); + }); + } + + request: MasterRequest; + client: MasterClient; + master: Master; + nextHeaders: undefined | Headers; + status: Status; + buffer: string; + lintSessionsPending: AbsoluteFilePathSet; + lintSessions: AbsoluteFilePathMap; + + write(res: JSONObject) { + const json = JSON.stringify(res); + const out = `Content-Length: ${String(json.length)}${HEADERS_END}${json}`; + this.client.bridge.lspFromServerBuffer.send(out); + } + + createFakeMasterRequest( + commandName: string, + args: Array = [], + ): MasterRequest { + return new MasterRequest({ + client: this.client, + master: this.master, + query: { + requestFlags: DEFAULT_CLIENT_REQUEST_FLAGS, + commandFlags: {}, + args, + commandName, + silent: true, + noData: false, + terminateWhenIdle: false, + }, + }); + } + + unwatchProject(path: AbsoluteFilePath) { + // TODO maybe unset all buffers? + const req = this.lintSessions.get(path); + if (req !== undefined) { + req.teardown(EMPTY_SUCCESS_RESPONSE); + this.lintSessions.delete(path); + } + } + + createProgress(opts?: ReporterProgressOptions): ReporterProgress { + return new LSPProgress(this, this.request.reporter, opts); + } + + async watchProject(path: AbsoluteFilePath) { + if (this.lintSessions.has(path) || this.lintSessionsPending.has(path)) { + return; + } + + this.lintSessionsPending.add(path); + + const project = await this.master.projectManager.findProject(path); + + if (project === undefined) { + // Not a Rome project + this.lintSessionsPending.delete(path); + return; + } + + const req = this.createFakeMasterRequest('lsp_project', [path.join()]); + await req.init(); + + const linter = new Linter( + req, + { + save: false, + hasDecisions: false, + formatOnly: false, + }, + ); + + const subscription = await linter.watch({ + onRunStart: () => {}, + createProgress: () => { + return this.createProgress(); + }, + onChanges: ({changes}) => { + for (const {ref, diagnostics} of changes) { + if (ref === undefined) { + // Cannot display diagnostics without a reference + continue; + } + + // We want to filter pendingFixes because we'll autoformat the file on save if necessary and it's just noise + const processor = this.request.createDiagnosticsProcessor(); + processor.addFilter({ + category: 'lint/pendingFixes', + }); + processor.addDiagnostics(diagnostics); + + this.write({ + method: 'textDocument/publishDiagnostics', + params: { + uri: `file://${ref.real.join()}`, + diagnostics: convertDiagnosticsToLSP( + processor.getDiagnostics(), + this.master, + ), + }, + }); + } + }, + }); + + req.endEvent.subscribe(() => { + subscription.unsubscribe(); + }); + + this.lintSessions.set(path, req); + this.lintSessionsPending.delete(path); + } + + shutdown() { + for (const path of this.lintSessions.keys()) { + this.unwatchProject(path); + } + this.lintSessions.clear(); + } + + async sendClientRequest( + req: PartialMasterQueryRequest, + ): Promise { + return this.master.handleRequest( + this.client, + { + silent: true, + ...req, + }, + ); + } + + async handleRequest( + method: string, + params: Consumer, + ): Promise { + switch (method) { + case 'initialize': { + const rootUri = params.get('rootUri'); + if (rootUri.exists()) { + this.watchProject(createAbsoluteFilePath(rootUri.asString())); + } + + const workspaceFolders = params.get('workspaceFolders'); + if (workspaceFolders.exists()) { + for (const elem of workspaceFolders.asArray()) { + this.watchProject(getPathFromTextDocument(elem)); + } + } + + return { + capabilities: { + textDocumentSync: { + openClose: true, + // This sends over the full text on change. We should make this incremental later + change: 1, + }, + documentFormattingProvider: true, + workspaceFolders: { + supported: true, + changeNotifications: true, + }, + }, + serverInfo: { + name: 'rome', + }, + }; + } + + case 'textDocument/formatting': { + const path = getPathFromTextDocument(params.get('textDocument')); + + const project = this.master.projectManager.findProjectExisting(path); + if (project === undefined) { + // Not in a Rome project + return null; + } + + const res = await this.request.requestWorkerFormat(path, {}); + if (res === undefined) { + // Not a file we support formatting + return null; + } + + return diffTextEdits(res.original, res.formatted); + } + + case 'shutdown': { + this.shutdown(); + break; + } + } + + return null; + } + + async handleNotification(method: string, params: Consumer): Promise { + switch (method) { + case 'workspace/didChangeWorkspaceFolders': { + for (const elem of params.get('added').asArray()) { + this.watchProject(getPathFromTextDocument(elem)); + } + for (const elem of params.get('removed').asArray()) { + this.unwatchProject(getPathFromTextDocument(elem)); + } + break; + } + + case 'textDocument/didChange': { + const path = getPathFromTextDocument(params.get('textDocument')); + const content = params.get('contentChanges').asArray()[0].get('text').asString(); + await this.request.requestWorkerUpdateBuffer(path, content); + break; + } + } + } + + normalizeMessage(content: string): undefined | Consumer { + try { + const data = JSON.parse(content); + const consumer = consumeUnknown(data, 'lsp/parse'); + return consumer; + } catch (err) { + if (err instanceof SyntaxError) { + console.error('JSON parse error', content); + return undefined; + } else { + throw err; + } + } + } + + async onMessage(headers: Headers, content: string) { + const consumer = this.normalizeMessage(content); + if (consumer === undefined) { + return; + } + + if (!consumer.has('method')) { + console.error('NO METHOD', content); + return; + } + + const method: string = consumer.get('method').asString(); + const params = consumer.get('params'); + + if (consumer.has('id')) { + const id = consumer.get('id').asNumber(); + + try { + const res: LSPResponseMessage = { + id, + result: await this.handleRequest(method, params), + }; + this.write(res); + } catch (err) { + const res: LSPResponseMessage = { + id, + error: { + code: -32_603, + message: err.message, + }, + }; + this.write(res); + } + } else { + await this.handleNotification(method, params); + } + } + + process() { + switch (this.status) { + case 'IDLE': { + if (this.buffer.length > 0) { + this.status = 'WAITING_FOR_HEADERS_END'; + this.process(); + } + break; + } + + case 'WAITING_FOR_HEADERS_END': { + const endIndex = this.buffer.indexOf(HEADERS_END); + if (endIndex !== -1) { + // Parse headers + const rawHeaders = this.buffer.slice(0, endIndex); + this.nextHeaders = parseHeaders(rawHeaders); + + // Process rest of the buffer + this.status = 'WAITING_FOR_RESPONSE_END'; + this.buffer = this.buffer.slice(endIndex + HEADERS_END.length); + this.process(); + } + break; + } + + case 'WAITING_FOR_RESPONSE_END': { + const headers = this.nextHeaders; + if (headers === undefined) { + throw new Error('Expected headers due to our status'); + } + if (this.buffer.length >= headers.length) { + const content = this.buffer.slice(0, headers.length); + this.onMessage(headers, content); + + // Reset headers and trim content + this.nextHeaders = undefined; + this.buffer = this.buffer.slice(headers.length); + + // Process rest of the buffer + this.status = 'IDLE'; + this.process(); + } + break; + } + } + } + + append(data: string) { + this.buffer += data; + this.process(); + } } diff --git a/packages/@romejs/core/master/lsp/types.ts b/packages/@romejs/core/master/lsp/types.ts index 624a8f8acc0..2a829294ac5 100644 --- a/packages/@romejs/core/master/lsp/types.ts +++ b/packages/@romejs/core/master/lsp/types.ts @@ -8,94 +8,94 @@ import {JSONPropertyValue} from '@romejs/codec-json'; * LICENSE file in the root directory of this source tree. */ export type LSPRequestMessage = { - /** + /** * The request id. */ - id: number | string; + id: number | string; - /** + /** * The method to be invoked. */ - method: string; + method: string; - /** + /** * The method's params. */ - params?: unknown; + params?: unknown; }; export type LSPResponseMessage = { - /** + /** * The request id. */ - id: number | string | null; + id: number | string | null; - /** + /** * The result of a request. This member is REQUIRED on success. * This member MUST NOT exist if there was an error invoking the method. */ - result?: JSONPropertyValue; + result?: JSONPropertyValue; - /** + /** * The error object in case a request fails. */ - error?: LSPResponseError; + error?: LSPResponseError; }; export type LSPResponseError = { - /** + /** * A number indicating the error type that occurred. */ - code: LSPErrorCodes[keyof LSPErrorCodes]; + code: LSPErrorCodes[keyof LSPErrorCodes]; - /** + /** * A string providing a short description of the error. */ - message: string; + message: string; - /** + /** * A primitive or structured value that contains additional * information about the error. Can be omitted. */ - data?: JSONPropertyValue; + data?: JSONPropertyValue; }; export type LSPNotificationMessage = { - /** + /** * The method to be invoked. */ - method: string; + method: string; - /** + /** * The notification's params. */ - params?: JSONPropertyValue; + params?: JSONPropertyValue; }; export type LSPErrorCodes = { - // Defined by JSON RPC - ParseError: -32700; - InvalidRequest: -32600; - MethodNotFound: -32601; - InvalidParams: -32602; - InternalError: -32603; - serverErrorStart: -32099; - serverErrorEnd: -32000; - ServerNotInitialized: -32002; - UnknownErrorCode: -32001; - - // Defined by the protocol. - RequestCancelled: -32800; - ContentModified: -32801; + // Defined by JSON RPC + ParseError: -32700; + InvalidRequest: -32600; + MethodNotFound: -32601; + InvalidParams: -32602; + InternalError: -32603; + serverErrorStart: -32099; + serverErrorEnd: -32000; + ServerNotInitialized: -32002; + UnknownErrorCode: -32001; + + // Defined by the protocol. + RequestCancelled: -32800; + ContentModified: -32801; }; export type LSPPosition = { - /** + /** * Line position in a document (zero-based). */ - line: Number0; + line: Number0; - /** + /** * Character offset on a line in a document (zero-based). Assuming that the line is * represented as a string, the `character` value represents the gap between the * `character` and `character + 1`. @@ -103,115 +103,115 @@ export type LSPPosition = { * If the character value is greater than the line length it defaults back to the * line length. */ - character: Number0; + character: Number0; }; export type LSPRange = { - /** + /** * The range's start position. */ - start: LSPPosition; + start: LSPPosition; - /** + /** * The range's end position. */ - end: LSPPosition; + end: LSPPosition; }; export type LSPDocumentUri = string; export type LSPLocation = { - uri: LSPDocumentUri; - range: LSPRange; + uri: LSPDocumentUri; + range: LSPRange; }; export type LSPLocationLink = { - /** + /** * Span of the origin of this link. * * Used as the underlined span for mouse interaction. Defaults to the word range at * the mouse position. */ - originSelectionRange?: LSPRange; + originSelectionRange?: LSPRange; - /** + /** * The target resource identifier of this link. */ - targetUri: LSPDocumentUri; + targetUri: LSPDocumentUri; - /** + /** * The full target range of this link. If the target for example is a symbol then target range is the * range enclosing this symbol not including leading/trailing whitespace but everything else * like comments. This information is typically used to highlight the range in the editor. */ - targetRange: LSPRange; + targetRange: LSPRange; - /** + /** * The range that should be selected and revealed when this link is being followed, e.g the name of a function. * Must be contained by the the `targetRange`. See also `DocumentSymbol#range` */ - targetSelectionRange: LSPRange; + targetSelectionRange: LSPRange; }; export type LSPDiagnostic = { - /** + /** * The range at which the message applies. */ - range: LSPRange; + range: LSPRange; - /** + /** * The diagnostic's severity. Can be omitted. If omitted it is up to the * client to interpret diagnostics as error, warning, info or hint. */ - severity?: LSPDiagnosticSeverity[keyof LSPDiagnosticSeverity]; + severity?: LSPDiagnosticSeverity[keyof LSPDiagnosticSeverity]; - /** + /** * The diagnostic's code, which might appear in the user interface. */ - code?: number | string; + code?: number | string; - /** + /** * A human-readable string describing the source of this * diagnostic, e.g. 'typescript' or 'super lint'. */ - source?: string; + source?: string; - /** + /** * The diagnostic's message. */ - message: string; + message: string; - /** + /** * Additional metadata about the diagnostic. * * @since 3.15.0 */ - tags?: Array; + tags?: Array; - /** + /** * An array of related diagnostic information, e.g. when symbol-names within * a scope collide all definitions can be marked via this property. */ - relatedInformation?: Array; + relatedInformation?: Array; }; export type LSPDiagnosticSeverity = { - /** + /** * Reports an error. */ - Error: 1; - /** + Error: 1; + /** * Reports a warning. */ - Warning: 2; - /** + Warning: 2; + /** * Reports an information. */ - Information: 3; - /** + Information: 3; + /** * Reports a hint. */ - Hint: 4; + Hint: 4; }; /** @@ -220,19 +220,19 @@ export type LSPDiagnosticSeverity = { * @since 3.15.0 */ export type LSPDiagnosticTag = { - /** + /** * Unused or unnecessary code. * * Clients are allowed to render diagnostics with this tag faded out instead of having * an error squiggle. */ - Unnecessary: 1; - /** + Unnecessary: 1; + /** * Deprecated or obsolete code. * * Clients are allowed to rendered diagnostics with this tag strike through. */ - Deprecated: 2; + Deprecated: 2; }; /** @@ -241,27 +241,27 @@ export type LSPDiagnosticTag = { * a symbol in a scope. */ export type LSPDiagnosticRelatedInformation = { - /** + /** * The location of this related diagnostic information. */ - location: LSPLocation; + location: LSPLocation; - /** + /** * The message of this related diagnostic information. */ - message: string; + message: string; }; export type LSPTextEdit = { - /** + /** * The range of the text document to be manipulated. To insert * text into a document create a range where start === end. */ - range: LSPRange; + range: LSPRange; - /** + /** * The string to be inserted. For delete operations use an * empty string. */ - newText: string; + newText: string; }; diff --git a/packages/@romejs/core/master/project/ProjectManager.ts b/packages/@romejs/core/master/project/ProjectManager.ts index 79aacc43cd1..488333d8a20 100644 --- a/packages/@romejs/core/master/project/ProjectManager.ts +++ b/packages/@romejs/core/master/project/ProjectManager.ts @@ -7,46 +7,46 @@ import Master from '../Master'; import { - DEFAULT_PROJECT_CONFIG, - DEFAULT_PROJECT_CONFIG_META, - ProjectConfig, - ProjectConfigMeta, - ProjectDefinition, - ROME_CONFIG_FILENAMES, - ROME_CONFIG_PACKAGE_JSON_FIELD, - ROME_CONFIG_WARN_FILENAMES, - assertHardMeta, - loadCompleteProjectConfig, - serializeJSONProjectConfig, + DEFAULT_PROJECT_CONFIG, + DEFAULT_PROJECT_CONFIG_META, + ProjectConfig, + ProjectConfigMeta, + ProjectDefinition, + ROME_CONFIG_FILENAMES, + ROME_CONFIG_PACKAGE_JSON_FIELD, + ROME_CONFIG_WARN_FILENAMES, + assertHardMeta, + loadCompleteProjectConfig, + serializeJSONProjectConfig, } from '@romejs/project'; import { - WorkerPartialManifests, - WorkerProjects, + WorkerPartialManifests, + WorkerProjects, } from '../../common/bridges/WorkerBridge'; import {WorkerContainer} from '../WorkerManager'; import { - DiagnosticLocation, - DiagnosticsProcessor, - createSingleDiagnosticError, - descriptions, + DiagnosticLocation, + DiagnosticsProcessor, + createSingleDiagnosticError, + descriptions, } from '@romejs/diagnostics'; import { - ManifestDefinition, - manifestNameToString, + ManifestDefinition, + manifestNameToString, } from '@romejs/codec-js-manifest'; import { - AbsoluteFilePath, - AbsoluteFilePathMap, - AbsoluteFilePathSet, - URLFilePath, - UnknownFilePath, - UnknownFilePathMap, - createAbsoluteFilePath, + AbsoluteFilePath, + AbsoluteFilePathMap, + AbsoluteFilePathSet, + URLFilePath, + UnknownFilePath, + UnknownFilePathMap, + createAbsoluteFilePath, } from '@romejs/path'; import {FileReference, JSONFileReference} from '../../common/types/files'; import { - GetFileHandlerResult, - getFileHandler, + GetFileHandlerResult, + getFileHandler, } from '../../common/file-handlers/index'; import {IMPLICIT_JS_EXTENSIONS} from '../../common/file-handlers/javascript'; import {createDirectory, readFileText} from '@romejs/fs'; @@ -55,858 +55,850 @@ import {consumeJSON} from '@romejs/codec-json'; import {VCSClient, getVCSClient} from '@romejs/vcs'; function cleanUidParts(parts: Array): string { - let uid = ''; - - let lastPart = ''; - for (const part of parts) { - if (uid !== '') { - uid += '/'; - } - - // Prune off any prefix shared with the last part - let sharedPrefix = ''; - for (let i = 0; i < part.length && lastPart[i] === part[i]; i++) { - sharedPrefix += part[i]; - } - - const partWithoutExtension = part.split('.')[0]; - if (sharedPrefix === partWithoutExtension) { - uid += part; - } else { - uid += part.slice(sharedPrefix.length); - } - - lastPart = part; - } - - return uid; + let uid = ''; + + let lastPart = ''; + for (const part of parts) { + if (uid !== '') { + uid += '/'; + } + + // Prune off any prefix shared with the last part + let sharedPrefix = ''; + for (let i = 0; i < part.length && lastPart[i] === part[i]; i++) { + sharedPrefix += part[i]; + } + + const partWithoutExtension = part.split('.')[0]; + if (sharedPrefix === partWithoutExtension) { + uid += part; + } else { + uid += part.slice(sharedPrefix.length); + } + + lastPart = part; + } + + return uid; } // If a UID has a relative path that's just index.js, index.ts etc then omit it function cleanRelativeUidPath(relative: UnknownFilePath): undefined | string { - return relative.join(); - - const segments = relative.getSegments(); - - // Quick deopt if there last segment is not index. - if (!segments[segments.length - 1].startsWith('index.')) { - return relative.join(); - } - - // Verify and pop off the last segment if it matches index.VALID_JS_EXTENSION - const basename = relative.getBasename(); - for (const ext of IMPLICIT_JS_EXTENSIONS) { - // Got a matching basename that we should omit - if (basename === `index.${ext}`) { - if (segments.length === 1) { - // If there's a single segment then we don't want anything - return undefined; - } else { - return relative.getParent().join(); - } - } - } - - // No matches, we hit the index. check above but not any of the valid extensions - return relative.join(); + return relative.join(); + + const segments = relative.getSegments(); + + // Quick deopt if there last segment is not index. + if (!segments[segments.length - 1].startsWith('index.')) { + return relative.join(); + } + + // Verify and pop off the last segment if it matches index.VALID_JS_EXTENSION + const basename = relative.getBasename(); + for (const ext of IMPLICIT_JS_EXTENSIONS) { + // Got a matching basename that we should omit + if (basename === `index.${ext}`) { + if (segments.length === 1) { + // If there's a single segment then we don't want anything + return undefined; + } else { + return relative.getParent().join(); + } + } + } + + // No matches, we hit the index. check above but not any of the valid extensions + return relative.join(); } export type ProjectConfigSource = { - consumer: Consumer; - value: undefined | Consumer; + consumer: Consumer; + value: undefined | Consumer; }; export default class ProjectManager { - constructor(master: Master) { - this.master = master; - - this.isAddingProject = false; - this.pendingAddProjects = []; - - this.projectIdCounter = 0; - this.projectFolderToId = new AbsoluteFilePathMap(); - this.projectConfigDependenciesToIds = new AbsoluteFilePathMap(); - this.fileToProject = new AbsoluteFilePathMap(); - this.projects = new Map(); - - // We maintain these maps so we can reverse any uids, and protect against collisions - this.uidToFilename = new Map(); - this.filenameToUid = new AbsoluteFilePathMap(); - this.remoteToLocalPath = new UnknownFilePathMap(); - this.localPathToRemote = new AbsoluteFilePathMap(); - } - - master: Master; - - isAddingProject: boolean; - pendingAddProjects: Array<{ - projectFolder: AbsoluteFilePath; - configPath: AbsoluteFilePath; - resolve: (project: ProjectDefinition) => void; - }>; - - uidToFilename: Map; - filenameToUid: AbsoluteFilePathMap; - - remoteToLocalPath: UnknownFilePathMap; - localPathToRemote: AbsoluteFilePathMap; - - projects: Map; - projectConfigDependenciesToIds: AbsoluteFilePathMap>; - projectIdCounter: number; - fileToProject: AbsoluteFilePathMap<{ - projectId: number; - path: AbsoluteFilePath; - }>; - - projectFolderToId: AbsoluteFilePathMap; - - async init() { - this.master.memoryFs.deletedFileEvent.subscribe((path) => { - this.handleDeleted(path); - }); - - const vendorProjectConfig: ProjectConfig = { - ...DEFAULT_PROJECT_CONFIG, - name: 'rome-internal-remote', - }; - const defaultVendorPath = vendorProjectConfig.files.vendorPath; - await createDirectory(defaultVendorPath, {recursive: true}); - await this.addProjectWithConfig({ - projectFolder: defaultVendorPath, - meta: DEFAULT_PROJECT_CONFIG_META, - config: vendorProjectConfig, - }); - await this.master.memoryFs.watch(defaultVendorPath, vendorProjectConfig); - } - - handleDeleted(path: AbsoluteFilePath) { - const filename = path.join(); - - this.projectConfigDependenciesToIds.delete(path); - this.fileToProject.delete(path); - - // Remove uids - const uid = this.filenameToUid.get(path); - this.filenameToUid.delete(path); - if (uid !== undefined) { - this.uidToFilename.delete(filename); - } - } - - getRemoteFromLocalPath(path: AbsoluteFilePath): undefined | URLFilePath { - return this.localPathToRemote.get(path); - } - - getFilePathFromUid(uid: string): undefined | AbsoluteFilePath { - return this.uidToFilename.get(uid); - } - - getFilePathFromUidOrAbsolute( - uid: undefined | string, - ): undefined | AbsoluteFilePath { - if (uid === undefined) { - return undefined; - } - - const uidToPath = this.getFilePathFromUid(uid); - if (uidToPath !== undefined) { - return uidToPath; - } - - const path = createAbsoluteFilePath(uid); - if (path.isAbsolute()) { - return path.assertAbsolute(); - } - - return undefined; - } - - normalizeFilenamesToFilePaths( - filenames: Iterable, - ): { - absolutes: AbsoluteFilePathSet; - others: Set; - } { - const others: Set = new Set(); - const absolutes = new AbsoluteFilePathSet(); - - for (const filename of filenames) { - if (filename === undefined) { - others.add(undefined); - continue; - } - - const absolute = this.getFilePathFromUidOrAbsolute(filename); - if (absolute === undefined) { - // Relative path - others.add(filename); - } else { - absolutes.add(absolute); - } - } - - return {absolutes, others}; - } - - setUid(path: AbsoluteFilePath, uid: string) { - const filename = path.join(); - - // Verify we didn't already generate this uid for another file - const collided = this.uidToFilename.get(uid); - if (collided !== undefined && !collided.equal(path)) { - throw new Error( - `UID collision between ${filename} and ${collided}: ${uid}`, - ); - } - - this.uidToFilename.set(uid, path); - this.filenameToUid.set(path, uid); - } - - getUid(path: AbsoluteFilePath): string { - // Allow passing in a UID - const filename = path.join(); - if (this.uidToFilename.has(filename)) { - return filename; - } - - // Check if we've already calculated and saved a UID - const existing = this.filenameToUid.get(path); - if (existing !== undefined) { - return existing; - } - - const project = this.assertProjectExisting(path); - - // Format of uids will be // - const parts: Array = []; - - let root = project.folder; - - // Push on parent package names - let targetPackagePath = path; - while (true) { - const pkg = this.master.memoryFs.getOwnedManifest(targetPackagePath); - if (pkg === undefined || pkg.folder.equal(project.folder)) { - break; - } else { - const name = manifestNameToString(pkg.manifest.name); - if (name !== undefined) { - parts.unshift(name); - - if (targetPackagePath === path) { - root = pkg.folder; - } - } - targetPackagePath = pkg.folder.getParent(); - } - } - - parts.unshift(project.config.name); - - const relative = cleanRelativeUidPath(root.relative(path)); - if (relative !== undefined) { - parts.push(relative); - } - - const uid = cleanUidParts(parts); - this.setUid(path, uid); - return uid; - } - - getFileReference(path: AbsoluteFilePath): FileReference { - const project = this.assertProjectExisting(path); - const uid = this.getUid(path); - const pkg = this.master.memoryFs.getOwnedManifest(path); - return { - uid, - project: project.id, - real: path, - manifest: pkg === undefined ? undefined : pkg.id, - relative: project.folder.relative(path).assertRelative(), - remote: this.localPathToRemote.has(path), - }; - } - - getURLFileReference(local: AbsoluteFilePath, url: URLFilePath): FileReference { - if (!this.remoteToLocalPath.has(url)) { - this.remoteToLocalPath.set(url, local); - this.localPathToRemote.set(local, url); - } - - return this.getFileReference(local); - } - - getTransportFileReference(path: AbsoluteFilePath): JSONFileReference { - const ref = this.getFileReference(path); - return { - ...ref, - relative: ref.relative.join(), - real: ref.real.join(), - }; - } - - async maybeEvictPossibleConfig(path: AbsoluteFilePath): Promise { - // TODO not sure if this case handles new manifests? - // check if this filename is a rome config dependency - const projectIds = this.projectConfigDependenciesToIds.get(path); - if (projectIds === undefined) { - return false; - } - - const projectsToEvict: Set = new Set(); - - function getAllProjects(project: ProjectDefinition) { - let children: Array = []; - for (const child of project.children) { - children = children.concat(getAllProjects(child)); - } - return [project, ...children]; - } - - for (const evictProjectId of projectIds) { - // Fetch the project - const project = this.projects.get(evictProjectId); - if (project === undefined) { - throw new Error( - `Expected project of id ${evictProjectId} since it was declared in projectConfigLocsToId`, - ); - } - - // Add all parent projects - let topProject = project; - while (topProject.parent !== undefined) { - topProject = topProject.parent; - } - for (const project of getAllProjects(topProject)) { - projectsToEvict.add(project); - } - } - - for (const project of projectsToEvict) { - await this.evictProject(project); - } - - return true; - } - - async evictProject(project: ProjectDefinition) { - const evictProjectId = project.id; - - // Remove the config locs from 'our internal map that belong to this project - for (const [configLoc, projectIds] of this.projectConfigDependenciesToIds) { - if (projectIds.has(evictProjectId)) { - projectIds.delete(evictProjectId); - } - - if (projectIds.size === 0) { - this.projectConfigDependenciesToIds.delete(configLoc); - } - } - - // Notify all workers that it should delete the project - for (const {bridge} of this.master.workerManager.getWorkers()) { - // Evict project - bridge.updateProjects.send({ - projects: [ - { - id: evictProjectId, - folder: project.folder.join(), - config: undefined, - }, - ], - }); - - // Evict packages - bridge.updateManifests.send({ - manifests: Array.from( - project.manifests.values(), - (def) => ({ - id: def.id, - manifest: undefined, - }), - ), - }); - } - - // Delete the project from 'our internal map - this.projects.delete(evictProjectId); - this.projectFolderToId.delete(project.folder); - - // Evict all files that belong to this project and delete their project mapping - const ownedFiles: Array = []; - for (const {projectId, path} of this.fileToProject.values()) { - if (evictProjectId === projectId) { - this.handleDeleted(path); - ownedFiles.push(path); - } - } - await Promise.all( - ownedFiles.map((path) => this.master.fileAllocator.evict(path)), - ); - - // Tell the MemoryFileSystem to stop watching and clear it's maps - this.master.memoryFs.unwatch(project.folder); - } - - getProjects(): Array { - return Array.from(this.projects.values()); - } - - async queueAddProject( - projectFolder: AbsoluteFilePath, - configPath: AbsoluteFilePath, - ): Promise { - // Check if we've already loaded this project - const maybeProject = this.findProjectExisting(projectFolder); - if (maybeProject !== undefined) { - return maybeProject; - } - - // If we're currently adding a project then add it to the queue - if (this.isAddingProject) { - return new Promise((resolve) => { - this.pendingAddProjects.push({projectFolder, configPath, resolve}); - }); - } - - // First time loading this project - this.isAddingProject = true; - - // fetch this project - const mainProject = await this.addProject(projectFolder, configPath); - const resolvedProjectsByDir: Map = new Map(); - resolvedProjectsByDir.set(projectFolder.join(), mainProject); - - // Resolve all pending projects that were added while we were adding the current project - const resolvedProjects: Array<{ - project: ProjectDefinition; - resolve: (project: ProjectDefinition) => void; - }> = []; - for (const {projectFolder, configPath, resolve} of this.pendingAddProjects) { - // Check if the project has already been resolved - const existing = resolvedProjectsByDir.get(projectFolder.join()); - if (existing !== undefined) { - resolvedProjects.push({project: existing, resolve}); - } else { - // It hasn't been resolved yet so let's add it - const project = await this.addProject(projectFolder, configPath); - resolvedProjects.push({project, resolve}); - } - } - - // Resolve all promises - for (const {project, resolve} of resolvedProjects) { - resolve(project); - } - - // Cleanup - this.pendingAddProjects = []; - this.isAddingProject = false; - - return mainProject; - } - - addDependencyToProjectId(path: AbsoluteFilePath, projectId: number): void { - const ids = this.projectConfigDependenciesToIds.get(path); - - if (ids === undefined) { - this.projectConfigDependenciesToIds.set(path, new Set([projectId])); - } else { - ids.add(projectId); - } - } - - findProjectConfigConsumer( - def: ProjectDefinition, - test: (consumer: Consumer) => undefined | false | Consumer, - ): ProjectConfigSource { - const meta = assertHardMeta(def.meta); - - for (const consumer of meta.consumersChain) { - const value = test(consumer); - if (value !== undefined && value !== false && value.exists()) { - return {value, consumer: meta.consumer}; - } - } - - return {value: undefined, consumer: meta.consumer}; - } - - async getVCSClient(project: ProjectDefinition): Promise { - const client = await this.maybeGetVCSClient(project); - - if (client === undefined) { - const { - value: rootConfigConsumer, - consumer, - } = this.findProjectConfigConsumer( - project, - (consumer) => consumer.has('vsc') && consumer.get('vsc').get('root'), - ); - - const rootConfigLocation: undefined | DiagnosticLocation = - rootConfigConsumer === undefined - ? undefined - : rootConfigConsumer.getDiagnosticLocation(); - - const location: DiagnosticLocation = - rootConfigLocation === undefined - ? consumer.getDiagnosticLocation() - : rootConfigLocation; - - throw createSingleDiagnosticError({ - description: descriptions.PROJECT_MANAGER.NO_VCS(rootConfigLocation), - location, - }); - } else { - return client; - } - } - - async maybeGetVCSClient( - project: ProjectDefinition, - ): Promise { - return await getVCSClient(project.config.vcs.root); - } - - async addProject( - projectFolder: AbsoluteFilePath, - configPath: AbsoluteFilePath, - ): Promise { - const {config, meta} = loadCompleteProjectConfig(projectFolder, configPath); - - return this.addProjectWithConfig({ - projectFolder, - meta, - config, - }); - } - - async addProjectWithConfig( - { - projectFolder, - meta, - config, - }: { - projectFolder: AbsoluteFilePath; - meta: ProjectConfigMeta; - config: ProjectConfig; - }, - ): Promise { - // Make sure there's no project with the same `name` as us - for (const project of this.projects.values()) { - if (project.config.name === config.name) { - throw new Error( - `Conflicting project names. ${projectFolder} and ${project.folder}`, - ); - } - } - - // Declare the project - const parentProject = this.findProjectExisting(projectFolder.getParent()); - const project: ProjectDefinition = { - config, - meta, - folder: projectFolder, - id: this.projectIdCounter++, - packages: new Map(), - manifests: new Map(), - parent: parentProject, - children: new Set(), - }; - - this.projects.set(project.id, project); - this.fileToProject.set( - projectFolder, - { - path: projectFolder, - projectId: project.id, - }, - ); - this.projectFolderToId.set(projectFolder, project.id); - - if (parentProject !== undefined) { - parentProject.children.add(project); - } - - // Add all project config dependencies so changes invalidate the whole project - if (meta.configPath !== undefined) { - this.addDependencyToProjectId(meta.configPath, project.id); - } - for (const loc of meta.configDependencies) { - this.addDependencyToProjectId(loc, project.id); - } - - // Notify other pieces of our creation - this.master.workerManager.onNewProject(project); - - // Start watching and crawl this project folder - await this.master.memoryFs.watch(projectFolder, config); - - return project; - } - - declareManifest( - project: ProjectDefinition, - isProjectPackage: boolean, - def: ManifestDefinition, - diagnostics: DiagnosticsProcessor, - ) { - const name = manifestNameToString(def.manifest.name); - - // Declare this package in all projects - const projects = this.getHierarchyFromProject(project); - - // Check for collisions - if (isProjectPackage && name !== undefined) { - for (const project of projects) { - // If there is no package then there's nothing to collide - const existingPackage = project.packages.get(name); - if (existingPackage === undefined) { - continue; - } - - diagnostics.addDiagnostic({ - description: descriptions.PROJECT_MANAGER.DUPLICATE_PACKAGE( - name, - existingPackage.path.join(), - ), - location: def.consumer.get('name').getDiagnosticLocation( - 'inner-value', - ), - }); - return; - } - } - - // Set as a package - for (const project of projects) { - this.addDependencyToProjectId(def.path, project.id); - project.manifests.set(def.id, def); - - if (isProjectPackage && name !== undefined) { - project.packages.set(name, def); - } - } - } - - async notifyWorkersOfProjects( - workers: Array, - projects?: Array, - ): Promise { - if (projects === undefined) { - projects = Array.from(this.projects.values()); - } - - const manifestsSerial: WorkerPartialManifests = []; - const projectsSerial: WorkerProjects = []; - for (const project of projects) { - projectsSerial.push({ - config: serializeJSONProjectConfig(project.config), - id: project.id, - folder: project.folder.join(), - }); - - for (const def of project.manifests.values()) { - manifestsSerial.push({ - id: def.id, - manifest: this.master.memoryFs.getPartialManifest(def), - }); - } - } - - const promises = []; - - for (const worker of workers) { - promises.push( - worker.bridge.updateProjects.call({projects: projectsSerial}), - ); - promises.push( - worker.bridge.updateManifests.call({ - manifests: manifestsSerial, - }), - ); - } - - await Promise.all(promises); - } - - async assertProject( - path: AbsoluteFilePath, - location?: DiagnosticLocation, - ): Promise { - // We won't recurse up and check a parent project if we've already visited it - const syncProject = this.findProjectExisting(path); - const project = syncProject || (await this.findProject(path)); - - if (project) { - // Continue searching for projects up the directory - // We don't do this for root projects since it would be a waste, but there's no implications other than some unnecessary work if we did - if (project.config.root === false && syncProject === undefined) { - await this.findProject(project.folder.getParent()); - } - - return project; - } - - if (location === undefined) { - throw new Error( - `Couldn't find a project. Checked ${ROME_CONFIG_FILENAMES.join(' or ')} for ${path.join()}`, - ); - } - - throw createSingleDiagnosticError({ - location, - description: descriptions.PROJECT_MANAGER.NOT_FOUND, - }); - } - - // Convenience method to get the project config and pass it to the file handler class - getHandlerWithProject(path: AbsoluteFilePath): GetFileHandlerResult { - const project = this.findProjectExisting(path); - if (project === undefined) { - return {ext: '', handler: undefined}; - } else { - return getFileHandler(path, project.config); - } - } - - getHierarchyFromFilename(path: AbsoluteFilePath): Array { - const project = this.findProjectExisting(path); - if (project === undefined) { - return []; - } else { - return this.getHierarchyFromProject(project); - } - } - - getHierarchyFromProject(project: ProjectDefinition): Array { - const projects: Array = []; - - let currProject: undefined | ProjectDefinition = project; - while (currProject !== undefined) { - projects.push(currProject); - - // root projects shouldn't be considered to have any parents - if (currProject.config.root) { - break; - } - - currProject = project.parent; - } - - return projects; - } - - assertProjectExisting(path: AbsoluteFilePath): ProjectDefinition { - const project = this.findProjectExisting(path); - if (project === undefined) { - throw new Error(`Expected existing project for ${path.join()}`); - } - return project; - } - - findProjectExisting(cwd: AbsoluteFilePath): undefined | ProjectDefinition { - const tried: Array = []; - - for (const dir of cwd.getChain()) { - const cached = this.fileToProject.get(dir); - if (cached === undefined) { - tried.push(dir); - } else { - for (const dir of tried) { - this.fileToProject.set(dir, cached); - } - - const project = this.projects.get(cached.projectId); - if (project === undefined) { - throw new Error( - 'Expected project from project id found in fileToProject', - ); - } - return project; - } - } - - return undefined; - } - - async findProject( - cwd: AbsoluteFilePath, - ): Promise { - // Check if we have an existing project - const syncProject = this.findProjectExisting(cwd); - if (syncProject !== undefined) { - return syncProject; - } - - const parentDirectories = cwd.getChain(); - - // If not then let's access the file system and try to find one - for (const dir of parentDirectories) { - // Check for dedicated project configs - for (const configFilename of ROME_CONFIG_FILENAMES) { - // Check in root - const configPath = dir.append(configFilename); - - const hasProject = await this.master.memoryFs.existsHard(configPath); - if (hasProject) { - return this.queueAddProject(dir, configPath); - } - } - - // Check for package.json - const packagePath = dir.append('package.json'); - if (await this.master.memoryFs.existsHard(packagePath)) { - const input = await readFileText(packagePath); - const json = await consumeJSON({input, path: packagePath}); - if (json.has(ROME_CONFIG_PACKAGE_JSON_FIELD)) { - return this.queueAddProject(dir, packagePath); - } - } - } - - // If we didn't find a project config then check for incorrect config filenames - for (const dir of parentDirectories) { - for (const basename of ROME_CONFIG_WARN_FILENAMES) { - const path = dir.append(basename); - - if (await this.master.memoryFs.existsHard(path)) { - this.warnIncorrectConfigFile( - path, - DiagnosticsProcessor.createImmediateThrower([ - { - category: 'project-manager', - message: 'Find project', - }, - ]), - ); - } - } - } - - return undefined; - } - - checkConfigFile(path: AbsoluteFilePath, diagnostics: DiagnosticsProcessor) { - if (ROME_CONFIG_WARN_FILENAMES.includes(path.getBasename())) { - this.warnIncorrectConfigFile(path, diagnostics); - } - } - - warnIncorrectConfigFile( - path: AbsoluteFilePath, - diagnostics: DiagnosticsProcessor, - ) { - diagnostics.addDiagnostic({ - description: descriptions.PROJECT_MANAGER.INCORRECT_CONFIG_FILENAME( - ROME_CONFIG_FILENAMES, - ), - location: { - filename: path.join(), - }, - }); - } + constructor(master: Master) { + this.master = master; + + this.isAddingProject = false; + this.pendingAddProjects = []; + + this.projectIdCounter = 0; + this.projectFolderToId = new AbsoluteFilePathMap(); + this.projectConfigDependenciesToIds = new AbsoluteFilePathMap(); + this.fileToProject = new AbsoluteFilePathMap(); + this.projects = new Map(); + + // We maintain these maps so we can reverse any uids, and protect against collisions + this.uidToFilename = new Map(); + this.filenameToUid = new AbsoluteFilePathMap(); + this.remoteToLocalPath = new UnknownFilePathMap(); + this.localPathToRemote = new AbsoluteFilePathMap(); + } + + master: Master; + + isAddingProject: boolean; + pendingAddProjects: Array<{ + projectFolder: AbsoluteFilePath; + configPath: AbsoluteFilePath; + resolve: (project: ProjectDefinition) => void; + }>; + + uidToFilename: Map; + filenameToUid: AbsoluteFilePathMap; + + remoteToLocalPath: UnknownFilePathMap; + localPathToRemote: AbsoluteFilePathMap; + + projects: Map; + projectConfigDependenciesToIds: AbsoluteFilePathMap>; + projectIdCounter: number; + fileToProject: AbsoluteFilePathMap<{ + projectId: number; + path: AbsoluteFilePath; + }>; + + projectFolderToId: AbsoluteFilePathMap; + + async init() { + this.master.memoryFs.deletedFileEvent.subscribe((path) => { + this.handleDeleted(path); + }); + + const vendorProjectConfig: ProjectConfig = { + ...DEFAULT_PROJECT_CONFIG, + name: 'rome-internal-remote', + }; + const defaultVendorPath = vendorProjectConfig.files.vendorPath; + await createDirectory(defaultVendorPath, {recursive: true}); + await this.addProjectWithConfig({ + projectFolder: defaultVendorPath, + meta: DEFAULT_PROJECT_CONFIG_META, + config: vendorProjectConfig, + }); + await this.master.memoryFs.watch(defaultVendorPath, vendorProjectConfig); + } + + handleDeleted(path: AbsoluteFilePath) { + const filename = path.join(); + + this.projectConfigDependenciesToIds.delete(path); + this.fileToProject.delete(path); + + // Remove uids + const uid = this.filenameToUid.get(path); + this.filenameToUid.delete(path); + if (uid !== undefined) { + this.uidToFilename.delete(filename); + } + } + + getRemoteFromLocalPath(path: AbsoluteFilePath): undefined | URLFilePath { + return this.localPathToRemote.get(path); + } + + getFilePathFromUid(uid: string): undefined | AbsoluteFilePath { + return this.uidToFilename.get(uid); + } + + getFilePathFromUidOrAbsolute( + uid: undefined | string, + ): undefined | AbsoluteFilePath { + if (uid === undefined) { + return undefined; + } + + const uidToPath = this.getFilePathFromUid(uid); + if (uidToPath !== undefined) { + return uidToPath; + } + + const path = createAbsoluteFilePath(uid); + if (path.isAbsolute()) { + return path.assertAbsolute(); + } + + return undefined; + } + + normalizeFilenamesToFilePaths( + filenames: Iterable, + ): { + absolutes: AbsoluteFilePathSet; + others: Set; + } { + const others: Set = new Set(); + const absolutes = new AbsoluteFilePathSet(); + + for (const filename of filenames) { + if (filename === undefined) { + others.add(undefined); + continue; + } + + const absolute = this.getFilePathFromUidOrAbsolute(filename); + if (absolute === undefined) { + // Relative path + others.add(filename); + } else { + absolutes.add(absolute); + } + } + + return {absolutes, others}; + } + + setUid(path: AbsoluteFilePath, uid: string) { + const filename = path.join(); + + // Verify we didn't already generate this uid for another file + const collided = this.uidToFilename.get(uid); + if (collided !== undefined && !collided.equal(path)) { + throw new Error(`UID collision between ${filename} and ${collided}: ${uid}`); + } + + this.uidToFilename.set(uid, path); + this.filenameToUid.set(path, uid); + } + + getUid(path: AbsoluteFilePath): string { + // Allow passing in a UID + const filename = path.join(); + if (this.uidToFilename.has(filename)) { + return filename; + } + + // Check if we've already calculated and saved a UID + const existing = this.filenameToUid.get(path); + if (existing !== undefined) { + return existing; + } + + const project = this.assertProjectExisting(path); + + // Format of uids will be // + const parts: Array = []; + + let root = project.folder; + + // Push on parent package names + let targetPackagePath = path; + while (true) { + const pkg = this.master.memoryFs.getOwnedManifest(targetPackagePath); + if (pkg === undefined || pkg.folder.equal(project.folder)) { + break; + } else { + const name = manifestNameToString(pkg.manifest.name); + if (name !== undefined) { + parts.unshift(name); + + if (targetPackagePath === path) { + root = pkg.folder; + } + } + targetPackagePath = pkg.folder.getParent(); + } + } + + parts.unshift(project.config.name); + + const relative = cleanRelativeUidPath(root.relative(path)); + if (relative !== undefined) { + parts.push(relative); + } + + const uid = cleanUidParts(parts); + this.setUid(path, uid); + return uid; + } + + getFileReference(path: AbsoluteFilePath): FileReference { + const project = this.assertProjectExisting(path); + const uid = this.getUid(path); + const pkg = this.master.memoryFs.getOwnedManifest(path); + return { + uid, + project: project.id, + real: path, + manifest: pkg === undefined ? undefined : pkg.id, + relative: project.folder.relative(path).assertRelative(), + remote: this.localPathToRemote.has(path), + }; + } + + getURLFileReference(local: AbsoluteFilePath, url: URLFilePath): FileReference { + if (!this.remoteToLocalPath.has(url)) { + this.remoteToLocalPath.set(url, local); + this.localPathToRemote.set(local, url); + } + + return this.getFileReference(local); + } + + getTransportFileReference(path: AbsoluteFilePath): JSONFileReference { + const ref = this.getFileReference(path); + return { + ...ref, + relative: ref.relative.join(), + real: ref.real.join(), + }; + } + + async maybeEvictPossibleConfig(path: AbsoluteFilePath): Promise { + // TODO not sure if this case handles new manifests? + // check if this filename is a rome config dependency + const projectIds = this.projectConfigDependenciesToIds.get(path); + if (projectIds === undefined) { + return false; + } + + const projectsToEvict: Set = new Set(); + + function getAllProjects(project: ProjectDefinition) { + let children: Array = []; + for (const child of project.children) { + children = children.concat(getAllProjects(child)); + } + return [project, ...children]; + } + + for (const evictProjectId of projectIds) { + // Fetch the project + const project = this.projects.get(evictProjectId); + if (project === undefined) { + throw new Error( + `Expected project of id ${evictProjectId} since it was declared in projectConfigLocsToId`, + ); + } + + // Add all parent projects + let topProject = project; + while (topProject.parent !== undefined) { + topProject = topProject.parent; + } + for (const project of getAllProjects(topProject)) { + projectsToEvict.add(project); + } + } + + for (const project of projectsToEvict) { + await this.evictProject(project); + } + + return true; + } + + async evictProject(project: ProjectDefinition) { + const evictProjectId = project.id; + + // Remove the config locs from 'our internal map that belong to this project + for (const [configLoc, projectIds] of this.projectConfigDependenciesToIds) { + if (projectIds.has(evictProjectId)) { + projectIds.delete(evictProjectId); + } + + if (projectIds.size === 0) { + this.projectConfigDependenciesToIds.delete(configLoc); + } + } + + // Notify all workers that it should delete the project + for (const {bridge} of this.master.workerManager.getWorkers()) { + // Evict project + bridge.updateProjects.send({ + projects: [ + { + id: evictProjectId, + folder: project.folder.join(), + config: undefined, + }, + ], + }); + + // Evict packages + bridge.updateManifests.send({ + manifests: Array.from( + project.manifests.values(), + (def) => ({ + id: def.id, + manifest: undefined, + }), + ), + }); + } + + // Delete the project from 'our internal map + this.projects.delete(evictProjectId); + this.projectFolderToId.delete(project.folder); + + // Evict all files that belong to this project and delete their project mapping + const ownedFiles: Array = []; + for (const {projectId, path} of this.fileToProject.values()) { + if (evictProjectId === projectId) { + this.handleDeleted(path); + ownedFiles.push(path); + } + } + await Promise.all( + ownedFiles.map((path) => this.master.fileAllocator.evict(path)), + ); + + // Tell the MemoryFileSystem to stop watching and clear it's maps + this.master.memoryFs.unwatch(project.folder); + } + + getProjects(): Array { + return Array.from(this.projects.values()); + } + + async queueAddProject( + projectFolder: AbsoluteFilePath, + configPath: AbsoluteFilePath, + ): Promise { + // Check if we've already loaded this project + const maybeProject = this.findProjectExisting(projectFolder); + if (maybeProject !== undefined) { + return maybeProject; + } + + // If we're currently adding a project then add it to the queue + if (this.isAddingProject) { + return new Promise((resolve) => { + this.pendingAddProjects.push({projectFolder, configPath, resolve}); + }); + } + + // First time loading this project + this.isAddingProject = true; + + // fetch this project + const mainProject = await this.addProject(projectFolder, configPath); + const resolvedProjectsByDir: Map = new Map(); + resolvedProjectsByDir.set(projectFolder.join(), mainProject); + + // Resolve all pending projects that were added while we were adding the current project + const resolvedProjects: Array<{ + project: ProjectDefinition; + resolve: (project: ProjectDefinition) => void; + }> = []; + for (const {projectFolder, configPath, resolve} of this.pendingAddProjects) { + // Check if the project has already been resolved + const existing = resolvedProjectsByDir.get(projectFolder.join()); + if (existing !== undefined) { + resolvedProjects.push({project: existing, resolve}); + } else { + // It hasn't been resolved yet so let's add it + const project = await this.addProject(projectFolder, configPath); + resolvedProjects.push({project, resolve}); + } + } + + // Resolve all promises + for (const {project, resolve} of resolvedProjects) { + resolve(project); + } + + // Cleanup + this.pendingAddProjects = []; + this.isAddingProject = false; + + return mainProject; + } + + addDependencyToProjectId(path: AbsoluteFilePath, projectId: number): void { + const ids = this.projectConfigDependenciesToIds.get(path); + + if (ids === undefined) { + this.projectConfigDependenciesToIds.set(path, new Set([projectId])); + } else { + ids.add(projectId); + } + } + + findProjectConfigConsumer( + def: ProjectDefinition, + test: (consumer: Consumer) => undefined | false | Consumer, + ): ProjectConfigSource { + const meta = assertHardMeta(def.meta); + + for (const consumer of meta.consumersChain) { + const value = test(consumer); + if (value !== undefined && value !== false && value.exists()) { + return {value, consumer: meta.consumer}; + } + } + + return {value: undefined, consumer: meta.consumer}; + } + + async getVCSClient(project: ProjectDefinition): Promise { + const client = await this.maybeGetVCSClient(project); + + if (client === undefined) { + const { + value: rootConfigConsumer, + consumer, + } = this.findProjectConfigConsumer( + project, + (consumer) => consumer.has('vsc') && consumer.get('vsc').get('root'), + ); + + const rootConfigLocation: undefined | DiagnosticLocation = + rootConfigConsumer === undefined + ? undefined + : rootConfigConsumer.getDiagnosticLocation(); + + const location: DiagnosticLocation = + rootConfigLocation === undefined + ? consumer.getDiagnosticLocation() + : rootConfigLocation; + + throw createSingleDiagnosticError({ + description: descriptions.PROJECT_MANAGER.NO_VCS(rootConfigLocation), + location, + }); + } else { + return client; + } + } + + async maybeGetVCSClient( + project: ProjectDefinition, + ): Promise { + return await getVCSClient(project.config.vcs.root); + } + + async addProject( + projectFolder: AbsoluteFilePath, + configPath: AbsoluteFilePath, + ): Promise { + const {config, meta} = loadCompleteProjectConfig(projectFolder, configPath); + + return this.addProjectWithConfig({ + projectFolder, + meta, + config, + }); + } + + async addProjectWithConfig( + { + projectFolder, + meta, + config, + }: { + projectFolder: AbsoluteFilePath; + meta: ProjectConfigMeta; + config: ProjectConfig; + }, + ): Promise { + // Make sure there's no project with the same `name` as us + for (const project of this.projects.values()) { + if (project.config.name === config.name) { + throw new Error( + `Conflicting project names. ${projectFolder} and ${project.folder}`, + ); + } + } + + // Declare the project + const parentProject = this.findProjectExisting(projectFolder.getParent()); + const project: ProjectDefinition = { + config, + meta, + folder: projectFolder, + id: this.projectIdCounter++, + packages: new Map(), + manifests: new Map(), + parent: parentProject, + children: new Set(), + }; + + this.projects.set(project.id, project); + this.fileToProject.set( + projectFolder, + { + path: projectFolder, + projectId: project.id, + }, + ); + this.projectFolderToId.set(projectFolder, project.id); + + if (parentProject !== undefined) { + parentProject.children.add(project); + } + + // Add all project config dependencies so changes invalidate the whole project + if (meta.configPath !== undefined) { + this.addDependencyToProjectId(meta.configPath, project.id); + } + for (const loc of meta.configDependencies) { + this.addDependencyToProjectId(loc, project.id); + } + + // Notify other pieces of our creation + this.master.workerManager.onNewProject(project); + + // Start watching and crawl this project folder + await this.master.memoryFs.watch(projectFolder, config); + + return project; + } + + declareManifest( + project: ProjectDefinition, + isProjectPackage: boolean, + def: ManifestDefinition, + diagnostics: DiagnosticsProcessor, + ) { + const name = manifestNameToString(def.manifest.name); + + // Declare this package in all projects + const projects = this.getHierarchyFromProject(project); + + // Check for collisions + if (isProjectPackage && name !== undefined) { + for (const project of projects) { + // If there is no package then there's nothing to collide + const existingPackage = project.packages.get(name); + if (existingPackage === undefined) { + continue; + } + + diagnostics.addDiagnostic({ + description: descriptions.PROJECT_MANAGER.DUPLICATE_PACKAGE( + name, + existingPackage.path.join(), + ), + location: def.consumer.get('name').getDiagnosticLocation('inner-value'), + }); + return; + } + } + + // Set as a package + for (const project of projects) { + this.addDependencyToProjectId(def.path, project.id); + project.manifests.set(def.id, def); + + if (isProjectPackage && name !== undefined) { + project.packages.set(name, def); + } + } + } + + async notifyWorkersOfProjects( + workers: Array, + projects?: Array, + ): Promise { + if (projects === undefined) { + projects = Array.from(this.projects.values()); + } + + const manifestsSerial: WorkerPartialManifests = []; + const projectsSerial: WorkerProjects = []; + for (const project of projects) { + projectsSerial.push({ + config: serializeJSONProjectConfig(project.config), + id: project.id, + folder: project.folder.join(), + }); + + for (const def of project.manifests.values()) { + manifestsSerial.push({ + id: def.id, + manifest: this.master.memoryFs.getPartialManifest(def), + }); + } + } + + const promises = []; + + for (const worker of workers) { + promises.push(worker.bridge.updateProjects.call({projects: projectsSerial})); + promises.push( + worker.bridge.updateManifests.call({ + manifests: manifestsSerial, + }), + ); + } + + await Promise.all(promises); + } + + async assertProject( + path: AbsoluteFilePath, + location?: DiagnosticLocation, + ): Promise { + // We won't recurse up and check a parent project if we've already visited it + const syncProject = this.findProjectExisting(path); + const project = syncProject || (await this.findProject(path)); + + if (project) { + // Continue searching for projects up the directory + // We don't do this for root projects since it would be a waste, but there's no implications other than some unnecessary work if we did + if (project.config.root === false && syncProject === undefined) { + await this.findProject(project.folder.getParent()); + } + + return project; + } + + if (location === undefined) { + throw new Error( + `Couldn't find a project. Checked ${ROME_CONFIG_FILENAMES.join(' or ')} for ${path.join()}`, + ); + } + + throw createSingleDiagnosticError({ + location, + description: descriptions.PROJECT_MANAGER.NOT_FOUND, + }); + } + + // Convenience method to get the project config and pass it to the file handler class + getHandlerWithProject(path: AbsoluteFilePath): GetFileHandlerResult { + const project = this.findProjectExisting(path); + if (project === undefined) { + return {ext: '', handler: undefined}; + } else { + return getFileHandler(path, project.config); + } + } + + getHierarchyFromFilename(path: AbsoluteFilePath): Array { + const project = this.findProjectExisting(path); + if (project === undefined) { + return []; + } else { + return this.getHierarchyFromProject(project); + } + } + + getHierarchyFromProject(project: ProjectDefinition): Array { + const projects: Array = []; + + let currProject: undefined | ProjectDefinition = project; + while (currProject !== undefined) { + projects.push(currProject); + + // root projects shouldn't be considered to have any parents + if (currProject.config.root) { + break; + } + + currProject = project.parent; + } + + return projects; + } + + assertProjectExisting(path: AbsoluteFilePath): ProjectDefinition { + const project = this.findProjectExisting(path); + if (project === undefined) { + throw new Error(`Expected existing project for ${path.join()}`); + } + return project; + } + + findProjectExisting(cwd: AbsoluteFilePath): undefined | ProjectDefinition { + const tried: Array = []; + + for (const dir of cwd.getChain()) { + const cached = this.fileToProject.get(dir); + if (cached === undefined) { + tried.push(dir); + } else { + for (const dir of tried) { + this.fileToProject.set(dir, cached); + } + + const project = this.projects.get(cached.projectId); + if (project === undefined) { + throw new Error('Expected project from project id found in fileToProject'); + } + return project; + } + } + + return undefined; + } + + async findProject( + cwd: AbsoluteFilePath, + ): Promise { + // Check if we have an existing project + const syncProject = this.findProjectExisting(cwd); + if (syncProject !== undefined) { + return syncProject; + } + + const parentDirectories = cwd.getChain(); + + // If not then let's access the file system and try to find one + for (const dir of parentDirectories) { + // Check for dedicated project configs + for (const configFilename of ROME_CONFIG_FILENAMES) { + // Check in root + const configPath = dir.append(configFilename); + + const hasProject = await this.master.memoryFs.existsHard(configPath); + if (hasProject) { + return this.queueAddProject(dir, configPath); + } + } + + // Check for package.json + const packagePath = dir.append('package.json'); + if (await this.master.memoryFs.existsHard(packagePath)) { + const input = await readFileText(packagePath); + const json = await consumeJSON({input, path: packagePath}); + if (json.has(ROME_CONFIG_PACKAGE_JSON_FIELD)) { + return this.queueAddProject(dir, packagePath); + } + } + } + + // If we didn't find a project config then check for incorrect config filenames + for (const dir of parentDirectories) { + for (const basename of ROME_CONFIG_WARN_FILENAMES) { + const path = dir.append(basename); + + if (await this.master.memoryFs.existsHard(path)) { + this.warnIncorrectConfigFile( + path, + DiagnosticsProcessor.createImmediateThrower([ + { + category: 'project-manager', + message: 'Find project', + }, + ]), + ); + } + } + } + + return undefined; + } + + checkConfigFile(path: AbsoluteFilePath, diagnostics: DiagnosticsProcessor) { + if (ROME_CONFIG_WARN_FILENAMES.includes(path.getBasename())) { + this.warnIncorrectConfigFile(path, diagnostics); + } + } + + warnIncorrectConfigFile( + path: AbsoluteFilePath, + diagnostics: DiagnosticsProcessor, + ) { + diagnostics.addDiagnostic({ + description: descriptions.PROJECT_MANAGER.INCORRECT_CONFIG_FILENAME( + ROME_CONFIG_FILENAMES, + ), + location: { + filename: path.join(), + }, + }); + } } diff --git a/packages/@romejs/core/master/testing/TestMasterRunner.ts b/packages/@romejs/core/master/testing/TestMasterRunner.ts index add62100e4e..22203a4c769 100644 --- a/packages/@romejs/core/master/testing/TestMasterRunner.ts +++ b/packages/@romejs/core/master/testing/TestMasterRunner.ts @@ -7,14 +7,14 @@ import {Reporter, ReporterProgress} from '@romejs/cli-reporter'; import { - Diagnostic, - DiagnosticsError, - createBlessedDiagnosticMessage, - deriveDiagnosticFromError, - deriveDiagnosticFromErrorStructure, - descriptions, - diagnosticLocationToMarkupFilelink, - getDiagnosticsFromError, + Diagnostic, + DiagnosticsError, + createBlessedDiagnosticMessage, + deriveDiagnosticFromError, + deriveDiagnosticFromErrorStructure, + descriptions, + diagnosticLocationToMarkupFilelink, + getDiagnosticsFromError, } from '@romejs/diagnostics'; import {TestRef} from '../../common/bridges/TestWorkerBridge'; import {Master, MasterRequest, TestWorkerBridge} from '@romejs/core'; @@ -22,30 +22,29 @@ import {DiagnosticsPrinter} from '@romejs/cli-diagnostics'; import {createClient} from '@romejs/codec-websocket'; import {humanizeNumber} from '@romejs/string-utils'; import { - Bridge, - BridgeError, - createBridgeFromChildProcess, + Bridge, + BridgeError, + createBridgeFromChildProcess, } from '@romejs/events'; import { - CoverageCollector, - ErrorFrame, - InspectorClient, - InspectorClientCloseError, - sourceMapManager, - urlToFilename, + CoverageCollector, + ErrorFrame, + InspectorClient, + InspectorClientCloseError, + urlToFilename, } from '@romejs/v8'; import fork from '../../common/utils/fork'; import {ManifestDefinition} from '@romejs/codec-js-manifest'; import {AbsoluteFilePath} from '@romejs/path'; import {ob1Coerce0To1} from '@romejs/ob1'; import { - CoverageFolder, - TestMasterRunnerConstructorOptions, - TestMasterRunnerOptions, - TestSource, - TestSources, - TestWorkerContainer, - TestWorkerContainers, + CoverageFolder, + TestMasterRunnerConstructorOptions, + TestMasterRunnerOptions, + TestSource, + TestSources, + TestWorkerContainer, + TestWorkerContainers, } from './types'; import {formatPercent, percentInsideCoverageFolder, sortMapKeys} from './utils'; import {escapeMarkup, markup, safeMarkup} from '@romejs/string-markup'; @@ -53,972 +52,963 @@ import {MAX_WORKER_COUNT} from '@romejs/core/common/constants'; import {TestWorkerFlags} from '@romejs/core/test-worker/TestWorker'; import net = require('net'); import { - FocusedTest, - TestWorkerFileResult, + FocusedTest, + TestWorkerFileResult, } from '@romejs/core/test-worker/TestWorkerRunner'; import {FileReference} from '@romejs/core/common/types/files'; +import {SourceMapConsumerCollection} from '@romejs/codec-source-map'; class BridgeDiagnosticsError extends DiagnosticsError { - constructor(diag: Diagnostic, bridge: Bridge) { - super(diag.description.message.value, [diag]); - this.bridge = bridge; - } + constructor(diag: Diagnostic, bridge: Bridge) { + super(diag.description.message.value, [diag]); + this.bridge = bridge; + } - bridge: Bridge; + bridge: Bridge; } function grammarNumberTests(num: number): string { - return `${num}`; + return `${num}`; } function getProgressTestRefText(ref: TestRef) { - return markup`: ${escapeMarkup( - ref.testName, - )}`; + return markup`: ${escapeMarkup( + ref.testName, + )}`; } function findAvailablePort(): Promise { - return new Promise((resolve, reject) => { - // When you create a server without specifying a port then the OS will choose a port number for you! - const server = net.createServer(); - server.unref(); - server.on('error', reject); - server.listen( - undefined, - () => { - const address = server.address(); - if (address == null || typeof address === 'string') { - throw new Error('Invalid address value'); - } - - server.close(() => { - resolve(address.port); - }); - }, - ); - }); + return new Promise((resolve, reject) => { + // When you create a server without specifying a port then the OS will choose a port number for you! + const server = net.createServer(); + server.unref(); + server.on('error', reject); + server.listen( + undefined, + () => { + const address = server.address(); + if (address == null || typeof address === 'string') { + throw new Error('Invalid address value'); + } + + server.close(() => { + resolve(address.port); + }); + }, + ); + }); } type TestProgress = { - teardown: () => void; + teardown: () => void; }; export default class TestMasterRunner { - constructor(opts: TestMasterRunnerConstructorOptions) { - this.sources = opts.sources; - this.reporter = opts.request.reporter; - this.master = opts.request.master; - this.cwd = opts.request.client.flags.cwd; - this.request = opts.request; - this.options = opts.options; - - this.ignoreBridgeEndError = new Set(); - - this.sourcesQueue = Array.from(opts.sources.values()); - - this.coverageCollector = new CoverageCollector(); - - this.progress = { - totalTests: 0, - startedTests: 0, - finishedTests: 0, - updatedSnapshots: 0, - deletedSnapshots: 0, - createdSnapshots: 0, - updatedInlineSnapshots: 0, - }; - - this.focusedTests = []; - - this.runningTests = new Map(); - this.testFileCounter = 0; - - this.printer = opts.request.createDiagnosticsPrinter( - this.request.createDiagnosticsProcessor({ - origins: [ - { - category: 'test', - message: 'Run initiated', - }, - ], - }), - ); - this.printer.processor.addDiagnostics(opts.addDiagnostics); - - // Add source maps - for (const [filename, {code, sourceMap}] of opts.sources) { - const consumer = sourceMap.toConsumer(); - this.coverageCollector.addSourceMap(filename, code, consumer); - this.printer.processor.sourceMaps.add(filename, consumer); - } - } - - coverageCollector: CoverageCollector; - options: TestMasterRunnerOptions; - request: MasterRequest; - reporter: Reporter; - sources: TestSources; - workers: undefined | TestWorkerContainers; - master: Master; - cwd: AbsoluteFilePath; - printer: DiagnosticsPrinter; - sourcesQueue: Array; - testFileCounter: number; - ignoreBridgeEndError: Set; - - runningTests: Map< - string, - { - ref: TestRef; - timeout: undefined | NodeJS.Timeout; - } - >; - - progress: { - totalTests: number; - startedTests: number; - finishedTests: number; - updatedInlineSnapshots: number; - updatedSnapshots: number; - deletedSnapshots: number; - createdSnapshots: number; - }; - - focusedTests: Array; - - async processTestResult( - ref: FileReference, - {inlineSnapshotUpdates, snapshotCounts}: TestWorkerFileResult, - ): Promise { - this.progress.createdSnapshots += snapshotCounts.created; - this.progress.updatedSnapshots += snapshotCounts.updated; - this.progress.deletedSnapshots += snapshotCounts.deleted; - this.progress.updatedInlineSnapshots += inlineSnapshotUpdates.length; - - if (inlineSnapshotUpdates.length > 0) { - const path = ref.real; - const filename = path.join(); - - // Resolve source maps. These will originally be pointed to the compiled source. - inlineSnapshotUpdates = inlineSnapshotUpdates.map((update) => { - const resolved = sourceMapManager.resolveLocation( - filename, - update.line, - update.column, - ); - - if (resolved.filename !== filename && resolved.filename !== ref.uid) { - throw new Error( - `Inline snapshot update resolved to ${resolved.filename} when it should be ${filename}`, - ); - } - - return { - ...update, - line: resolved.line, - column: resolved.column, - }; - }); - - const diagnostics = await this.request.requestWorkerUpdateInlineSnapshots( - path, - inlineSnapshotUpdates, - {}, - ); - this.printer.processor.addDiagnostics(diagnostics); - } - } - - async prepareWorker( - {bridge, process, inspector}: TestWorkerContainer, - progress: ReporterProgress, - ): Promise<() => Promise> { - const {options: opts, sourcesQueue} = this; - const req = this.request; - const {flags} = req.client; - - if (inspector !== undefined && opts.coverage === true) { - await inspector.call('Profiler.enable'); - await inspector.call( - 'Profiler.startPreciseCoverage', - { - // Turning this on disables V8 optimizations https://v8.dev/blog/javascript-code-coverage#precise-coverage-(function-granularity) - callCount: false, - // Otherwise coverage will only have function granularity - detailed: true, - }, - ); - } - - const tests: Array<{ - ref: FileReference; - id: number; - }> = []; - - const prepareTest = async () => { - if (sourcesQueue.length === 0) { - return; - } - - const item = sourcesQueue.pop()!; - const {ref, code} = item; - - const id = this.testFileCounter; - this.testFileCounter++; - - const progressText = ``; - progress.pushText(progressText); - - try { - const {focusedTests} = await bridge.prepareTest.call({ - id, - options: opts, - projectFolder: req.master.projectManager.assertProjectExisting( - ref.real, - ).folder.join(), - file: req.master.projectManager.getTransportFileReference(ref.real), - cwd: flags.cwd.join(), - code, - }); - - this.focusedTests = this.focusedTests.concat(focusedTests); - - tests.push({id, ref}); - } catch (err) { - this.handlePossibleBridgeError(err); - } - - progress.popText(progressText); - progress.tick(); - - await prepareTest(); - }; - - await prepareTest(); - - return async () => { - try { - for (const {ref, id} of tests) { - const result = await bridge.runTest.call({ - id, - onlyFocusedTests: this.focusedTests.length > 0, - }); - await this.processTestResult(ref, result); - } - } catch (err) { - this.handlePossibleBridgeError(err); - } finally { - if (inspector !== undefined) { - if (opts.coverage) { - if (inspector.alive) { - const profile = await inspector.call( - 'Profiler.takePreciseCoverage', - ); - this.coverageCollector.addCoverage(profile.get('result').asAny()); - - // Not really necessary but let's clean up anyway for completeness - await inspector.call('Profiler.stopPreciseCoverage'); - await inspector.call('Profiler.disable'); - } else { - // TODO log that we failed to fetch some coverage - } - } - - inspector.end(); - } - - process.kill(); - } - }; - } - - handlePossibleBridgeError(err: Error) { - let diagnostics = getDiagnosticsFromError(err); - let bridge: undefined | Bridge; - - if (err instanceof BridgeDiagnosticsError) { - bridge = err.bridge; - } - - if (err instanceof BridgeError) { - bridge = err.bridge; - diagnostics = [ - deriveDiagnosticFromError( - err, - { - description: { - category: 'tests/failure', - }, - }, - ), - ]; - } - - if (diagnostics === undefined || bridge === undefined) { - throw err; - } else { - if (!this.ignoreBridgeEndError.has(bridge)) { - this.printer.processor.addDiagnostics(diagnostics); - } - } - } - - async spawnWorker(flags: TestWorkerFlags): Promise { - const proc = fork( - 'test-worker', - { - stdio: 'pipe', - }, - ['--inspector-port', String(flags.inspectorPort)], - ); - - const {stdout, stderr} = proc; - if (stdout == null || stderr == null) { - throw new Error('stdout or stderr was undefined for a spawned Worker'); - } - - stdout.on( - 'data', - (chunk) => { - process.stdout.write(chunk); - }, - ); - - // Suppress any debugger logs - stderr.on( - 'data', - (chunk) => { - const str = chunk.toString(); - - if (str.startsWith('Debugger listening on ws://')) { - return; - } - - if ( - str.startsWith('For help, see: https://nodejs.org/en/docs/inspector') - ) { - return; - } - - if (str.startsWith('Debugger attached')) { - return; - } - - process.stderr.write(chunk); - }, - ); - - const bridge = createBridgeFromChildProcess( - TestWorkerBridge, - proc, - { - type: 'client', - }, - ); - await bridge.handshake(); - - const {inspectorUrl} = await bridge.inspectorDetails.call(); - - let inspector: undefined | InspectorClient; - if (inspectorUrl !== undefined) { - const client = new InspectorClient(await createClient(inspectorUrl)); - inspector = client; - await client.call('Debugger.enable'); - - bridge.endEvent.subscribe(() => { - client.end(); - }); - } - - bridge.testsFound.subscribe((tests) => { - for (const ref of tests) { - this.onTestFound(ref); - } - }); - - bridge.testDiagnostic.subscribe(({diagnostic, origin}) => { - this.printer.processor.addDiagnostic(diagnostic, origin); - }); - - return { - bridge, - process: proc, - inspector, - }; - } - - async setupWorkers(): Promise { - // TODO some smarter logic. we may not need all these workers - const containerPromises: Array> = []; - for (let i = 0; i < MAX_WORKER_COUNT; i++) { - const inspectorPort = await findAvailablePort(); - containerPromises.push(this.spawnWorker({inspectorPort})); - } - - const containers: TestWorkerContainers = await Promise.all( - containerPromises, - ); - - // Every 5 seconds, ping the worker and wait a max of 5 seconds, if we receive no response then consider the worker dead - for (const container of containers) { - container.bridge.monitorHeartbeat( - 5_000, - async () => { - this.handleWorkerTimeout('10 seconds', container); - }, - ); - } - - return containers; - } - - async init() { - this.workers = await this.setupWorkers(); - - const workerContainers: TestWorkerContainers = this.getWorkers(); - - // Prepare all tests - const progress = this.reporter.progress({ - title: 'Preparing', - }); - progress.setTotal(this.sourcesQueue.length); - const callbacks = await Promise.all( - workerContainers.map((container) => - this.prepareWorker(container, progress) - ), - ); - progress.end(); - - // Run tests - const runProgress = this.setupRunProgress(); - await Promise.all(callbacks.map((callback) => callback())); - runProgress.teardown(); - - this.throwPrinter(); - } - - async handleWorkerTimeout( - duration: string, - container: TestWorkerContainer, - ): Promise { - return new Promise((resolve, reject) => { - const timeout = setTimeout( - () => { - resolve( - container.bridge.end( - `Test worker was unresponsive for ${duration}. We tried to collect some additional metadata but we timed out again trying to fetch it...`, - ), - ); - }, - 3_000, - ); - - this._handleWorkerTimeout(duration, container).then(() => { - clearTimeout(timeout); - resolve(); - }).catch((err) => { - clearTimeout(timeout); - if (err instanceof InspectorClientCloseError) { - return container.bridge.end( - `Test worker was unresponsive for ${duration}. We tried to collect some additional metadata but the inspector connection closed abruptly`, - ); - } else { - reject(err); - } - }); - }); - } - - async _handleWorkerTimeout( - duration: string, - {bridge, inspector}: TestWorkerContainer, - ): Promise { - if (inspector === undefined) { - bridge.end( - `Test worker was unresponsive for ${duration}. There was no inspector connected so we were unable to capture stack frames before it was terminated.`, - ); - return undefined; - } - - inspector.call('Debugger.pause'); - - const params = await inspector.wait('Debugger.paused'); - - const frames: Array = []; - - const callFrames = params.get('callFrames').asArray().slice(0, 20); - for (const callFrame of callFrames) { - const loc = callFrame.get('location'); - - const resolved = sourceMapManager.resolveLocation( - urlToFilename(callFrame.get('url').asString()), - ob1Coerce0To1(loc.get('lineNumber').asZeroIndexedNumber()), - loc.get('columnNumber').asZeroIndexedNumber(), - ); - - const name = callFrame.get('scopeChain').asArray()[0].get('name').asString( - '', - ).split('$').pop(); - - frames.push({ - resolvedLocation: resolved.found, - typeName: undefined, - functionName: name, - methodName: undefined, - filename: resolved.filename, - lineNumber: resolved.line, - columnNumber: resolved.column, - isTopLevel: false, - isEval: false, - isNative: false, - isConstructor: false, - isAsync: false, - }); - } - - bridge.endWithError( - new BridgeDiagnosticsError( - deriveDiagnosticFromErrorStructure( - { - name: 'Error', - frames, - }, - { - description: { - category: 'tests/timeout', - message: createBlessedDiagnosticMessage( - `Test worker was unresponsive for ${duration}. Possible infinite loop. Below is a stack trace before the test was terminated.`, - ), - advice: [ - { - type: 'log', - category: 'info', - text: `You can find the specific test that caused this by running rome test --sync-tests`, - }, - ], - }, - }, - ), - bridge, - ), - ); - } - - getWorkers(): TestWorkerContainers { - if (this.workers === undefined) { - throw new Error('TestMasterRunner.init has not been called yet'); - } else { - return this.workers; - } - } - - refToKey(ref: TestRef): string { - return `${ref.filename}: ${ref.testName}`; - } - - getTotalTests(): number { - if (this.focusedTests.length > 0) { - return this.focusedTests.length; - } else { - return this.progress.totalTests; - } - } - - onTestStart( - container: TestWorkerContainer, - ref: TestRef, - timeoutMs: undefined | number, - ) { - this.progress.startedTests++; - - let timeout = undefined; - if (timeoutMs !== undefined) { - timeout = setTimeout( - () => { - // TODO This will kill the whole worker, maybe it's possible to just terminate the current test? Throw an error, see if the next test was ran, or else terminate completely - this.handleWorkerTimeout(`${String(timeoutMs)}ms`, container); - }, - timeoutMs, - ); - } - - this.runningTests.set( - this.refToKey(ref), - { - ref, - timeout, - }, - ); - } - - onTestFound(data: TestRef) { - data; - this.progress.totalTests++; - } - - onTestFinished(ref: TestRef) { - const key = this.refToKey(ref); - const running = this.runningTests.get(key); - if (running === undefined) { - throw new Error('Expected there to be a running test'); - } - - if (running.timeout !== undefined) { - clearTimeout(running.timeout); - } - this.runningTests.delete(key); - - this.progress.finishedTests++; - } - - setupRunProgress(): TestProgress { - const workers = this.getWorkers(); - - const progress = this.request.reporter.progress({ - persistent: true, - title: 'Running', - }); - progress.setTotal(this.getTotalTests()); - - for (let i = 0; i < workers.length; i++) { - const container = workers[i]; - const {bridge} = container; - - const ourRunningTests: Set = new Set(); - - bridge.endEvent.subscribe((error) => { - // Cancel all currently running tests - const cancelTests: Array = []; - - for (const key of ourRunningTests) { - const test = this.runningTests.get(key); - if (test !== undefined) { - cancelTests.push(test.ref); - } - } - - for (const ref of cancelTests) { - this.onTestFinished(ref); - - if (cancelTests.length === 1) { - // If we only have one test to cancel then let's only point the bridge error to this test - this.ignoreBridgeEndError.add(bridge); - - const errDiag = deriveDiagnosticFromError( - error, - { - label: ref.testName, - filename: ref.filename, - description: { - category: 'tests/failure', - }, - }, - ); - - this.printer.processor.addDiagnostic({ - ...errDiag, - description: { - ...errDiag.description, - // We don't care about the advice - advice: [], - }, - }); - } else { - this.printer.processor.addDiagnostic({ - label: ref.testName, - description: descriptions.TESTS.CANCELLED, - location: { - filename: ref.filename, - }, - }); - } - } - }); - - bridge.testStart.subscribe((data) => { - ourRunningTests.add(this.refToKey(data.ref)); - this.onTestStart(container, data.ref, data.timeout); - progress.pushText(getProgressTestRefText(data.ref)); - }); - - bridge.testFinish.subscribe((data) => { - this.onTestFinished(data.ref); - progress.popText(getProgressTestRefText(data.ref)); - progress.tick(); - }); - } - - return { - teardown() { - progress.end(); - }, - }; - } - - printCoverageReport(isError: boolean) { - const {reporter, master, coverageCollector} = this; - - if (isError && this.options.showAllCoverage) { - // Only show coverage for errors when --show-all-coverage has been specified - return; - } - - if (!this.options.coverage) { - return; - } - - reporter.info('Generating coverage'); - - // Fetch coverage entries - const files = coverageCollector.generate(); - if (files.length === 0) { - return; - } - - reporter.heading('Code coverage'); - - // Get the packages associated with all the ran tests, we will filter code coverage to those packages only - const testedPackages: Set = new Set(); - for (const {ref} of this.sources.values()) { - testedPackages.add(master.memoryFs.getOwnedManifest(ref.real)); - } - - let root: CoverageFolder = { - name: undefined, - folders: new Map(), - files: new Map(), - }; - - let totalFiles = 0; - - // Turn the flat list of filenames into a directory tree - for (const file of files) { - const {filename} = file; - - // Get the absolute filename - const absolute = master.projectManager.getFilePathFromUid(filename); - if (absolute === undefined) { - continue; - } - - // Filter out untested packages - const pkg = master.memoryFs.getOwnedManifest(absolute); - if (testedPackages.has(pkg) === false) { - continue; - } - - // TODO maybe filter out test files too? - - // Track unfiltered files - totalFiles++; - - const filenameParts = filename.split('/'); - const basename = filenameParts.pop(); - if (basename === undefined) { - throw new Error('Should always be at least one element from a split()'); - } - - let target: CoverageFolder = root; - - for (const part of filenameParts) { - const existingFolder = target.folders.get(part); - if (existingFolder === undefined) { - const newFolder = { - name: part, - folders: new Map(), - files: new Map(), - }; - target.folders.set(part, newFolder); - target = newFolder; - } else { - target = existingFolder; - } - } - - target.files.set(basename, file); - } - - // Continuously merge all entries with only a single folder from the root - while (root.folders.size === 1 && root.files.size === 0) { - // Awkward way to get the first value out of the folders map... - const newRoot = root.folders.values().next().value; - root = { - ...newRoot, - name: root.name !== undefined && newRoot.name !== undefined - ? `${root.name}/${newRoot.name}` - : newRoot.name, - }; - } - - const rows: Array> = []; - - // If there's more than 15 files to show, and we don't have the explicit showAllCoverage flag - - // then truncate the output - const showAllCoverage = this.options.showAllCoverage || totalFiles < 15; - - function buildRows(folder: CoverageFolder, depth: number) { - const name = folder.name === undefined ? 'All files' : `${folder.name}/`; - const folderPercent = percentInsideCoverageFolder(folder); - - rows.push([ - ' '.repeat(depth) + `${name}`, - formatPercent(folderPercent.functions), - formatPercent(folderPercent.branches), - formatPercent(folderPercent.lines), - ]); - - // Don't ever show anything deeper than a single level when showAllCoverage is off - if (!showAllCoverage && depth > 0) { - return; - } - - const fileIndent = ' '.repeat(depth + 1); - for (const [name, file] of sortMapKeys(folder.files)) { - let absolute = file.filename; - - // Exchange any UIDs - const absolutePath = master.projectManager.getFilePathFromUid( - file.filename, - ); - if (absolutePath !== undefined) { - absolute = absolutePath.join(); - } - - rows.push([ - fileIndent + markup`${name}`, - formatPercent(file.functions.percent), - formatPercent(file.branches.percent), - formatPercent(file.lines.percent), - ]); - } - - for (const subFolder of sortMapKeys(folder.folders).values()) { - buildRows(subFolder, depth + 1); - } - } - - buildRows(root, 0); - - reporter.table(['File', '% Functions', '% Branches', '% Lines'], rows); - - if (!showAllCoverage) { - reporter.br(); - reporter.info( - 'Additional coverage information available. Refine the executed tests or add the --show-all-coverage flag', - ); - } - - reporter.hr(); - } - - getSourceCode(filename: string): undefined | string { - const testSource = this.sources.get(filename); - if (testSource === undefined) { - return undefined; - } else { - return testSource.code; - } - } - - printFocusedTestWarning(reporter: Reporter) { - const {focusedTests} = this; - if (focusedTests.length === 0) { - return; - } - - const formattedFocusedTests = focusedTests.map(({testName, location}) => { - const loc = this.printer.processor.normalizer.normalizeLocation(location); - - return markup`${testName} at ${safeMarkup( - diagnosticLocationToMarkupFilelink(loc), - )}`; - }); - - if (focusedTests.length === 1) { - reporter.warn(`Only ran the focused test ${formattedFocusedTests[0]}`); - } else { - reporter.warn( - `Only ran the following ${focusedTests.length} focused ${grammarNumberTests( - focusedTests.length, - )}`, - ); - reporter.list(formattedFocusedTests); - } - - const otherTotal = this.progress.totalTests - this.focusedTests.length; - reporter.info( - `${otherTotal} other ${grammarNumberTests( - otherTotal, - )} ignored`, - ); - } - - printSnapshotCounts(reporter: Reporter) { - const { - createdSnapshots, - deletedSnapshots, - updatedSnapshots, - updatedInlineSnapshots, - } = this.progress; - - const snapshotCounts: Array<{ - inline: boolean; - count: number; - noun: string; - }> = [ - {inline: false, count: createdSnapshots, noun: 'created'}, - {inline: false, count: updatedSnapshots, noun: 'updated'}, - {inline: false, count: deletedSnapshots, noun: 'deleted'}, - {inline: true, count: updatedInlineSnapshots, noun: 'updated'}, - ].filter(({count}) => count > 0); - - if (snapshotCounts.length === 0) { - return; - } - - const first = snapshotCounts.shift()!; - const parts = [ - // Inline snapshots will always be the last element, so if it's inline here then there's no others - `${first.count} ${first.inline - ? 'inline snapshots' - : 'snapshots'} ${first.noun}`, - ]; - - for (let {inline, count, noun} of snapshotCounts) { - if (inline) { - noun = `inline ${noun}`; - } - parts.push(`${count} ${noun}`); - } - - reporter.success(parts.join(', ')); - } - - throwPrinter() { - const {printer} = this; - - printer.onFooterPrint((reporter, isError) => { - this.printCoverageReport(isError); - this.printSnapshotCounts(reporter); - this.printFocusedTestWarning(reporter); - - if (!isError) { - // Don't say "all" when we have focused tests - let prefix = this.focusedTests.length === 0 ? 'All ' : ''; - const totalCount = this.getTotalTests(); - reporter.success( - `${prefix}${humanizeNumber(totalCount)} ${grammarNumberTests( - totalCount, - )} passed!`, - ); - return true; - } - - // Show default footer - return false; - }); - - throw printer; - } + constructor(opts: TestMasterRunnerConstructorOptions) { + this.sources = opts.sources; + this.reporter = opts.request.reporter; + this.master = opts.request.master; + this.cwd = opts.request.client.flags.cwd; + this.request = opts.request; + this.options = opts.options; + + this.ignoreBridgeEndError = new Set(); + + this.sourcesQueue = Array.from(opts.sources.values()); + + this.coverageCollector = new CoverageCollector(); + + this.progress = { + totalTests: 0, + startedTests: 0, + finishedTests: 0, + updatedSnapshots: 0, + deletedSnapshots: 0, + createdSnapshots: 0, + updatedInlineSnapshots: 0, + }; + + this.focusedTests = []; + + this.runningTests = new Map(); + this.testFileCounter = 0; + + this.printer = opts.request.createDiagnosticsPrinter( + this.request.createDiagnosticsProcessor({ + origins: [ + { + category: 'test', + message: 'Run initiated', + }, + ], + }), + ); + this.printer.processor.addDiagnostics(opts.addDiagnostics); + this.sourceMaps = this.printer.processor.sourceMaps; + + // Add source maps + for (const [filename, {code, sourceMap}] of opts.sources) { + const consumer = sourceMap.toConsumer(); + this.coverageCollector.addSourceMap(filename, code, consumer); + this.printer.processor.sourceMaps.add(filename, consumer); + } + } + + coverageCollector: CoverageCollector; + sourceMaps: SourceMapConsumerCollection; + options: TestMasterRunnerOptions; + request: MasterRequest; + reporter: Reporter; + sources: TestSources; + workers: undefined | TestWorkerContainers; + master: Master; + cwd: AbsoluteFilePath; + printer: DiagnosticsPrinter; + sourcesQueue: Array; + testFileCounter: number; + ignoreBridgeEndError: Set; + + runningTests: Map< + string, + { + ref: TestRef; + timeout: undefined | NodeJS.Timeout; + } + >; + + progress: { + totalTests: number; + startedTests: number; + finishedTests: number; + updatedInlineSnapshots: number; + updatedSnapshots: number; + deletedSnapshots: number; + createdSnapshots: number; + }; + + focusedTests: Array; + + async processTestResult( + ref: FileReference, + {inlineSnapshotUpdates, snapshotCounts}: TestWorkerFileResult, + ): Promise { + this.progress.createdSnapshots += snapshotCounts.created; + this.progress.updatedSnapshots += snapshotCounts.updated; + this.progress.deletedSnapshots += snapshotCounts.deleted; + this.progress.updatedInlineSnapshots += inlineSnapshotUpdates.length; + + if (inlineSnapshotUpdates.length > 0) { + const path = ref.real; + const filename = path.join(); + + // Resolve source maps. These will originally be pointed to the compiled source. + inlineSnapshotUpdates = inlineSnapshotUpdates.map((update) => { + const resolved = this.sourceMaps.assertApproxOriginalPositionFor( + filename, + update.line, + update.column, + ); + + if (resolved.source !== filename && resolved.source !== ref.uid) { + throw new Error( + `Inline snapshot update resolved to ${resolved.source} when it should be ${filename}`, + ); + } + + return { + ...update, + line: resolved.line, + column: resolved.column, + }; + }); + + const diagnostics = await this.request.requestWorkerUpdateInlineSnapshots( + path, + inlineSnapshotUpdates, + {}, + ); + this.printer.processor.addDiagnostics(diagnostics); + } + } + + async prepareWorker( + {bridge, process, inspector}: TestWorkerContainer, + progress: ReporterProgress, + ): Promise<() => Promise> { + const {options: opts, sourcesQueue} = this; + const req = this.request; + const {flags} = req.client; + + if (inspector !== undefined && opts.coverage === true) { + await inspector.call('Profiler.enable'); + await inspector.call( + 'Profiler.startPreciseCoverage', + { + // Turning this on disables V8 optimizations https://v8.dev/blog/javascript-code-coverage#precise-coverage-(function-granularity) + callCount: false, + // Otherwise coverage will only have function granularity + detailed: true, + }, + ); + } + + const tests: Array<{ + ref: FileReference; + id: number; + }> = []; + + const prepareTest = async () => { + if (sourcesQueue.length === 0) { + return; + } + + const item = sourcesQueue.pop()!; + const {ref, code} = item; + + const id = this.testFileCounter; + this.testFileCounter++; + + const progressText = ``; + progress.pushText(progressText); + + try { + const {focusedTests} = await bridge.prepareTest.call({ + id, + options: opts, + projectFolder: req.master.projectManager.assertProjectExisting(ref.real).folder.join(), + file: req.master.projectManager.getTransportFileReference(ref.real), + cwd: flags.cwd.join(), + code, + }); + + this.focusedTests = this.focusedTests.concat(focusedTests); + + tests.push({id, ref}); + } catch (err) { + this.handlePossibleBridgeError(err); + } + + progress.popText(progressText); + progress.tick(); + + await prepareTest(); + }; + + await prepareTest(); + + return async () => { + try { + for (const {ref, id} of tests) { + const result = await bridge.runTest.call({ + id, + onlyFocusedTests: this.focusedTests.length > 0, + }); + await this.processTestResult(ref, result); + } + } catch (err) { + this.handlePossibleBridgeError(err); + } finally { + if (inspector !== undefined) { + if (opts.coverage) { + if (inspector.alive) { + const profile = await inspector.call('Profiler.takePreciseCoverage'); + this.coverageCollector.addCoverage(profile.get('result').asAny()); + + // Not really necessary but let's clean up anyway for completeness + await inspector.call('Profiler.stopPreciseCoverage'); + await inspector.call('Profiler.disable'); + } else { + // TODO log that we failed to fetch some coverage + } + } + + inspector.end(); + } + + process.kill(); + } + }; + } + + handlePossibleBridgeError(err: Error) { + let diagnostics = getDiagnosticsFromError(err); + let bridge: undefined | Bridge; + + if (err instanceof BridgeDiagnosticsError) { + bridge = err.bridge; + } + + if (err instanceof BridgeError) { + bridge = err.bridge; + diagnostics = [ + deriveDiagnosticFromError( + err, + { + description: { + category: 'tests/failure', + }, + }, + ), + ]; + } + + if (diagnostics === undefined || bridge === undefined) { + throw err; + } else { + if (!this.ignoreBridgeEndError.has(bridge)) { + this.printer.processor.addDiagnostics(diagnostics); + } + } + } + + async spawnWorker(flags: TestWorkerFlags): Promise { + const proc = fork( + 'test-worker', + { + stdio: 'pipe', + }, + ['--inspector-port', String(flags.inspectorPort)], + ); + + const {stdout, stderr} = proc; + if (stdout == null || stderr == null) { + throw new Error('stdout or stderr was undefined for a spawned Worker'); + } + + stdout.on( + 'data', + (chunk) => { + process.stdout.write(chunk); + }, + ); + + // Suppress any debugger logs + stderr.on( + 'data', + (chunk) => { + const str = chunk.toString(); + + if (str.startsWith('Debugger listening on ws://')) { + return; + } + + if (str.startsWith('For help, see: https://nodejs.org/en/docs/inspector')) { + return; + } + + if (str.startsWith('Debugger attached')) { + return; + } + + process.stderr.write(chunk); + }, + ); + + const bridge = createBridgeFromChildProcess( + TestWorkerBridge, + proc, + { + type: 'client', + }, + ); + await bridge.handshake(); + + const {inspectorUrl} = await bridge.inspectorDetails.call(); + + let inspector: undefined | InspectorClient; + if (inspectorUrl !== undefined) { + const client = new InspectorClient(await createClient(inspectorUrl)); + inspector = client; + await client.call('Debugger.enable'); + + bridge.endEvent.subscribe(() => { + client.end(); + }); + } + + bridge.testsFound.subscribe((tests) => { + for (const ref of tests) { + this.onTestFound(ref); + } + }); + + bridge.testDiagnostic.subscribe(({diagnostic, origin}) => { + this.printer.processor.addDiagnostic(diagnostic, origin); + }); + + return { + bridge, + process: proc, + inspector, + }; + } + + async setupWorkers(): Promise { + // TODO some smarter logic. we may not need all these workers + const containerPromises: Array> = []; + for (let i = 0; i < MAX_WORKER_COUNT; i++) { + const inspectorPort = await findAvailablePort(); + containerPromises.push(this.spawnWorker({inspectorPort})); + } + + const containers: TestWorkerContainers = await Promise.all(containerPromises); + + // Every 5 seconds, ping the worker and wait a max of 5 seconds, if we receive no response then consider the worker dead + for (const container of containers) { + container.bridge.monitorHeartbeat( + 5_000, + async () => { + this.handleWorkerTimeout('10 seconds', container); + }, + ); + } + + return containers; + } + + async init() { + this.workers = await this.setupWorkers(); + + const workerContainers: TestWorkerContainers = this.getWorkers(); + + // Prepare all tests + const progress = this.reporter.progress({ + title: 'Preparing', + }); + progress.setTotal(this.sourcesQueue.length); + const callbacks = await Promise.all( + workerContainers.map((container) => this.prepareWorker(container, progress)), + ); + progress.end(); + + // Run tests + const runProgress = this.setupRunProgress(); + await Promise.all(callbacks.map((callback) => callback())); + runProgress.teardown(); + + this.throwPrinter(); + } + + async handleWorkerTimeout( + duration: string, + container: TestWorkerContainer, + ): Promise { + return new Promise((resolve, reject) => { + const timeout = setTimeout( + () => { + resolve( + container.bridge.end( + `Test worker was unresponsive for ${duration}. We tried to collect some additional metadata but we timed out again trying to fetch it...`, + ), + ); + }, + 3_000, + ); + + this._handleWorkerTimeout(duration, container).then(() => { + clearTimeout(timeout); + resolve(); + }).catch((err) => { + clearTimeout(timeout); + if (err instanceof InspectorClientCloseError) { + return container.bridge.end( + `Test worker was unresponsive for ${duration}. We tried to collect some additional metadata but the inspector connection closed abruptly`, + ); + } else { + reject(err); + } + }); + }); + } + + async _handleWorkerTimeout( + duration: string, + {bridge, inspector}: TestWorkerContainer, + ): Promise { + if (inspector === undefined) { + bridge.end( + `Test worker was unresponsive for ${duration}. There was no inspector connected so we were unable to capture stack frames before it was terminated.`, + ); + return undefined; + } + + inspector.call('Debugger.pause'); + + const params = await inspector.wait('Debugger.paused'); + + const frames: Array = []; + + const callFrames = params.get('callFrames').asArray().slice(0, 20); + for (const callFrame of callFrames) { + const loc = callFrame.get('location'); + + const resolved = this.sourceMaps.assertApproxOriginalPositionFor( + urlToFilename(callFrame.get('url').asString()), + ob1Coerce0To1(loc.get('lineNumber').asZeroIndexedNumber()), + loc.get('columnNumber').asZeroIndexedNumber(), + ); + + const name = callFrame.get('scopeChain').asArray()[0].get('name').asString( + '', + ).split('$').pop(); + + frames.push({ + resolvedLocation: resolved.found, + typeName: undefined, + functionName: name, + methodName: undefined, + filename: resolved.source, + lineNumber: resolved.line, + columnNumber: resolved.column, + isTopLevel: false, + isEval: false, + isNative: false, + isConstructor: false, + isAsync: false, + }); + } + + bridge.endWithError( + new BridgeDiagnosticsError( + deriveDiagnosticFromErrorStructure( + { + name: 'Error', + frames, + }, + { + description: { + category: 'tests/timeout', + message: createBlessedDiagnosticMessage( + `Test worker was unresponsive for ${duration}. Possible infinite loop. Below is a stack trace before the test was terminated.`, + ), + advice: [ + { + type: 'log', + category: 'info', + text: `You can find the specific test that caused this by running rome test --sync-tests`, + }, + ], + }, + }, + ), + bridge, + ), + ); + } + + getWorkers(): TestWorkerContainers { + if (this.workers === undefined) { + throw new Error('TestMasterRunner.init has not been called yet'); + } else { + return this.workers; + } + } + + refToKey(ref: TestRef): string { + return `${ref.filename}: ${ref.testName}`; + } + + getTotalTests(): number { + if (this.focusedTests.length > 0) { + return this.focusedTests.length; + } else { + return this.progress.totalTests; + } + } + + onTestStart( + container: TestWorkerContainer, + ref: TestRef, + timeoutMs: undefined | number, + ) { + this.progress.startedTests++; + + let timeout = undefined; + if (timeoutMs !== undefined) { + timeout = setTimeout( + () => { + // TODO This will kill the whole worker, maybe it's possible to just terminate the current test? Throw an error, see if the next test was ran, or else terminate completely + this.handleWorkerTimeout(`${String(timeoutMs)}ms`, container); + }, + timeoutMs, + ); + } + + this.runningTests.set( + this.refToKey(ref), + { + ref, + timeout, + }, + ); + } + + onTestFound(data: TestRef) { + data; + this.progress.totalTests++; + } + + onTestFinished(ref: TestRef) { + const key = this.refToKey(ref); + const running = this.runningTests.get(key); + if (running === undefined) { + throw new Error('Expected there to be a running test'); + } + + if (running.timeout !== undefined) { + clearTimeout(running.timeout); + } + this.runningTests.delete(key); + + this.progress.finishedTests++; + } + + setupRunProgress(): TestProgress { + const workers = this.getWorkers(); + + const progress = this.request.reporter.progress({ + persistent: true, + title: 'Running', + }); + progress.setTotal(this.getTotalTests()); + + for (let i = 0; i < workers.length; i++) { + const container = workers[i]; + const {bridge} = container; + + const ourRunningTests: Set = new Set(); + + bridge.endEvent.subscribe((error) => { + // Cancel all currently running tests + const cancelTests: Array = []; + + for (const key of ourRunningTests) { + const test = this.runningTests.get(key); + if (test !== undefined) { + cancelTests.push(test.ref); + } + } + + for (const ref of cancelTests) { + this.onTestFinished(ref); + + if (cancelTests.length === 1) { + // If we only have one test to cancel then let's only point the bridge error to this test + this.ignoreBridgeEndError.add(bridge); + + const errDiag = deriveDiagnosticFromError( + error, + { + label: ref.testName, + filename: ref.filename, + description: { + category: 'tests/failure', + }, + }, + ); + + this.printer.processor.addDiagnostic({ + ...errDiag, + description: { + ...errDiag.description, + // We don't care about the advice + advice: [], + }, + }); + } else { + this.printer.processor.addDiagnostic({ + label: ref.testName, + description: descriptions.TESTS.CANCELLED, + location: { + filename: ref.filename, + }, + }); + } + } + }); + + bridge.testStart.subscribe((data) => { + ourRunningTests.add(this.refToKey(data.ref)); + this.onTestStart(container, data.ref, data.timeout); + progress.pushText(getProgressTestRefText(data.ref)); + }); + + bridge.testFinish.subscribe((data) => { + this.onTestFinished(data.ref); + progress.popText(getProgressTestRefText(data.ref)); + progress.tick(); + }); + } + + return { + teardown() { + progress.end(); + }, + }; + } + + printCoverageReport(isError: boolean) { + const {reporter, master, coverageCollector} = this; + + if (isError && this.options.showAllCoverage) { + // Only show coverage for errors when --show-all-coverage has been specified + return; + } + + if (!this.options.coverage) { + return; + } + + reporter.info('Generating coverage'); + + // Fetch coverage entries + const files = coverageCollector.generate(); + if (files.length === 0) { + return; + } + + reporter.heading('Code coverage'); + + // Get the packages associated with all the ran tests, we will filter code coverage to those packages only + const testedPackages: Set = new Set(); + for (const {ref} of this.sources.values()) { + testedPackages.add(master.memoryFs.getOwnedManifest(ref.real)); + } + + let root: CoverageFolder = { + name: undefined, + folders: new Map(), + files: new Map(), + }; + + let totalFiles = 0; + + // Turn the flat list of filenames into a directory tree + for (const file of files) { + const {filename} = file; + + // Get the absolute filename + const absolute = master.projectManager.getFilePathFromUid(filename); + if (absolute === undefined) { + continue; + } + + // Filter out untested packages + const pkg = master.memoryFs.getOwnedManifest(absolute); + if (testedPackages.has(pkg) === false) { + continue; + } + + // TODO maybe filter out test files too? + + // Track unfiltered files + totalFiles++; + + const filenameParts = filename.split('/'); + const basename = filenameParts.pop(); + if (basename === undefined) { + throw new Error('Should always be at least one element from a split()'); + } + + let target: CoverageFolder = root; + + for (const part of filenameParts) { + const existingFolder = target.folders.get(part); + if (existingFolder === undefined) { + const newFolder = { + name: part, + folders: new Map(), + files: new Map(), + }; + target.folders.set(part, newFolder); + target = newFolder; + } else { + target = existingFolder; + } + } + + target.files.set(basename, file); + } + + // Continuously merge all entries with only a single folder from the root + while (root.folders.size === 1 && root.files.size === 0) { + // Awkward way to get the first value out of the folders map... + const newRoot = root.folders.values().next().value; + root = { + ...newRoot, + name: root.name !== undefined && newRoot.name !== undefined + ? `${root.name}/${newRoot.name}` + : newRoot.name, + }; + } + + const rows: Array> = []; + + // If there's more than 15 files to show, and we don't have the explicit showAllCoverage flag + + // then truncate the output + const showAllCoverage = this.options.showAllCoverage || totalFiles < 15; + + function buildRows(folder: CoverageFolder, depth: number) { + const name = folder.name === undefined ? 'All files' : `${folder.name}/`; + const folderPercent = percentInsideCoverageFolder(folder); + + rows.push([ + ' '.repeat(depth) + `${name}`, + formatPercent(folderPercent.functions), + formatPercent(folderPercent.branches), + formatPercent(folderPercent.lines), + ]); + + // Don't ever show anything deeper than a single level when showAllCoverage is off + if (!showAllCoverage && depth > 0) { + return; + } + + const fileIndent = ' '.repeat(depth + 1); + for (const [name, file] of sortMapKeys(folder.files)) { + let absolute = file.filename; + + // Exchange any UIDs + const absolutePath = master.projectManager.getFilePathFromUid(file.filename); + if (absolutePath !== undefined) { + absolute = absolutePath.join(); + } + + rows.push([ + fileIndent + markup`${name}`, + formatPercent(file.functions.percent), + formatPercent(file.branches.percent), + formatPercent(file.lines.percent), + ]); + } + + for (const subFolder of sortMapKeys(folder.folders).values()) { + buildRows(subFolder, depth + 1); + } + } + + buildRows(root, 0); + + reporter.table(['File', '% Functions', '% Branches', '% Lines'], rows); + + if (!showAllCoverage) { + reporter.br(); + reporter.info( + 'Additional coverage information available. Refine the executed tests or add the --show-all-coverage flag', + ); + } + + reporter.hr(); + } + + getSourceCode(filename: string): undefined | string { + const testSource = this.sources.get(filename); + if (testSource === undefined) { + return undefined; + } else { + return testSource.code; + } + } + + printFocusedTestWarning(reporter: Reporter) { + const {focusedTests} = this; + if (focusedTests.length === 0) { + return; + } + + const formattedFocusedTests = focusedTests.map(({testName, location}) => { + const loc = this.printer.processor.normalizer.normalizeLocation(location); + + return markup`${testName} at ${safeMarkup( + diagnosticLocationToMarkupFilelink(loc), + )}`; + }); + + if (focusedTests.length === 1) { + reporter.warn(`Only ran the focused test ${formattedFocusedTests[0]}`); + } else { + reporter.warn( + `Only ran the following ${focusedTests.length} focused ${grammarNumberTests( + focusedTests.length, + )}`, + ); + reporter.list(formattedFocusedTests); + } + + const otherTotal = this.progress.totalTests - this.focusedTests.length; + reporter.info( + `${otherTotal} other ${grammarNumberTests( + otherTotal, + )} ignored`, + ); + } + + printSnapshotCounts(reporter: Reporter) { + const { + createdSnapshots, + deletedSnapshots, + updatedSnapshots, + updatedInlineSnapshots, + } = this.progress; + + const snapshotCounts: Array<{ + inline: boolean; + count: number; + noun: string; + }> = [ + {inline: false, count: createdSnapshots, noun: 'created'}, + {inline: false, count: updatedSnapshots, noun: 'updated'}, + {inline: false, count: deletedSnapshots, noun: 'deleted'}, + {inline: true, count: updatedInlineSnapshots, noun: 'updated'}, + ].filter(({count}) => count > 0); + + if (snapshotCounts.length === 0) { + return; + } + + const first = snapshotCounts.shift()!; + const parts = [ + // Inline snapshots will always be the last element, so if it's inline here then there's no others + `${first.count} ${first.inline + ? 'inline snapshots' + : 'snapshots'} ${first.noun}`, + ]; + + for (let {inline, count, noun} of snapshotCounts) { + if (inline) { + noun = `inline ${noun}`; + } + parts.push(`${count} ${noun}`); + } + + reporter.success(parts.join(', ')); + } + + throwPrinter() { + const {printer} = this; + + printer.onFooterPrint((reporter, isError) => { + this.printCoverageReport(isError); + this.printSnapshotCounts(reporter); + this.printFocusedTestWarning(reporter); + + if (!isError) { + // Don't say "all" when we have focused tests + let prefix = this.focusedTests.length === 0 ? 'All ' : ''; + const totalCount = this.getTotalTests(); + reporter.success( + `${prefix}${humanizeNumber(totalCount)} ${grammarNumberTests( + totalCount, + )} passed!`, + ); + return true; + } + + // Show default footer + return false; + }); + + throw printer; + } } diff --git a/packages/@romejs/core/master/testing/types.ts b/packages/@romejs/core/master/testing/types.ts index 2b9438569b7..67ab0328110 100644 --- a/packages/@romejs/core/master/testing/types.ts +++ b/packages/@romejs/core/master/testing/types.ts @@ -13,40 +13,40 @@ import child = require('child_process'); import {FileReference} from '@romejs/core/common/types/files'; export type TestSource = { - code: string; - sourceMap: SourceMapGenerator; - ref: FileReference; + code: string; + sourceMap: SourceMapGenerator; + ref: FileReference; }; export type TestSources = Map; export type TestMasterRunnerConstructorOptions = { - sources: TestSources; - request: MasterRequest; - addDiagnostics: Diagnostics; - options: TestMasterRunnerOptions; + sources: TestSources; + request: MasterRequest; + addDiagnostics: Diagnostics; + options: TestMasterRunnerOptions; }; export type TestMasterRunnerOptions = { - focusAllowed: boolean; - coverage: boolean; - showAllCoverage: boolean; - updateSnapshots: boolean; - freezeSnapshots: boolean; - verboseDiagnostics: boolean; - syncTests: boolean; + focusAllowed: boolean; + coverage: boolean; + showAllCoverage: boolean; + updateSnapshots: boolean; + freezeSnapshots: boolean; + verboseDiagnostics: boolean; + syncTests: boolean; }; export type CoverageFolder = { - name: undefined | string; - folders: Map; - files: Map; + name: undefined | string; + folders: Map; + files: Map; }; export type TestWorkerContainer = { - bridge: TestWorkerBridge; - process: child.ChildProcess; - inspector: undefined | InspectorClient; + bridge: TestWorkerBridge; + process: child.ChildProcess; + inspector: undefined | InspectorClient; }; export type TestWorkerContainers = Array; diff --git a/packages/@romejs/core/master/testing/utils.ts b/packages/@romejs/core/master/testing/utils.ts index 8c2197169d6..979a52ecabf 100644 --- a/packages/@romejs/core/master/testing/utils.ts +++ b/packages/@romejs/core/master/testing/utils.ts @@ -9,60 +9,60 @@ import {naturalCompare} from '@romejs/string-utils'; import {CoverageFolder} from './types'; export function sortMapKeys(map: Map): Map { - const sortedKeys = Array.from(map.keys()).sort(naturalCompare); - const newMap: Map = new Map(); - for (const key of sortedKeys) { - const val = map.get(key); - if (val === undefined) { - throw new Error('Expected value'); - } - newMap.set(key, val); - } - return newMap; + const sortedKeys = Array.from(map.keys()).sort(naturalCompare); + const newMap: Map = new Map(); + for (const key of sortedKeys) { + const val = map.get(key); + if (val === undefined) { + throw new Error('Expected value'); + } + newMap.set(key, val); + } + return newMap; } export function formatPercent(num: number): string { - const str = String(Math.floor(num)); - if (num > 80) { - return `${str}`; - } else if (num > 40) { - return `${str}`; - } else { - return `${str}`; - } + const str = String(Math.floor(num)); + if (num > 80) { + return `${str}`; + } else if (num > 40) { + return `${str}`; + } else { + return `${str}`; + } } export function percentInsideCoverageFolder( - folder: CoverageFolder, + folder: CoverageFolder, ): { - functions: number; - branches: number; - lines: number; + functions: number; + branches: number; + lines: number; } { - let totalFiles = 0; - let functions = 0; - let branches = 0; - let lines = 0; + let totalFiles = 0; + let functions = 0; + let branches = 0; + let lines = 0; - const folders = [folder]; - while (folders.length > 0) { - const folder = folders.shift()!; + const folders = [folder]; + while (folders.length > 0) { + const folder = folders.shift()!; - for (const file of folder.files.values()) { - totalFiles++; - functions += file.functions.percent; - branches += file.branches.percent; - lines += file.lines.percent; - } + for (const file of folder.files.values()) { + totalFiles++; + functions += file.functions.percent; + branches += file.branches.percent; + lines += file.lines.percent; + } - for (const subFolder of folder.folders.values()) { - folders.push(subFolder); - } - } + for (const subFolder of folder.folders.values()) { + folders.push(subFolder); + } + } - return { - functions: totalFiles === 0 ? 100 : functions / totalFiles, - branches: totalFiles === 0 ? 100 : branches / totalFiles, - lines: totalFiles === 0 ? 100 : lines / totalFiles, - }; + return { + functions: totalFiles === 0 ? 100 : functions / totalFiles, + branches: totalFiles === 0 ? 100 : branches / totalFiles, + lines: totalFiles === 0 ? 100 : lines / totalFiles, + }; } diff --git a/packages/@romejs/core/master/web/WebRequest.ts b/packages/@romejs/core/master/web/WebRequest.ts index 99d78c901d8..6e33809b643 100644 --- a/packages/@romejs/core/master/web/WebRequest.ts +++ b/packages/@romejs/core/master/web/WebRequest.ts @@ -7,8 +7,8 @@ import {Master, MasterRequest, WebBridge} from '@romejs/core'; import { - deriveDiagnosticFromError, - getDiagnosticsFromError, + deriveDiagnosticFromError, + getDiagnosticsFromError, } from '@romejs/diagnostics'; import {dedent, removePrefix, removeSuffix} from '@romejs/string-utils'; import Bundler from '../bundler/Bundler'; @@ -25,129 +25,129 @@ import http = require('http'); const waitForever = new Promise(() => {}); export function stripBundleSuffix(pathname: string): string { - return removePrefix(removeSuffix(pathname, '.bundle'), '/'); + return removePrefix(removeSuffix(pathname, '.bundle'), '/'); } export default class WebRequest { - constructor( - server: WebServer, - req: http.IncomingMessage, - res: http.ServerResponse, - ) { - this.req = req; - this.res = res; - this.server = server; - this.reporter = server.reporter; - this.masterRequest = server.masterRequest; - this.master = server.master; - - const reqUrl = req.url; - if (reqUrl === undefined) { - throw new Error('req.url should not be undefined'); - } - this.url = consumeUrl(reqUrl); - } - - reporter: Reporter; - server: WebServer; - master: Master; - masterRequest: MasterRequest; - - url: ConsumableUrl; - - req: http.IncomingMessage; - res: http.ServerResponse; - - loadRawBody(): Promise { - const {req} = this; - - req.setEncoding('utf8'); - let rawBody = ''; - - return new Promise((resolve) => { - req.on( - 'data', - (chunk) => { - rawBody += chunk; - }, - ); - - req.on( - 'end', - () => { - resolve(rawBody); - }, - ); - }); - } - - async dispatch(): Promise { - const {res} = this; - - try { - const rawBody = await this.loadRawBody(); - await this.dispatchWithBody(rawBody); - res.end(); - } catch (err) { - res.writeHead(500, {'Content-Type': 'text/plain'}); - - let diagnostics = getDiagnosticsFromError(err); - if (diagnostics === undefined) { - diagnostics = [ - deriveDiagnosticFromError( - err, - { - description: { - category: 'internalError/httpServer', - }, - }, - ), - ]; - } - - //this.request.reporter.clear(); - try { - const printer = this.masterRequest.createDiagnosticsPrinter( - this.master.createDiagnosticsProcessor({ - origins: [ - { - category: 'WebRequest', - }, - ], - }), - ); - printer.processor.addDiagnostics(diagnostics); - await printer.print(); - } catch (err) { - this.reporter.warn('Failed trying to print diagnostics'); - this.reporter.error(err.stack); - } - - res.end('Diagnostics available, see console'); - } - } - - async dispatchWithBody(body: string): Promise { - const {res} = this; - const pathname = this.url.path.asString(); - body; - - switch (pathname) { - case '/favicon.ico': { - res.end(''); - break; - } - - case '/__rome__/websocket': - return this.handleFrontendWebsocket(); - - case '/__rome__/script.js': - return this.handleFrontendScript(); - - case '/__rome__': { - res.writeHead(200, {'Content-Type': 'text/html'}); - res.end( - dedent` + constructor( + server: WebServer, + req: http.IncomingMessage, + res: http.ServerResponse, + ) { + this.req = req; + this.res = res; + this.server = server; + this.reporter = server.reporter; + this.masterRequest = server.masterRequest; + this.master = server.master; + + const reqUrl = req.url; + if (reqUrl === undefined) { + throw new Error('req.url should not be undefined'); + } + this.url = consumeUrl(reqUrl); + } + + reporter: Reporter; + server: WebServer; + master: Master; + masterRequest: MasterRequest; + + url: ConsumableUrl; + + req: http.IncomingMessage; + res: http.ServerResponse; + + loadRawBody(): Promise { + const {req} = this; + + req.setEncoding('utf8'); + let rawBody = ''; + + return new Promise((resolve) => { + req.on( + 'data', + (chunk) => { + rawBody += chunk; + }, + ); + + req.on( + 'end', + () => { + resolve(rawBody); + }, + ); + }); + } + + async dispatch(): Promise { + const {res} = this; + + try { + const rawBody = await this.loadRawBody(); + await this.dispatchWithBody(rawBody); + res.end(); + } catch (err) { + res.writeHead(500, {'Content-Type': 'text/plain'}); + + let diagnostics = getDiagnosticsFromError(err); + if (diagnostics === undefined) { + diagnostics = [ + deriveDiagnosticFromError( + err, + { + description: { + category: 'internalError/httpServer', + }, + }, + ), + ]; + } + + //this.request.reporter.clear(); + try { + const printer = this.masterRequest.createDiagnosticsPrinter( + this.master.createDiagnosticsProcessor({ + origins: [ + { + category: 'WebRequest', + }, + ], + }), + ); + printer.processor.addDiagnostics(diagnostics); + await printer.print(); + } catch (err) { + this.reporter.warn('Failed trying to print diagnostics'); + this.reporter.error(err.stack); + } + + res.end('Diagnostics available, see console'); + } + } + + async dispatchWithBody(body: string): Promise { + const {res} = this; + const pathname = this.url.path.asString(); + body; + + switch (pathname) { + case '/favicon.ico': { + res.end(''); + break; + } + + case '/__rome__/websocket': + return this.handleFrontendWebsocket(); + + case '/__rome__/script.js': + return this.handleFrontendScript(); + + case '/__rome__': { + res.writeHead(200, {'Content-Type': 'text/html'}); + res.end( + dedent` @@ -161,207 +161,205 @@ export default class WebRequest { `, - ); - break; - } - - case '/hot': - return this.handleDeviceWebsocket(); - - default: - return this.handleWildcard(pathname); - } - } - - async handleWildcard(pathname: string) { - const {req, res} = this; - - // Check for *.bundle - if (pathname.endsWith('.bundle')) { - const handled = await this.handleBundleRequest(); - if (handled) { - return; - } - } - - // Look up static file - const project = await this.masterRequest.assertClientCwdProject(); - if (project.config.develop.serveStatic) { - const handled = await this.handlePossibleStatic(pathname, project); - if (handled) { - return; - } - } - - this.reporter.error(`Unknown request for`, req.url); - res.writeHead(404); - res.end('Not found'); - } - - async handlePossibleStatic( - pathname: string, - project: ProjectDefinition, - ): Promise { - project; - - const possibleStaticPath = await this.server.pathnameToAbsolutePath( - pathname, - ); - - // TODO check if it is a file - if ( - possibleStaticPath !== undefined && - (await this.master.memoryFs.existsHard(possibleStaticPath)) - ) { - return true; - } - - return false; - } - - async handleFrontendScript() { - const {res} = this; - res.writeHead(200, {'Content-Type': 'application/javascript'}); - - const bundler = new Bundler( - this.masterRequest, - { - inlineSourceMap: false, - cwd: this.masterRequest.client.flags.cwd, - resolver: { - platform: 'web', - }, - }, - ); - const resolved = await this.master.resolver.resolveEntryAssertPath({ - origin: this.masterRequest.client.flags.cwd, - source: createUnknownFilePath('@romejs-web/frontend'), - }); - const bundle = await bundler.bundle(resolved); - res.end(bundle.entry.js); - } - - negotiateWebsocket() { - const {req} = this; - - const digest = createKey(String(req.headers['sec-websocket-key'])); - - const headers = [ - 'HTTP/1.1 101 Switching Protocols', - 'Upgrade: websocket', - 'Connection: Upgrade', - 'Sec-WebSocket-Protocol: rome', - `Sec-WebSocket-Accept: ${digest}`, - '', - '', - ]; - - req.socket.write(headers.join('\r\n')); - } - - async handleDeviceWebsocketMessage( - socket: WebSocketInterface, - data: HmrClientMessage, - ) { - switch (data.type) { - case 'log': - return this.server.printConsoleLog(data); - - case 'log-opt-in': - // ??? - return; - - case 'register-entrypoints': - /// ??? - return; - - default: - console.log('UNKNOWN MESSAGE', data); - } - } - - async handleDeviceWebsocket() { - const {req} = this; - this.negotiateWebsocket(); - - const socket = new WebSocketInterface('server', req.socket); - this.server.deviceWebsockets.add(socket); - - req.socket.on( - 'error', - (err) => { - console.log(err.stack); - }, - ); - - this.reporter.success(`Device websocket client connected`); - - socket.completeFrameEvent.subscribe((frame) => { - const text = frame.payload.toString(); - try { - const json = JSON.parse(text); - this.handleDeviceWebsocketMessage(socket, json); - } catch (err) { - if (err instanceof SyntaxError) { - console.log('UNKNOWN FRAME', text); - return; - } else { - throw err; - } - } - }); - - socket.errorEvent.subscribe((err) => { - console.log(err); - }); - - socket.endEvent.subscribe(() => { - console.log('END'); - this.server.deviceWebsockets.delete(socket); - }); - - await waitForever; - } - - async handleFrontendWebsocket() { - const {req} = this; - this.negotiateWebsocket(); - - const socket = new WebSocketInterface('server', req.socket); - const bridge = createBridgeFromWebSocketInterface( - WebBridge, - socket, - { - type: 'client', - }, - ); - this.server.frontendWebsocketBridges.add(bridge); - - req.socket.on( - 'close', - () => { - this.server.frontendWebsocketBridges.delete(bridge); - }, - ); - - await bridge.handshake(); - - this.reporter.success('Frontend websocket client connected'); - - this.server.sendRequests(bridge); - - await waitForever; - } - - async handleBundleRequest() { - const {res} = this; - - const {bundler, path} = await this.server.getBundler(this.url); - const bundle = await bundler.bundle(path); - const content = bundle.entry.js.content; - - res.writeHead(200, {'Content-Type': 'application/javascript'}); - res.end(content); - return true; - } + ); + break; + } + + case '/hot': + return this.handleDeviceWebsocket(); + + default: + return this.handleWildcard(pathname); + } + } + + async handleWildcard(pathname: string) { + const {req, res} = this; + + // Check for *.bundle + if (pathname.endsWith('.bundle')) { + const handled = await this.handleBundleRequest(); + if (handled) { + return; + } + } + + // Look up static file + const project = await this.masterRequest.assertClientCwdProject(); + if (project.config.develop.serveStatic) { + const handled = await this.handlePossibleStatic(pathname, project); + if (handled) { + return; + } + } + + this.reporter.error(`Unknown request for`, req.url); + res.writeHead(404); + res.end('Not found'); + } + + async handlePossibleStatic( + pathname: string, + project: ProjectDefinition, + ): Promise { + project; + + const possibleStaticPath = await this.server.pathnameToAbsolutePath(pathname); + + // TODO check if it is a file + if ( + possibleStaticPath !== undefined && + (await this.master.memoryFs.existsHard(possibleStaticPath)) + ) { + return true; + } + + return false; + } + + async handleFrontendScript() { + const {res} = this; + res.writeHead(200, {'Content-Type': 'application/javascript'}); + + const bundler = new Bundler( + this.masterRequest, + { + inlineSourceMap: false, + cwd: this.masterRequest.client.flags.cwd, + resolver: { + platform: 'web', + }, + }, + ); + const resolved = await this.master.resolver.resolveEntryAssertPath({ + origin: this.masterRequest.client.flags.cwd, + source: createUnknownFilePath('@romejs-web/frontend'), + }); + const bundle = await bundler.bundle(resolved); + res.end(bundle.entry.js); + } + + negotiateWebsocket() { + const {req} = this; + + const digest = createKey(String(req.headers['sec-websocket-key'])); + + const headers = [ + 'HTTP/1.1 101 Switching Protocols', + 'Upgrade: websocket', + 'Connection: Upgrade', + 'Sec-WebSocket-Protocol: rome', + `Sec-WebSocket-Accept: ${digest}`, + '', + '', + ]; + + req.socket.write(headers.join('\r\n')); + } + + async handleDeviceWebsocketMessage( + socket: WebSocketInterface, + data: HmrClientMessage, + ) { + switch (data.type) { + case 'log': + return this.server.printConsoleLog(data); + + case 'log-opt-in': + // ??? + return; + + case 'register-entrypoints': + /// ??? + return; + + default: + console.log('UNKNOWN MESSAGE', data); + } + } + + async handleDeviceWebsocket() { + const {req} = this; + this.negotiateWebsocket(); + + const socket = new WebSocketInterface('server', req.socket); + this.server.deviceWebsockets.add(socket); + + req.socket.on( + 'error', + (err) => { + console.log(err.stack); + }, + ); + + this.reporter.success(`Device websocket client connected`); + + socket.completeFrameEvent.subscribe((frame) => { + const text = frame.payload.toString(); + try { + const json = JSON.parse(text); + this.handleDeviceWebsocketMessage(socket, json); + } catch (err) { + if (err instanceof SyntaxError) { + console.log('UNKNOWN FRAME', text); + return; + } else { + throw err; + } + } + }); + + socket.errorEvent.subscribe((err) => { + console.log(err); + }); + + socket.endEvent.subscribe(() => { + console.log('END'); + this.server.deviceWebsockets.delete(socket); + }); + + await waitForever; + } + + async handleFrontendWebsocket() { + const {req} = this; + this.negotiateWebsocket(); + + const socket = new WebSocketInterface('server', req.socket); + const bridge = createBridgeFromWebSocketInterface( + WebBridge, + socket, + { + type: 'client', + }, + ); + this.server.frontendWebsocketBridges.add(bridge); + + req.socket.on( + 'close', + () => { + this.server.frontendWebsocketBridges.delete(bridge); + }, + ); + + await bridge.handshake(); + + this.reporter.success('Frontend websocket client connected'); + + this.server.sendRequests(bridge); + + await waitForever; + } + + async handleBundleRequest() { + const {res} = this; + + const {bundler, path} = await this.server.getBundler(this.url); + const bundle = await bundler.bundle(path); + const content = bundle.entry.js.content; + + res.writeHead(200, {'Content-Type': 'application/javascript'}); + res.end(content); + return true; + } } diff --git a/packages/@romejs/core/master/web/hmr.ts b/packages/@romejs/core/master/web/hmr.ts index 53b02ff95ae..c083537c2d3 100644 --- a/packages/@romejs/core/master/web/hmr.ts +++ b/packages/@romejs/core/master/web/hmr.ts @@ -8,101 +8,101 @@ export type ModuleMap = Array<[number, string]>; export type Bundle = { - pre: string; - post: string; - modules: ModuleMap; + pre: string; + post: string; + modules: ModuleMap; }; export type DeltaBundle = { - added: ModuleMap; - modified: ModuleMap; - deleted: Array; + added: ModuleMap; + modified: ModuleMap; + deleted: Array; }; export type BundleVariant = - | ({ - base: true; - revisionId: string; - } & Bundle) - | ({ - base: false; - revisionId: string; - } & DeltaBundle); + | ({ + base: true; + revisionId: string; + } & Bundle) + | ({ + base: false; + revisionId: string; + } & DeltaBundle); export type BundleMetadata = { - pre: number; - post: number; - modules: Array<[number, number]>; + pre: number; + post: number; + modules: Array<[number, number]>; }; export type FormattedError = { - type: string; - message: string; - errors: Array<{ - description: string; - }>; + type: string; + message: string; + errors: Array<{ + description: string; + }>; }; export type HmrModule = { - module: [number, string]; - sourceMappingURL: string; - sourceURL: string; + module: [number, string]; + sourceMappingURL: string; + sourceURL: string; }; export type HmrUpdate = { - added: Array; - deleted: Array; - isInitialUpdate: boolean; - modified: Array; - revisionId: string; + added: Array; + deleted: Array; + isInitialUpdate: boolean; + modified: Array; + revisionId: string; }; export type HmrServerUpdateMessage = { - type: 'update'; - body: HmrUpdate; + type: 'update'; + body: HmrUpdate; }; export type HmrServerErrorMessage = { - type: 'error'; - body: FormattedError; + type: 'error'; + body: FormattedError; }; export type HmrClientLogMessage = { - type: 'log'; - level: - | 'trace' - | 'info' - | 'warn' - | 'log' - | 'group' - | 'groupCollapsed' - | 'groupEnd' - | 'debug'; - data: Array; + type: 'log'; + level: + | 'trace' + | 'info' + | 'warn' + | 'log' + | 'group' + | 'groupCollapsed' + | 'groupEnd' + | 'debug'; + data: Array; }; export type HmrClientMessage = - | { - type: 'register-entrypoints'; - entryPoints: Array; - } - | HmrClientLogMessage - | { - type: 'log-opt-in'; - }; + | { + type: 'register-entrypoints'; + entryPoints: Array; + } + | HmrClientLogMessage + | { + type: 'log-opt-in'; + }; export type HmrServerMessage = - | { - type: 'bundle-registered'; - } - | { - type: 'update-start'; - body: { - isInitialUpdate: boolean; - }; - } - | { - type: 'update-done'; - } - | HmrServerUpdateMessage - | HmrServerErrorMessage; + | { + type: 'bundle-registered'; + } + | { + type: 'update-start'; + body: { + isInitialUpdate: boolean; + }; + } + | { + type: 'update-done'; + } + | HmrServerUpdateMessage + | HmrServerErrorMessage; diff --git a/packages/@romejs/core/master/web/index.ts b/packages/@romejs/core/master/web/index.ts index 1cb2e2f78cd..349cbf2a991 100644 --- a/packages/@romejs/core/master/web/index.ts +++ b/packages/@romejs/core/master/web/index.ts @@ -14,8 +14,8 @@ import http = require('http'); import {escapeMarkup} from '@romejs/string-markup'; import {Reporter, ReporterStream} from '@romejs/cli-reporter'; import { - MasterQueryRequest, - MasterQueryResponse, + MasterQueryRequest, + MasterQueryResponse, } from '../../common/bridges/MasterBridge'; import {MasterMarker} from '../Master'; import {ClientFlagsJSON} from '../../common/types/client'; @@ -27,268 +27,268 @@ import {HmrClientLogMessage, HmrServerMessage} from './hmr'; import {ConsumableUrl} from '@romejs/codec-url'; export type WebMasterTime = { - startTime: number; - endTime: undefined | number; + startTime: number; + endTime: undefined | number; }; export type WebMasterClient = WebMasterTime & { - id: number; - flags: ClientFlagsJSON; - stdoutAnsi: string; - stdoutHTML: string; + id: number; + flags: ClientFlagsJSON; + stdoutAnsi: string; + stdoutHTML: string; }; export type WebMasterRequest = WebMasterTime & { - id: number; - client: number; - query: MasterQueryRequest; - markers: Array; - response: undefined | MasterQueryResponse; + id: number; + client: number; + query: MasterQueryRequest; + markers: Array; + response: undefined | MasterQueryResponse; }; export class WebServer { - constructor(req: MasterRequest) { - const {master} = req; - - this.masterRequest = req; - this.reporter = req.reporter; - this.master = master; - - this.bundlerCache = new Map(); - - this.savingRequests = false; - this.clientRequestHistory = new Map(); - this.clientHistory = new Map(); - - this.deviceWebsockets = new Set(); - this.frontendWebsocketBridges = new Set(); - - this.server = http.createServer((req, res) => { - const webRequest = new WebRequest(this, req, res); - webRequest.dispatch(); - }); - - master.clientStartEvent.subscribe((client) => { - if (!this.savingRequests) { - return; - } - - const data: WebMasterClient = { - id: client.id, - flags: { - ...client.flags, - cwd: client.flags.cwd.join(), - }, - startTime: Date.now(), - endTime: undefined, - stdoutAnsi: '', - stdoutHTML: '', - }; - this.clientHistory.set(client.id, data); - this.refreshRequests(); - - const ansiReporterStream: ReporterStream = { - type: 'all', - format: 'ansi', - columns: 100, - unicode: true, - write(chunk) { - data.stdoutAnsi += chunk; - }, - }; - - const htmlReporterStream: ReporterStream = { - type: 'all', - format: 'html', - columns: 100, - unicode: true, - write(chunk) { - data.stdoutHTML += chunk; - }, - }; - - client.reporter.addStream(ansiReporterStream); - master.connectedReporters.addStream(ansiReporterStream); - - client.reporter.addStream(htmlReporterStream); - master.connectedReporters.addStream(htmlReporterStream); - - client.bridge.endEvent.subscribe(() => { - master.connectedReporters.removeStream(ansiReporterStream); - master.connectedReporters.removeStream(htmlReporterStream); - - data.endTime = Date.now(); - this.refreshRequests(); - }); - }); - - master.requestStartEvent.subscribe((request) => { - if (!this.savingRequests) { - return; - } - - const data: WebMasterRequest = { - id: request.id, - client: request.client.id, - query: request.query, - markers: [], - response: undefined, - startTime: Date.now(), - endTime: undefined, - }; - this.clientRequestHistory.set(request.id, data); - this.refreshRequests(); - - request.markerEvent.subscribe((marker) => { - data.markers.push(marker); - this.refreshRequests(); - }); - - request.endEvent.subscribe((response) => { - // Update completion fields - data.response = response; - data.endTime = Date.now(); - this.refreshRequests(); - }); - }); - } - - bundlerCache: Map; - - savingRequests: boolean; - clientRequestHistory: Map; - clientHistory: Map; - - deviceWebsockets: Set; - frontendWebsocketBridges: Set; - - reporter: Reporter; - masterRequest: MasterRequest; - master: Master; - server: http.Server; - - sendRequests(bridge: WebBridge) { - bridge.requests.send({ - requests: Array.from(this.clientRequestHistory.values()), - clients: Array.from(this.clientHistory.values()), - }); - } - - refreshRequests() { - for (const bridge of this.frontendWebsocketBridges) { - this.sendRequests(bridge); - } - } - - close() { - this.server.close(); - } - - listen(port: number) { - this.server.listen(port); - - //this.reporter.clear(); - const url = `http://localhost:${String(port)}`; - this.reporter.success(`Listening on ${url}`); - this.reporter.info( - `Web console available at ${url}/__rome__`, - ); - } - - printConsoleLog(msg: HmrClientLogMessage) { - const {reporter} = this.masterRequest; - - let buf = msg.data.map((arg) => { - if (typeof arg === 'string') { - return escapeMarkup(arg); - } else { - return prettyFormat(arg, {markup: true}); - } - }).join(' '); - - switch (msg.level) { - case 'info': { - reporter.info(buf); - break; - } - - case 'warn': { - reporter.warn(buf); - break; - } - - case 'log': - case 'trace': { - reporter.verboseForce(buf); - break; - } - - case 'group': - case 'groupCollapsed': - case 'groupEnd': - reporter.logAll('TODO'); - } - } - - async pathnameToAbsolutePath( - pathname: string, - ): Promise { - const project = await this.masterRequest.assertClientCwdProject(); - const possibleStaticPath = project.folder.append(pathname); - - // This check makes sure that files outside of the project directory cannot be served - if (possibleStaticPath.isRelativeTo(project.folder)) { - return possibleStaticPath; - } else { - return undefined; - } - } - - sendToAllDeviceWebsockets(msg: HmrServerMessage) { - const text = JSON.stringify(msg); - for (const socket of this.deviceWebsockets) { - socket.send(text); - } - } - - async getBundler( - url: ConsumableUrl, - ): Promise<{ - bundler: Bundler; - path: AbsoluteFilePath; - }> { - const pathname = stripBundleSuffix(String(url.path.asString())); - - const absolute = await this.pathnameToAbsolutePath(pathname); - if (absolute === undefined) { - throw new Error('Pathname is attempting to escalate out of cwd'); - } - - const pathPointer = url.path.getDiagnosticLocation(); - const path = await this.master.resolver.resolveEntryAssertPath( - { - origin: this.masterRequest.client.flags.cwd, - source: absolute, - }, - pathPointer === undefined ? undefined : {location: pathPointer}, - ); - - const platform = url.query.get('platform').asStringSetOrVoid(PLATFORMS); - const cacheKey = JSON.stringify({ - platform, - }); - - const cached = this.bundlerCache.get(cacheKey); - if (cached !== undefined) { - return {bundler: cached, path}; - } - - const bundlerConfig: BundlerConfig = this.masterRequest.getBundlerConfigFromFlags({ - platform, - }); - - const bundler = new Bundler(this.masterRequest, bundlerConfig); - this.bundlerCache.set(cacheKey, bundler); - return {bundler, path}; - } + constructor(req: MasterRequest) { + const {master} = req; + + this.masterRequest = req; + this.reporter = req.reporter; + this.master = master; + + this.bundlerCache = new Map(); + + this.savingRequests = false; + this.clientRequestHistory = new Map(); + this.clientHistory = new Map(); + + this.deviceWebsockets = new Set(); + this.frontendWebsocketBridges = new Set(); + + this.server = http.createServer((req, res) => { + const webRequest = new WebRequest(this, req, res); + webRequest.dispatch(); + }); + + master.clientStartEvent.subscribe((client) => { + if (!this.savingRequests) { + return; + } + + const data: WebMasterClient = { + id: client.id, + flags: { + ...client.flags, + cwd: client.flags.cwd.join(), + }, + startTime: Date.now(), + endTime: undefined, + stdoutAnsi: '', + stdoutHTML: '', + }; + this.clientHistory.set(client.id, data); + this.refreshRequests(); + + const ansiReporterStream: ReporterStream = { + type: 'all', + format: 'ansi', + columns: 100, + unicode: true, + write(chunk) { + data.stdoutAnsi += chunk; + }, + }; + + const htmlReporterStream: ReporterStream = { + type: 'all', + format: 'html', + columns: 100, + unicode: true, + write(chunk) { + data.stdoutHTML += chunk; + }, + }; + + client.reporter.addStream(ansiReporterStream); + master.connectedReporters.addStream(ansiReporterStream); + + client.reporter.addStream(htmlReporterStream); + master.connectedReporters.addStream(htmlReporterStream); + + client.bridge.endEvent.subscribe(() => { + master.connectedReporters.removeStream(ansiReporterStream); + master.connectedReporters.removeStream(htmlReporterStream); + + data.endTime = Date.now(); + this.refreshRequests(); + }); + }); + + master.requestStartEvent.subscribe((request) => { + if (!this.savingRequests) { + return; + } + + const data: WebMasterRequest = { + id: request.id, + client: request.client.id, + query: request.query, + markers: [], + response: undefined, + startTime: Date.now(), + endTime: undefined, + }; + this.clientRequestHistory.set(request.id, data); + this.refreshRequests(); + + request.markerEvent.subscribe((marker) => { + data.markers.push(marker); + this.refreshRequests(); + }); + + request.endEvent.subscribe((response) => { + // Update completion fields + data.response = response; + data.endTime = Date.now(); + this.refreshRequests(); + }); + }); + } + + bundlerCache: Map; + + savingRequests: boolean; + clientRequestHistory: Map; + clientHistory: Map; + + deviceWebsockets: Set; + frontendWebsocketBridges: Set; + + reporter: Reporter; + masterRequest: MasterRequest; + master: Master; + server: http.Server; + + sendRequests(bridge: WebBridge) { + bridge.requests.send({ + requests: Array.from(this.clientRequestHistory.values()), + clients: Array.from(this.clientHistory.values()), + }); + } + + refreshRequests() { + for (const bridge of this.frontendWebsocketBridges) { + this.sendRequests(bridge); + } + } + + close() { + this.server.close(); + } + + listen(port: number) { + this.server.listen(port); + + //this.reporter.clear(); + const url = `http://localhost:${String(port)}`; + this.reporter.success(`Listening on ${url}`); + this.reporter.info( + `Web console available at ${url}/__rome__`, + ); + } + + printConsoleLog(msg: HmrClientLogMessage) { + const {reporter} = this.masterRequest; + + let buf = msg.data.map((arg) => { + if (typeof arg === 'string') { + return escapeMarkup(arg); + } else { + return prettyFormat(arg, {markup: true}); + } + }).join(' '); + + switch (msg.level) { + case 'info': { + reporter.info(buf); + break; + } + + case 'warn': { + reporter.warn(buf); + break; + } + + case 'log': + case 'trace': { + reporter.verboseForce(buf); + break; + } + + case 'group': + case 'groupCollapsed': + case 'groupEnd': + reporter.logAll('TODO'); + } + } + + async pathnameToAbsolutePath( + pathname: string, + ): Promise { + const project = await this.masterRequest.assertClientCwdProject(); + const possibleStaticPath = project.folder.append(pathname); + + // This check makes sure that files outside of the project directory cannot be served + if (possibleStaticPath.isRelativeTo(project.folder)) { + return possibleStaticPath; + } else { + return undefined; + } + } + + sendToAllDeviceWebsockets(msg: HmrServerMessage) { + const text = JSON.stringify(msg); + for (const socket of this.deviceWebsockets) { + socket.send(text); + } + } + + async getBundler( + url: ConsumableUrl, + ): Promise<{ + bundler: Bundler; + path: AbsoluteFilePath; + }> { + const pathname = stripBundleSuffix(String(url.path.asString())); + + const absolute = await this.pathnameToAbsolutePath(pathname); + if (absolute === undefined) { + throw new Error('Pathname is attempting to escalate out of cwd'); + } + + const pathPointer = url.path.getDiagnosticLocation(); + const path = await this.master.resolver.resolveEntryAssertPath( + { + origin: this.masterRequest.client.flags.cwd, + source: absolute, + }, + pathPointer === undefined ? undefined : {location: pathPointer}, + ); + + const platform = url.query.get('platform').asStringSetOrVoid(PLATFORMS); + const cacheKey = JSON.stringify({ + platform, + }); + + const cached = this.bundlerCache.get(cacheKey); + if (cached !== undefined) { + return {bundler: cached, path}; + } + + const bundlerConfig: BundlerConfig = this.masterRequest.getBundlerConfigFromFlags({ + platform, + }); + + const bundler = new Bundler(this.masterRequest, bundlerConfig); + this.bundlerCache.set(cacheKey, bundler); + return {bundler, path}; + } } diff --git a/packages/@romejs/core/test-worker/SnapshotManager.ts b/packages/@romejs/core/test-worker/SnapshotManager.ts index a95f5a9d329..35edcd61485 100644 --- a/packages/@romejs/core/test-worker/SnapshotManager.ts +++ b/packages/@romejs/core/test-worker/SnapshotManager.ts @@ -6,9 +6,9 @@ */ import { - AbsoluteFilePath, - AbsoluteFilePathMap, - createAbsoluteFilePath, + AbsoluteFilePath, + AbsoluteFilePathMap, + createAbsoluteFilePath, } from '@romejs/path'; import {exists, readFileText, unlink, writeFile} from '@romejs/fs'; import {TestMasterRunnerOptions} from '../master/testing/types'; @@ -20,412 +20,412 @@ import {Number0, Number1} from '@romejs/ob1'; import prettyFormat from '@romejs/pretty-format'; function cleanHeading(key: string): string { - if (key[0] === '`') { - key = key.slice(1); - } + if (key[0] === '`') { + key = key.slice(1); + } - if (key[key.length - 1] === '`') { - key = key.slice(0, -1); - } + if (key[key.length - 1] === '`') { + key = key.slice(0, -1); + } - return key.trim(); + return key.trim(); } type SnapshotEntry = { - testName: string; - entryName: string; - language: undefined | string; - value: string; + testName: string; + entryName: string; + language: undefined | string; + value: string; }; type Snapshot = { - existsOnDisk: boolean; - used: boolean; - raw: string; - entries: Map; + existsOnDisk: boolean; + used: boolean; + raw: string; + entries: Map; }; export const SNAPSHOT_EXT = '.test.md'; function buildEntriesKey(testName: string, entryName: string): string { - return `${testName}#${entryName}`; + return `${testName}#${entryName}`; } export type InlineSnapshotUpdate = { - line: Number1; - column: Number0; - snapshot: boolean | number | string | null | undefined; + line: Number1; + column: Number0; + snapshot: boolean | number | string | null | undefined; }; export type InlineSnapshotUpdates = Array; export type SnapshotCounts = { - deleted: number; - updated: number; - created: number; + deleted: number; + updated: number; + created: number; }; export default class SnapshotManager { - constructor(runner: TestWorkerRunner, testPath: AbsoluteFilePath) { - this.defaultSnapshotPath = testPath.getParent().append( - `${testPath.getExtensionlessBasename()}${SNAPSHOT_EXT}`, - ); - this.testPath = testPath; - this.runner = runner; - this.options = runner.options; - this.snapshots = new AbsoluteFilePathMap(); - this.inlineSnapshotsUpdates = []; - this.snapshotCounts = { - deleted: 0, - updated: 0, - created: 0, - }; - } - - inlineSnapshotsUpdates: Array; - testPath: AbsoluteFilePath; - defaultSnapshotPath: AbsoluteFilePath; - snapshots: AbsoluteFilePathMap; - runner: TestWorkerRunner; - options: TestMasterRunnerOptions; - snapshotCounts: SnapshotCounts; - - normalizeSnapshotPath(filename: undefined | string): AbsoluteFilePath { - if (filename === undefined) { - return this.defaultSnapshotPath; - } - - const path = createAbsoluteFilePath(filename); - const ext = path.getExtensions(); - if (ext.endsWith(SNAPSHOT_EXT)) { - return path; - } else { - return path.addExtension(SNAPSHOT_EXT); - } - } - - async init() { - await this.loadSnapshot(this.defaultSnapshotPath); - } - - async emitDiagnostic(metadata: DiagnosticDescription) { - await this.runner.emitDiagnostic({ - description: metadata, - location: { - filename: this.defaultSnapshotPath.join(), - }, - }); - } - - async loadSnapshot(path: AbsoluteFilePath): Promise { - if (!(await exists(path))) { - return; - } - - const content = await readFileText(path); - const parser = createSnapshotParser({ - path, - input: content, - }); - - const nodes = parser.parse(); - - const snapshot: Snapshot = { - existsOnDisk: true, - used: false, - raw: parser.input, - entries: new Map(), - }; - this.snapshots.set(path, snapshot); - - while (nodes.length > 0) { - const node = nodes.shift()!; - - if (node.type === 'Heading' && node.level === 1) { - // Title - continue; - } - - if (node.type === 'Heading' && node.level === 2) { - const testName = cleanHeading(node.text); - - while (nodes.length > 0) { - const node = nodes[0]; - - if (node.type === 'Heading' && node.level === 3) { - nodes.shift(); - - const entryName = cleanHeading(node.text); - - const codeBlock = nodes.shift(); - if (codeBlock === undefined || codeBlock.type !== 'CodeBlock') { - throw parser.unexpected({ - description: descriptions.SNAPSHOTS.EXPECTED_CODE_BLOCK_AFTER_HEADING, - loc: node.loc, - }); - } - - snapshot.entries.set( - buildEntriesKey(testName, entryName), - { - testName, - entryName, - language: codeBlock.language, - value: codeBlock.text, - }, - ); - - continue; - } - - if (node.type === 'CodeBlock') { - nodes.shift(); - - snapshot.entries.set( - buildEntriesKey(testName, '0'), - { - testName, - entryName: '0', - language: node.language, - value: node.text, - }, - ); - } - - break; - } - - continue; - } - } - - return snapshot; - } - - buildSnapshot(entries: Iterable): Array { - // Build the snapshot - let lines: Array = []; - - function pushNewline() { - if (lines[lines.length - 1] !== '') { - lines.push(''); - } - } - - lines.push(`# \`${this.testPath.getBasename()}\``); - pushNewline(); - const relativeTestPath = this.runner.projectFolder.relative(this.testPath).join(); - lines.push( - `**DO NOT MODIFY**. This file has been autogenerated. Run \`rome test ${relativeTestPath} --update-snapshots\` to update.`, - ); - pushNewline(); - - const testNameToEntries: Map> = new Map(); - for (const entry of entries) { - let entriesByTestName = testNameToEntries.get(entry.testName); - if (entriesByTestName === undefined) { - entriesByTestName = new Map(); - testNameToEntries.set(entry.testName, entriesByTestName); - } - entriesByTestName.set(entry.entryName, entry); - } - - // Get test names and sort them so they are in a predictable - const testNames = Array.from(testNameToEntries.keys()).sort(); - - for (const testName of testNames) { - const entries = testNameToEntries.get(testName)!; - - lines.push(`## \`${testName}\``); - pushNewline(); - - const entryNames = Array.from(entries.keys()).sort(); - - for (const snapshotName of entryNames) { - const entry = entries.get(snapshotName)!; - - const {value} = entry; - const language = entry.language === undefined ? '' : entry.language; - - // If the test only has one snapshot then omit the heading - const skipHeading = snapshotName === '0' && entryNames.length === 1; - if (!skipHeading) { - lines.push(`### \`${snapshotName}\``); - } - - pushNewline(); - lines.push('```' + language); - // TODO escape triple backquotes - lines.push(value); - lines.push('```'); - pushNewline(); - } - } - return lines; - } - - async save() { - // If there'a s focused test then we don't write or validate a snapshot - if (this.runner.hasFocusedTests) { - return; - } - - const {hasDiagnostics} = this.runner; - - for (const [path, {used, existsOnDisk, raw, entries}] of this.snapshots) { - const lines = this.buildSnapshot(entries.values()); - const formatted = lines.join('\n'); - - if (this.options.freezeSnapshots) { - if (used) { - if (formatted !== raw) { - await this.emitDiagnostic( - descriptions.SNAPSHOTS.INCORRECT(raw, formatted), - ); - } - } else { - await this.emitDiagnostic(descriptions.SNAPSHOTS.REDUNDANT); - } - } else { - if (existsOnDisk && !used) { - // Don't delete a snapshot if there are test failures as those failures may be hiding a snapshot usage - if (!hasDiagnostics) { - // If a snapshot wasn't used or is empty then delete it! - await unlink(path); - this.snapshotCounts.deleted++; - } - } else if (used && formatted !== raw) { - // Fresh snapshot! - await writeFile(path, formatted); - if (existsOnDisk) { - this.snapshotCounts.updated++; - } else { - this.snapshotCounts.created++; - } - } - } - } - } - - testInlineSnapshot( - callFrame: ErrorFrame, - received: unknown, - expected?: InlineSnapshotUpdate['snapshot'], - ): { - status: 'MATCH' | 'NO_MATCH' | 'UPDATE'; - } { - let receivedFormat = prettyFormat(received); - - let expectedFormat; - if (typeof expected === 'string') { - expectedFormat = expected; - } else { - expectedFormat = prettyFormat(expected); - } - - // Matches, no need to do anything - if (receivedFormat === expectedFormat) { - return {status: 'MATCH'}; - } - - const shouldSave = this.options.updateSnapshots || expected === undefined; - if (shouldSave) { - const {lineNumber, columnNumber} = callFrame; - if (lineNumber === undefined || columnNumber === undefined) { - throw new Error('Call frame has no line or column'); - } - - if (!this.options.freezeSnapshots) { - let snapshot: InlineSnapshotUpdate['snapshot'] = receivedFormat; - if ( - typeof received === 'string' || - typeof received === 'number' || - typeof received === 'boolean' || - received === undefined || - received === null - ) { - snapshot = received; - } - - this.inlineSnapshotsUpdates.push({ - line: lineNumber, - column: columnNumber, - snapshot, - }); - } - - return {status: 'UPDATE'}; - } - - return {status: 'NO_MATCH'}; - } - - async get( - testName: string, - entryName: string, - optionalFilename: undefined | string, - ): Promise { - const snapshotPath = this.normalizeSnapshotPath(optionalFilename); - let snapshot = this.snapshots.get(snapshotPath); - - if (snapshot === undefined) { - snapshot = await this.loadSnapshot(snapshotPath); - } - - if (snapshot === undefined) { - return undefined; - } - - snapshot.used = true; - - // If we're force updating, pretend that there was no entry - if (this.options.updateSnapshots) { - return undefined; - } - - const entry = snapshot.entries.get(buildEntriesKey(testName, entryName)); - if (entry === undefined) { - return undefined; - } else { - return entry.value; - } - } - - set( - { - testName, - entryName, - value, - language, - optionalFilename, - }: { - testName: string; - entryName: string; - value: string; - language: undefined | string; - optionalFilename: undefined | string; - }, - ) { - const snapshotPath = this.normalizeSnapshotPath(optionalFilename); - let snapshot = this.snapshots.get(snapshotPath); - if (snapshot === undefined) { - snapshot = { - raw: '', - existsOnDisk: false, - used: true, - entries: new Map(), - }; - this.snapshots.set(snapshotPath, snapshot); - } - - snapshot.entries.set( - buildEntriesKey(testName, entryName), - { - testName, - entryName, - language, - value, - }, - ); - } + constructor(runner: TestWorkerRunner, testPath: AbsoluteFilePath) { + this.defaultSnapshotPath = testPath.getParent().append( + `${testPath.getExtensionlessBasename()}${SNAPSHOT_EXT}`, + ); + this.testPath = testPath; + this.runner = runner; + this.options = runner.options; + this.snapshots = new AbsoluteFilePathMap(); + this.inlineSnapshotsUpdates = []; + this.snapshotCounts = { + deleted: 0, + updated: 0, + created: 0, + }; + } + + inlineSnapshotsUpdates: Array; + testPath: AbsoluteFilePath; + defaultSnapshotPath: AbsoluteFilePath; + snapshots: AbsoluteFilePathMap; + runner: TestWorkerRunner; + options: TestMasterRunnerOptions; + snapshotCounts: SnapshotCounts; + + normalizeSnapshotPath(filename: undefined | string): AbsoluteFilePath { + if (filename === undefined) { + return this.defaultSnapshotPath; + } + + const path = createAbsoluteFilePath(filename); + const ext = path.getExtensions(); + if (ext.endsWith(SNAPSHOT_EXT)) { + return path; + } else { + return path.addExtension(SNAPSHOT_EXT); + } + } + + async init() { + await this.loadSnapshot(this.defaultSnapshotPath); + } + + async emitDiagnostic(metadata: DiagnosticDescription) { + await this.runner.emitDiagnostic({ + description: metadata, + location: { + filename: this.defaultSnapshotPath.join(), + }, + }); + } + + async loadSnapshot(path: AbsoluteFilePath): Promise { + if (!(await exists(path))) { + return; + } + + const content = await readFileText(path); + const parser = createSnapshotParser({ + path, + input: content, + }); + + const nodes = parser.parse(); + + const snapshot: Snapshot = { + existsOnDisk: true, + used: false, + raw: parser.input, + entries: new Map(), + }; + this.snapshots.set(path, snapshot); + + while (nodes.length > 0) { + const node = nodes.shift()!; + + if (node.type === 'Heading' && node.level === 1) { + // Title + continue; + } + + if (node.type === 'Heading' && node.level === 2) { + const testName = cleanHeading(node.text); + + while (nodes.length > 0) { + const node = nodes[0]; + + if (node.type === 'Heading' && node.level === 3) { + nodes.shift(); + + const entryName = cleanHeading(node.text); + + const codeBlock = nodes.shift(); + if (codeBlock === undefined || codeBlock.type !== 'CodeBlock') { + throw parser.unexpected({ + description: descriptions.SNAPSHOTS.EXPECTED_CODE_BLOCK_AFTER_HEADING, + loc: node.loc, + }); + } + + snapshot.entries.set( + buildEntriesKey(testName, entryName), + { + testName, + entryName, + language: codeBlock.language, + value: codeBlock.text, + }, + ); + + continue; + } + + if (node.type === 'CodeBlock') { + nodes.shift(); + + snapshot.entries.set( + buildEntriesKey(testName, '0'), + { + testName, + entryName: '0', + language: node.language, + value: node.text, + }, + ); + } + + break; + } + + continue; + } + } + + return snapshot; + } + + buildSnapshot(entries: Iterable): Array { + // Build the snapshot + let lines: Array = []; + + function pushNewline() { + if (lines[lines.length - 1] !== '') { + lines.push(''); + } + } + + lines.push(`# \`${this.testPath.getBasename()}\``); + pushNewline(); + const relativeTestPath = this.runner.projectFolder.relative(this.testPath).join(); + lines.push( + `**DO NOT MODIFY**. This file has been autogenerated. Run \`rome test ${relativeTestPath} --update-snapshots\` to update.`, + ); + pushNewline(); + + const testNameToEntries: Map> = new Map(); + for (const entry of entries) { + let entriesByTestName = testNameToEntries.get(entry.testName); + if (entriesByTestName === undefined) { + entriesByTestName = new Map(); + testNameToEntries.set(entry.testName, entriesByTestName); + } + entriesByTestName.set(entry.entryName, entry); + } + + // Get test names and sort them so they are in a predictable + const testNames = Array.from(testNameToEntries.keys()).sort(); + + for (const testName of testNames) { + const entries = testNameToEntries.get(testName)!; + + lines.push(`## \`${testName}\``); + pushNewline(); + + const entryNames = Array.from(entries.keys()).sort(); + + for (const snapshotName of entryNames) { + const entry = entries.get(snapshotName)!; + + const {value} = entry; + const language = entry.language === undefined ? '' : entry.language; + + // If the test only has one snapshot then omit the heading + const skipHeading = snapshotName === '0' && entryNames.length === 1; + if (!skipHeading) { + lines.push(`### \`${snapshotName}\``); + } + + pushNewline(); + lines.push('```' + language); + // TODO escape triple backquotes + lines.push(value); + lines.push('```'); + pushNewline(); + } + } + return lines; + } + + async save() { + // If there'a s focused test then we don't write or validate a snapshot + if (this.runner.hasFocusedTests) { + return; + } + + const {hasDiagnostics} = this.runner; + + for (const [path, {used, existsOnDisk, raw, entries}] of this.snapshots) { + const lines = this.buildSnapshot(entries.values()); + const formatted = lines.join('\n'); + + if (this.options.freezeSnapshots) { + if (used) { + if (formatted !== raw) { + await this.emitDiagnostic( + descriptions.SNAPSHOTS.INCORRECT(raw, formatted), + ); + } + } else { + await this.emitDiagnostic(descriptions.SNAPSHOTS.REDUNDANT); + } + } else { + if (existsOnDisk && !used) { + // Don't delete a snapshot if there are test failures as those failures may be hiding a snapshot usage + if (!hasDiagnostics) { + // If a snapshot wasn't used or is empty then delete it! + await unlink(path); + this.snapshotCounts.deleted++; + } + } else if (used && formatted !== raw) { + // Fresh snapshot! + await writeFile(path, formatted); + if (existsOnDisk) { + this.snapshotCounts.updated++; + } else { + this.snapshotCounts.created++; + } + } + } + } + } + + testInlineSnapshot( + callFrame: ErrorFrame, + received: unknown, + expected?: InlineSnapshotUpdate['snapshot'], + ): { + status: 'MATCH' | 'NO_MATCH' | 'UPDATE'; + } { + let receivedFormat = prettyFormat(received); + + let expectedFormat; + if (typeof expected === 'string') { + expectedFormat = expected; + } else { + expectedFormat = prettyFormat(expected); + } + + // Matches, no need to do anything + if (receivedFormat === expectedFormat) { + return {status: 'MATCH'}; + } + + const shouldSave = this.options.updateSnapshots || expected === undefined; + if (shouldSave) { + const {lineNumber, columnNumber} = callFrame; + if (lineNumber === undefined || columnNumber === undefined) { + throw new Error('Call frame has no line or column'); + } + + if (!this.options.freezeSnapshots) { + let snapshot: InlineSnapshotUpdate['snapshot'] = receivedFormat; + if ( + typeof received === 'string' || + typeof received === 'number' || + typeof received === 'boolean' || + received === undefined || + received === null + ) { + snapshot = received; + } + + this.inlineSnapshotsUpdates.push({ + line: lineNumber, + column: columnNumber, + snapshot, + }); + } + + return {status: 'UPDATE'}; + } + + return {status: 'NO_MATCH'}; + } + + async get( + testName: string, + entryName: string, + optionalFilename: undefined | string, + ): Promise { + const snapshotPath = this.normalizeSnapshotPath(optionalFilename); + let snapshot = this.snapshots.get(snapshotPath); + + if (snapshot === undefined) { + snapshot = await this.loadSnapshot(snapshotPath); + } + + if (snapshot === undefined) { + return undefined; + } + + snapshot.used = true; + + // If we're force updating, pretend that there was no entry + if (this.options.updateSnapshots) { + return undefined; + } + + const entry = snapshot.entries.get(buildEntriesKey(testName, entryName)); + if (entry === undefined) { + return undefined; + } else { + return entry.value; + } + } + + set( + { + testName, + entryName, + value, + language, + optionalFilename, + }: { + testName: string; + entryName: string; + value: string; + language: undefined | string; + optionalFilename: undefined | string; + }, + ) { + const snapshotPath = this.normalizeSnapshotPath(optionalFilename); + let snapshot = this.snapshots.get(snapshotPath); + if (snapshot === undefined) { + snapshot = { + raw: '', + existsOnDisk: false, + used: true, + entries: new Map(), + }; + this.snapshots.set(snapshotPath, snapshot); + } + + snapshot.entries.set( + buildEntriesKey(testName, entryName), + { + testName, + entryName, + language, + value, + }, + ); + } } diff --git a/packages/@romejs/core/test-worker/SnapshotParser.ts b/packages/@romejs/core/test-worker/SnapshotParser.ts index bca1494e109..d4aa106d55e 100644 --- a/packages/@romejs/core/test-worker/SnapshotParser.ts +++ b/packages/@romejs/core/test-worker/SnapshotParser.ts @@ -6,203 +6,200 @@ */ import { - BaseTokens, - NodeBase, - ParserOptions, - ValueToken, - createParser, + BaseTokens, + NodeBase, + ParserOptions, + ValueToken, + createParser, } from '@romejs/parser-core'; import {isEscaped} from '@romejs/string-utils'; import {Number0, ob1Add, ob1Get0} from '@romejs/ob1'; import {descriptions} from '@romejs/diagnostics'; type Tokens = BaseTokens & { - Hashes: ValueToken<'Hashes', number>; - CodeBlock: ValueToken< - 'CodeBlock', - { - text: string; - language: undefined | string; - } - >; - TextLine: ValueToken<'TextLine', string>; + Hashes: ValueToken<'Hashes', number>; + CodeBlock: ValueToken< + 'CodeBlock', + { + text: string; + language: undefined | string; + } + >; + TextLine: ValueToken<'TextLine', string>; }; type HeadingNode = NodeBase & { - type: 'Heading'; - text: string; - level: number; + type: 'Heading'; + text: string; + level: number; }; type CodeBlockNode = NodeBase & { - type: 'CodeBlock'; - language: undefined | string; - text: string; + type: 'CodeBlock'; + language: undefined | string; + text: string; }; type TextLineNode = NodeBase & { - type: 'TextLine'; - text: string; + type: 'TextLine'; + text: string; }; type Node = HeadingNode | CodeBlockNode | TextLineNode; function isHash(char: string): boolean { - return char === '#'; + return char === '#'; } function isCodeBlockEnd(index: Number0, input: string): boolean { - return ( - input[ob1Get0(index)] === '`' && - !isEscaped(index, input) && - input[ob1Get0(ob1Add(index, 1))] === '`' && - input[ob1Get0(ob1Add(index, 2))] === '`' - ); + return ( + input[ob1Get0(index)] === '`' && + !isEscaped(index, input) && + input[ob1Get0(ob1Add(index, 1))] === '`' && + input[ob1Get0(ob1Add(index, 2))] === '`' + ); } function isInCodeBlock(char: string, index: Number0, input: string): boolean { - return !isCodeBlockEnd(index, input); + return !isCodeBlockEnd(index, input); } function isntNewline(char: string): boolean { - return char !== '\n'; + return char !== '\n'; } function unescapeTicks(code: string): string { - return code; + return code; } export const createSnapshotParser = createParser((ParserCore) => - class SnapshotParser extends ParserCore { - constructor(opts: ParserOptions) { - super(opts, 'parse/snapshots'); - this.ignoreWhitespaceTokens = true; - } - - tokenize(index: Number0, input: string) { - const char = input[ob1Get0(index)]; - - switch (char) { - case '#': { - const [hashes] = this.readInputFrom(index, isHash); - const level = hashes.length; - return this.finishValueToken('Hashes', level, ob1Add(index, level)); - } - - case '`': { - const nextChar = input[ob1Get0(ob1Add(index, 1))]; - const nextNextChar = input[ob1Get0(ob1Add(index, 2))]; - - if (nextChar === '`' && nextNextChar === '`') { - let codeOffset = ob1Add(index, 3); - - let language: undefined | string; - if (input[ob1Get0(codeOffset)] !== '\n') { - [language, codeOffset] = this.readInputFrom( - codeOffset, - isntNewline, - ); - } - - // Expect the first offset character to be a newline - if (input[ob1Get0(codeOffset)] === '\n') { - // Skip leading newline - codeOffset = ob1Add(codeOffset, 1); - } else { - throw this.unexpected({ - description: descriptions.SNAPSHOTS.MISSING_NEWLINE_AFTER_CODE_BLOCK, - start: this.getPositionFromIndex(codeOffset), - }); - } - - let [code] = this.readInputFrom(codeOffset, isInCodeBlock); - - let end = ob1Add(codeOffset, code.length); - - if (isCodeBlockEnd(end, input)) { - // Check for trailing newline - if (code[code.length - 1] === '\n') { - // Trim trailing newline - code = code.slice(0, -1); - - // Skip closing ticks - end = ob1Add(end, 3); - - return this.finishValueToken( - 'CodeBlock', - { - language, - text: unescapeTicks(code), - }, - end, - ); - } else { - throw this.unexpected({ - description: descriptions.SNAPSHOTS.MISSING_NEWLINE_BEFORE_CODE_BLOCK, - start: this.getPositionFromIndex(end), - }); - } - } else { - throw this.unexpected({ - description: descriptions.SNAPSHOTS.UNCLOSED_CODE_BLOCK, - start: this.getPositionFromIndex(end), - }); - } - } - } - } - - const [text, end] = this.readInputFrom(index, isntNewline); - return this.finishValueToken('TextLine', text, end); - } - - parse(): Array { - const nodes: Array = []; - - while (!this.matchToken('EOF')) { - const start = this.getPosition(); - const token = this.getToken(); - - switch (token.type) { - case 'Hashes': { - const level = token.value; - this.nextToken(); - const text = this.expectToken('TextLine').value; - nodes.push({ - type: 'Heading', - level, - text, - loc: this.finishLoc(start), - }); - break; - } - - case 'CodeBlock': { - nodes.push({ - type: 'CodeBlock', - ...token.value, - loc: this.finishLoc(start), - }); - this.nextToken(); - break; - } - - case 'TextLine': { - nodes.push({ - type: 'TextLine', - text: token.value, - loc: this.finishLoc(start), - }); - this.nextToken(); - break; - } - - default: - throw this.unexpected(); - } - } - - return nodes; - } - } + class SnapshotParser extends ParserCore { + constructor(opts: ParserOptions) { + super(opts, 'parse/snapshots'); + this.ignoreWhitespaceTokens = true; + } + + tokenize(index: Number0, input: string) { + const char = input[ob1Get0(index)]; + + switch (char) { + case '#': { + const [hashes] = this.readInputFrom(index, isHash); + const level = hashes.length; + return this.finishValueToken('Hashes', level, ob1Add(index, level)); + } + + case '`': { + const nextChar = input[ob1Get0(ob1Add(index, 1))]; + const nextNextChar = input[ob1Get0(ob1Add(index, 2))]; + + if (nextChar === '`' && nextNextChar === '`') { + let codeOffset = ob1Add(index, 3); + + let language: undefined | string; + if (input[ob1Get0(codeOffset)] !== '\n') { + [language, codeOffset] = this.readInputFrom(codeOffset, isntNewline); + } + + // Expect the first offset character to be a newline + if (input[ob1Get0(codeOffset)] === '\n') { + // Skip leading newline + codeOffset = ob1Add(codeOffset, 1); + } else { + throw this.unexpected({ + description: descriptions.SNAPSHOTS.MISSING_NEWLINE_AFTER_CODE_BLOCK, + start: this.getPositionFromIndex(codeOffset), + }); + } + + let [code] = this.readInputFrom(codeOffset, isInCodeBlock); + + let end = ob1Add(codeOffset, code.length); + + if (isCodeBlockEnd(end, input)) { + // Check for trailing newline + if (code[code.length - 1] === '\n') { + // Trim trailing newline + code = code.slice(0, -1); + + // Skip closing ticks + end = ob1Add(end, 3); + + return this.finishValueToken( + 'CodeBlock', + { + language, + text: unescapeTicks(code), + }, + end, + ); + } else { + throw this.unexpected({ + description: descriptions.SNAPSHOTS.MISSING_NEWLINE_BEFORE_CODE_BLOCK, + start: this.getPositionFromIndex(end), + }); + } + } else { + throw this.unexpected({ + description: descriptions.SNAPSHOTS.UNCLOSED_CODE_BLOCK, + start: this.getPositionFromIndex(end), + }); + } + } + } + } + + const [text, end] = this.readInputFrom(index, isntNewline); + return this.finishValueToken('TextLine', text, end); + } + + parse(): Array { + const nodes: Array = []; + + while (!this.matchToken('EOF')) { + const start = this.getPosition(); + const token = this.getToken(); + + switch (token.type) { + case 'Hashes': { + const level = token.value; + this.nextToken(); + const text = this.expectToken('TextLine').value; + nodes.push({ + type: 'Heading', + level, + text, + loc: this.finishLoc(start), + }); + break; + } + + case 'CodeBlock': { + nodes.push({ + type: 'CodeBlock', + ...token.value, + loc: this.finishLoc(start), + }); + this.nextToken(); + break; + } + + case 'TextLine': { + nodes.push({ + type: 'TextLine', + text: token.value, + loc: this.finishLoc(start), + }); + this.nextToken(); + break; + } + + default: + throw this.unexpected(); + } + } + + return nodes; + } + } ); diff --git a/packages/@romejs/core/test-worker/TestAPI.ts b/packages/@romejs/core/test-worker/TestAPI.ts index b458fc1d6f9..93c8ed610cb 100644 --- a/packages/@romejs/core/test-worker/TestAPI.ts +++ b/packages/@romejs/core/test-worker/TestAPI.ts @@ -6,13 +6,13 @@ */ import { - Diagnostic, - DiagnosticAdvice, - createBlessedDiagnosticMessage, - createSingleDiagnosticError, - deriveDiagnosticFromErrorStructure, - descriptions, - getErrorStackAdvice, + Diagnostic, + DiagnosticAdvice, + createBlessedDiagnosticMessage, + createSingleDiagnosticError, + deriveDiagnosticFromErrorStructure, + descriptions, + getErrorStackAdvice, } from '@romejs/diagnostics'; import SnapshotManager from './SnapshotManager'; import {TestMasterRunnerOptions} from '../master/testing/types'; @@ -23,744 +23,738 @@ import prettyFormat from '@romejs/pretty-format'; import {FileReference} from '../common/types/files'; import {escapeMarkup, markup} from '@romejs/string-markup'; import { - AsyncFunc, - ExpectedError, - SyncThrower, - TestDiagnosticAdviceItem, - TestHelper, - TestSnapshotOptions, + AsyncFunc, + ExpectedError, + SyncThrower, + TestDiagnosticAdviceItem, + TestHelper, + TestSnapshotOptions, } from '@romejs-runtime/rome/test'; function formatExpectedError(expected: ExpectedError): string { - if (typeof expected === 'string') { - return JSON.stringify(expected); - } + if (typeof expected === 'string') { + return JSON.stringify(expected); + } - if (expected instanceof RegExp) { - return String(expected); - } + if (expected instanceof RegExp) { + return String(expected); + } - if (typeof expected === 'function') { - return expected.name; - } + if (typeof expected === 'function') { + return expected.name; + } - return 'unknown'; + return 'unknown'; } function matchExpectedError(error: Error, expected: ExpectedError): boolean { - if (expected === undefined) { - return true; - } + if (expected === undefined) { + return true; + } - if (typeof expected === 'string') { - return error.message.includes(expected); - } + if (typeof expected === 'string') { + return error.message.includes(expected); + } - if (expected instanceof RegExp) { - return expected.test(error.message); - } + if (expected instanceof RegExp) { + return expected.test(error.message); + } - if (typeof expected === 'function') { - return error instanceof expected; - } + if (typeof expected === 'function') { + return error instanceof expected; + } - return false; + return false; } export type OnTimeout = (time: number) => void; type SnapshotOptions = { - entryName: string; - expected: unknown; - message?: string; - opts?: TestSnapshotOptions; + entryName: string; + expected: unknown; + message?: string; + opts?: TestSnapshotOptions; }; type EmitDiagnostic = (diag: Diagnostic) => Promise; export default class TestAPI implements TestHelper { - constructor( - { - testName, - onTimeout, - file, - snapshotManager, - options, - emitDiagnostic, - }: { - emitDiagnostic: EmitDiagnostic; - file: FileReference; - testName: string; - onTimeout: OnTimeout; - snapshotManager: SnapshotManager; - options: TestMasterRunnerOptions; - }, - ) { - this.testName = testName; - this.options = options; - this.snapshotManager = snapshotManager; - this.snapshotCounter = 0; - this.file = file; - this.teardownEvent = new Event({name: 'TestAPI.teardown'}); - this.startTime = Date.now(); - this.onTimeout = onTimeout; - this.emitDiagnostic = emitDiagnostic; - this.timeoutMax = 0; - this.timeoutId = undefined; - this.setTimeout(5_000); - this.advice = []; - } - - startTime: number; - options: TestMasterRunnerOptions; - file: FileReference; - emitDiagnostic: EmitDiagnostic; - - onTimeout: OnTimeout; - timeoutId: undefined | NodeJS.Timeout; - timeoutStart: undefined | number; - timeoutMax: undefined | number; - - advice: DiagnosticAdvice; - teardownEvent: Event; - testName: string; - snapshotCounter: number; - snapshotManager: SnapshotManager; - - buildMatchAdvice( - received: unknown, - expected: unknown, - { - visualMethod, - expectedAlias, - receivedAlias, - }: { - visualMethod?: string; - expectedAlias?: string; - receivedAlias?: string; - } = {}, - ): DiagnosticAdvice { - let expectedFormat; - let receivedFormat; - if (typeof received === 'string' && typeof expected === 'string') { - expectedFormat = expected; - receivedFormat = received; - } else { - expectedFormat = prettyFormat(expected); - receivedFormat = prettyFormat(received); - } - - const advice: DiagnosticAdvice = []; - - if (expectedFormat === receivedFormat) { - // Better error message when both values are visually identical - advice.push({ - type: 'log', - category: 'info', - text: `Both the received and expected values are visually identical`, - }); - - advice.push({ - type: 'code', - code: expectedFormat, - }); - - if (visualMethod !== undefined) { - advice.push({ - type: 'log', - category: 'info', - text: `Try using t.${visualMethod} if you wanted a visual match`, - }); - } - } else { - const bothSingleLine = - !expectedFormat.match(/\n/g) && !receivedFormat.match(/\n/g); - - if (!bothSingleLine) { - advice.push({ - type: 'log', - category: 'info', - text: `Expected to receive`, - }); - - advice.push({ - type: 'code', - code: expectedFormat, - }); - - advice.push({ - type: 'log', - category: 'info', - text: `But got`, - }); - - advice.push({ - type: 'code', - code: receivedFormat, - }); - - advice.push({ - type: 'log', - category: 'info', - text: 'Diff', - }); - } - - advice.push({ - type: 'diff', - diff: stringDiff(expectedFormat, receivedFormat), - legend: { - add: receivedAlias ? receivedAlias : 'Received', - delete: expectedAlias ? expectedAlias : 'Expected', - }, - }); - } - - return advice; - } - - addToAdvice(item: TestDiagnosticAdviceItem): void { - this.advice.push(item); - } - - clearAdvice() { - this.advice = []; - } - - onTeardown(callback: AsyncFunc): void { - this.teardownEvent.subscribe(callback); - } - - clearTimeout(): void { - if (this.timeoutId !== undefined) { - clearTimeout(this.timeoutId); - } - - this.timeoutMax = undefined; - this.timeoutStart = undefined; - } - - extendTimeout(time: number): void { - const {timeoutMax, timeoutStart} = this; - if (timeoutMax === undefined || timeoutStart === undefined) { - throw new Error('No timeout set'); - } - - const elapsed = Date.now() - timeoutStart; - const newTime = timeoutMax - elapsed + time; - this.setTimeout(newTime); - } - - setTimeout(time: number): void { - this.clearTimeout(); - - this.timeoutStart = Date.now(); - this.timeoutMax = time; - - this.timeoutId = setTimeout( - () => { - this.onTimeout(time); - }, - time, - ); - } - - checkTimeout(): void { - const {startTime, timeoutMax} = this; - if (timeoutMax === undefined) { - return; - } - - const delta = Date.now() - startTime; - if (delta > timeoutMax) { - throw new Error(`Test timeout - exceeded ${String(timeoutMax)}ms`); - } - } - - fail( - message: string = 'Test failure triggered by t.fail()', - advice: DiagnosticAdvice = [], - framesToShift: number = 0, - ): never { - const diag = deriveDiagnosticFromErrorStructure( - getErrorStructure(new Error(), framesToShift + 1), - { - description: { - category: 'tests/failure', - message: createBlessedDiagnosticMessage(message), - advice, - }, - }, - ); - throw createSingleDiagnosticError(diag); - } - - truthy(value: unknown, message: string = 'Expected value to be truthy'): void { - if (Boolean(value) === false) { - this.fail( - message, - [ - { - type: 'log', - category: 'info', - text: `Received`, - }, - { - type: 'code', - code: prettyFormat(value), - }, - ], - 1, - ); - } - } - - falsy(value: unknown, message: string = 'Expected value to be falsy'): void { - if (Boolean(value) === true) { - this.fail( - message, - [ - { - type: 'log', - category: 'info', - text: `Received`, - }, - { - type: 'code', - code: prettyFormat(value), - }, - ], - 1, - ); - } - } - - true(value: unknown, message: string = 'Expected value to be true'): void { - if (value !== true) { - this.fail( - message, - [ - { - type: 'log', - category: 'info', - text: `Received`, - }, - { - type: 'code', - code: prettyFormat(value), - }, - ], - 1, - ); - } - } - - false(value: unknown, message: string = 'Expected value to be false'): void { - if (value !== false) { - this.fail( - message, - [ - { - type: 'log', - category: 'info', - text: `Received`, - }, - { - type: 'code', - code: prettyFormat(value), - }, - ], - 1, - ); - } - } - - is( - received: unknown, - expected: unknown, - message: string = 't.is() failed, using Object.is semantics', - ): void { - if (Object.is(received, expected) !== true) { - this.fail( - message, - this.buildMatchAdvice( - received, - expected, - { - visualMethod: 'looksLike', - }, - ), - 1, - ); - } - } - - not( - received: unknown, - expected: unknown, - message: string = 't.not() failed, using !Object.is() semantics', - ): void { - if (Object.is(received, expected) === true) { - this.fail( - message, - this.buildMatchAdvice( - received, - expected, - { - visualMethod: 'notLooksLike', - }, - ), - 1, - ); - } - } - - looksLike( - received: unknown, - expected: unknown, - message: string = 't.looksLike() failed, using prettyFormat semantics', - ): void { - const actualInspect = prettyFormat(received); - const expectedInspect = prettyFormat(expected); - - if (actualInspect !== expectedInspect) { - this.fail(message, this.buildMatchAdvice(received, expected), 1); - } - } - - notLooksLike( - received: unknown, - expected: unknown, - message: string = 't.notLooksLike() failed, using !prettyFormat semantics', - ): void { - const actualInspect = prettyFormat(received); - const expectedInspect = prettyFormat(expected); - - if (actualInspect === expectedInspect) { - this.fail(message, this.buildMatchAdvice(received, expected), 1); - } - } - - throws( - thrower: SyncThrower, - expected?: ExpectedError, - message: string = 't.throws() failed, callback did not throw an error', - ): void { - try { - thrower(); - } catch (err) { - if (matchExpectedError(err, expected)) { - return undefined; - } else { - this.fail( - `t.throws() expected an error to be thrown that matches ${formatExpectedError( - expected, - )} but got ${err.name}: ${JSON.stringify(err.message)}`, - getErrorStackAdvice( - getErrorStructure(err), - 'Incorrect error stack trace', - ), - 1, - ); - } - } - - this.fail(message, undefined, 1); - } - - async throwsAsync( - thrower: AsyncFunc, - expected?: ExpectedError, - message: string = 't.throws() failed, callback did not throw an error', - ): Promise { - try { - await thrower(); - } catch (err) { - if (matchExpectedError(err, expected)) { - return undefined; - } else { - this.fail( - `t.throws() expected an error to be thrown that matches ${formatExpectedError( - expected, - )} but got ${err.name}: ${JSON.stringify(err.message)}`, - getErrorStackAdvice( - getErrorStructure(err), - 'Incorrect error stack trace', - ), - 1, - ); - } - } - this.fail(message, undefined, 1); - } - - notThrows( - nonThrower: SyncThrower, - message: string = 't.notThrows() failed, callback threw an error', - ): void { - try { - nonThrower(); - } catch (err) { - const advice = getErrorStackAdvice( - getErrorStructure(err), - `t.notThrows did not expect an error to be thrown but got ${err.name}: ${JSON.stringify( - err.message, - )}`, - ); - this.fail(message, advice, 1); - } - } - - async notThrowsAsync( - nonThrower: AsyncFunc, - message: string = 't.notThrowsAsync failed, callback threw an error', - ): Promise { - try { - await nonThrower(); - } catch (err) { - const advice = getErrorStackAdvice( - getErrorStructure(err), - `t.notThrowsAsync did not expect an error to be thrown but got ${err.name}: ${JSON.stringify( - err.message, - )}`, - ); - this.fail(message, advice, 1); - } - } - - regex( - contents: string, - regex: RegExp, - message: string = 't.regex failed, using RegExp.test semantics', - ): void { - if (!regex.test(contents)) { - this.fail( - message, - [ - { - type: 'log', - category: 'info', - text: `Expected`, - }, - { - type: 'code', - code: prettyFormat(contents), - }, - { - type: 'log', - category: 'info', - text: `to match pattern`, - }, - { - type: 'code', - code: prettyFormat(regex.source), - }, - ], - 1, - ); - } - } - - notRegex( - contents: string, - regex: RegExp, - message: string = 't.regex failed, using RegExp.test semantics', - ): void { - if (regex.test(contents)) { - this.fail( - message, - [ - { - type: 'log', - category: 'info', - text: `Expected`, - }, - { - type: 'code', - code: prettyFormat(contents), - }, - { - type: 'log', - category: 'info', - text: `to not match pattern`, - }, - { - type: 'code', - code: prettyFormat(regex.source), - }, - ], - 1, - ); - } - } - - inlineSnapshot(received: unknown, snapshot?: string | boolean | number) { - const callFrame = getErrorStructure(new Error()).frames[1]; - const callError = getErrorStructure(new Error(), 1); - - this.onTeardown(async () => { - const {status} = this.snapshotManager.testInlineSnapshot( - callFrame, - received, - snapshot, - ); - - if (status === 'UPDATE' && this.options.freezeSnapshots) { - await this.emitDiagnostic( - deriveDiagnosticFromErrorStructure( - callError, - { - description: descriptions.SNAPSHOTS.INLINE_FROZEN, - }, - ), - ); - } - - if (status === 'NO_MATCH') { - await this.emitDiagnostic( - deriveDiagnosticFromErrorStructure( - callError, - { - description: { - ...descriptions.SNAPSHOTS.INLINE_BAD_MATCH, - advice: this.buildMatchAdvice( - received, - snapshot, - { - receivedAlias: 'What the code gave us', - expectedAlias: 'Existing inline snapshot', - }, - ), - }, - }, - ), - ); - } - }); - } - - snapshot( - expected: unknown, - message?: string, - opts?: TestSnapshotOptions, - ): string { - const id = this.snapshotCounter++; - return this.bufferSnapshot({ - entryName: String(id), - expected, - message, - opts, - }); - } - - namedSnapshot( - entryName: string, - expected: unknown, - message?: string, - opts?: TestSnapshotOptions, - ): string { - return this.bufferSnapshot({ - entryName, - expected, - message, - opts, - }); - } - - bufferSnapshot( - { - entryName, - message, - expected, - opts = {}, - }: SnapshotOptions, - ): string { - let language: undefined | string = opts.language; - - let formatted = ''; - if (typeof expected === 'string') { - formatted = expected; - } else { - language = 'javascript'; - formatted = prettyFormat(expected); - } - - const callError = getErrorStructure(new Error(), 2); - - this.onTeardown(async () => { - // Get the current snapshot - const existingSnapshot = await this.snapshotManager.get( - this.testName, - entryName, - opts.filename, - ); - if (existingSnapshot === undefined) { - if (this.options.freezeSnapshots) { - await this.emitDiagnostic( - deriveDiagnosticFromErrorStructure( - callError, - { - description: descriptions.SNAPSHOTS.FROZEN, - }, - ), - ); - } else { - // No snapshot exists, let's save this one! - this.snapshotManager.set({ - testName: this.testName, - entryName, - value: formatted, - language, - optionalFilename: opts.filename, - }); - } - return; - } - - // Compare the snapshots - if (formatted !== existingSnapshot) { - const advice: DiagnosticAdvice = this.buildMatchAdvice( - formatted, - existingSnapshot, - { - receivedAlias: 'What the code gave us', - expectedAlias: 'Existing snapshot', - }, - ); - - if (message === undefined) { - message = markup`Snapshot ${entryName} at doesn't match`; - } else { - message = escapeMarkup(message); - - advice.push({ - type: 'log', - category: 'info', - text: markup`Snapshot can be found at `, - }); - } - - advice.push({ - type: 'log', - category: 'info', - text: markup`Run rome test --update-snapshots to update this snapshot`, - }); - - await this.emitDiagnostic( - deriveDiagnosticFromErrorStructure( - callError, - { - description: { - category: 'tests/snapshots/incorrect', - message: createBlessedDiagnosticMessage(message), - advice, - }, - }, - ), - ); - } - }); - - return entryName; - } + constructor( + { + testName, + onTimeout, + file, + snapshotManager, + options, + emitDiagnostic, + }: { + emitDiagnostic: EmitDiagnostic; + file: FileReference; + testName: string; + onTimeout: OnTimeout; + snapshotManager: SnapshotManager; + options: TestMasterRunnerOptions; + }, + ) { + this.testName = testName; + this.options = options; + this.snapshotManager = snapshotManager; + this.snapshotCounter = 0; + this.file = file; + this.teardownEvent = new Event({name: 'TestAPI.teardown'}); + this.startTime = Date.now(); + this.onTimeout = onTimeout; + this.emitDiagnostic = emitDiagnostic; + this.timeoutMax = 0; + this.timeoutId = undefined; + this.setTimeout(5_000); + this.advice = []; + } + + startTime: number; + options: TestMasterRunnerOptions; + file: FileReference; + emitDiagnostic: EmitDiagnostic; + + onTimeout: OnTimeout; + timeoutId: undefined | NodeJS.Timeout; + timeoutStart: undefined | number; + timeoutMax: undefined | number; + + advice: DiagnosticAdvice; + teardownEvent: Event; + testName: string; + snapshotCounter: number; + snapshotManager: SnapshotManager; + + buildMatchAdvice( + received: unknown, + expected: unknown, + { + visualMethod, + expectedAlias, + receivedAlias, + }: { + visualMethod?: string; + expectedAlias?: string; + receivedAlias?: string; + } = {}, + ): DiagnosticAdvice { + let expectedFormat; + let receivedFormat; + if (typeof received === 'string' && typeof expected === 'string') { + expectedFormat = expected; + receivedFormat = received; + } else { + expectedFormat = prettyFormat(expected); + receivedFormat = prettyFormat(received); + } + + const advice: DiagnosticAdvice = []; + + if (expectedFormat === receivedFormat) { + // Better error message when both values are visually identical + advice.push({ + type: 'log', + category: 'info', + text: `Both the received and expected values are visually identical`, + }); + + advice.push({ + type: 'code', + code: expectedFormat, + }); + + if (visualMethod !== undefined) { + advice.push({ + type: 'log', + category: 'info', + text: `Try using t.${visualMethod} if you wanted a visual match`, + }); + } + } else { + const bothSingleLine = + !expectedFormat.match(/\n/g) && !receivedFormat.match(/\n/g); + + if (!bothSingleLine) { + advice.push({ + type: 'log', + category: 'info', + text: `Expected to receive`, + }); + + advice.push({ + type: 'code', + code: expectedFormat, + }); + + advice.push({ + type: 'log', + category: 'info', + text: `But got`, + }); + + advice.push({ + type: 'code', + code: receivedFormat, + }); + + advice.push({ + type: 'log', + category: 'info', + text: 'Diff', + }); + } + + advice.push({ + type: 'diff', + diff: stringDiff(expectedFormat, receivedFormat), + legend: { + add: receivedAlias ? receivedAlias : 'Received', + delete: expectedAlias ? expectedAlias : 'Expected', + }, + }); + } + + return advice; + } + + addToAdvice(item: TestDiagnosticAdviceItem): void { + this.advice.push(item); + } + + clearAdvice() { + this.advice = []; + } + + onTeardown(callback: AsyncFunc): void { + this.teardownEvent.subscribe(callback); + } + + clearTimeout(): void { + if (this.timeoutId !== undefined) { + clearTimeout(this.timeoutId); + } + + this.timeoutMax = undefined; + this.timeoutStart = undefined; + } + + extendTimeout(time: number): void { + const {timeoutMax, timeoutStart} = this; + if (timeoutMax === undefined || timeoutStart === undefined) { + throw new Error('No timeout set'); + } + + const elapsed = Date.now() - timeoutStart; + const newTime = timeoutMax - elapsed + time; + this.setTimeout(newTime); + } + + setTimeout(time: number): void { + this.clearTimeout(); + + this.timeoutStart = Date.now(); + this.timeoutMax = time; + + this.timeoutId = setTimeout( + () => { + this.onTimeout(time); + }, + time, + ); + } + + checkTimeout(): void { + const {startTime, timeoutMax} = this; + if (timeoutMax === undefined) { + return; + } + + const delta = Date.now() - startTime; + if (delta > timeoutMax) { + throw new Error(`Test timeout - exceeded ${String(timeoutMax)}ms`); + } + } + + fail( + message: string = 'Test failure triggered by t.fail()', + advice: DiagnosticAdvice = [], + framesToShift: number = 0, + ): never { + const diag = deriveDiagnosticFromErrorStructure( + getErrorStructure(new Error(), framesToShift + 1), + { + description: { + category: 'tests/failure', + message: createBlessedDiagnosticMessage(message), + advice, + }, + }, + ); + throw createSingleDiagnosticError(diag); + } + + truthy(value: unknown, message: string = 'Expected value to be truthy'): void { + if (Boolean(value) === false) { + this.fail( + message, + [ + { + type: 'log', + category: 'info', + text: `Received`, + }, + { + type: 'code', + code: prettyFormat(value), + }, + ], + 1, + ); + } + } + + falsy(value: unknown, message: string = 'Expected value to be falsy'): void { + if (Boolean(value) === true) { + this.fail( + message, + [ + { + type: 'log', + category: 'info', + text: `Received`, + }, + { + type: 'code', + code: prettyFormat(value), + }, + ], + 1, + ); + } + } + + true(value: unknown, message: string = 'Expected value to be true'): void { + if (value !== true) { + this.fail( + message, + [ + { + type: 'log', + category: 'info', + text: `Received`, + }, + { + type: 'code', + code: prettyFormat(value), + }, + ], + 1, + ); + } + } + + false(value: unknown, message: string = 'Expected value to be false'): void { + if (value !== false) { + this.fail( + message, + [ + { + type: 'log', + category: 'info', + text: `Received`, + }, + { + type: 'code', + code: prettyFormat(value), + }, + ], + 1, + ); + } + } + + is( + received: unknown, + expected: unknown, + message: string = 't.is() failed, using Object.is semantics', + ): void { + if (Object.is(received, expected) !== true) { + this.fail( + message, + this.buildMatchAdvice( + received, + expected, + { + visualMethod: 'looksLike', + }, + ), + 1, + ); + } + } + + not( + received: unknown, + expected: unknown, + message: string = 't.not() failed, using !Object.is() semantics', + ): void { + if (Object.is(received, expected) === true) { + this.fail( + message, + this.buildMatchAdvice( + received, + expected, + { + visualMethod: 'notLooksLike', + }, + ), + 1, + ); + } + } + + looksLike( + received: unknown, + expected: unknown, + message: string = 't.looksLike() failed, using prettyFormat semantics', + ): void { + const actualInspect = prettyFormat(received); + const expectedInspect = prettyFormat(expected); + + if (actualInspect !== expectedInspect) { + this.fail(message, this.buildMatchAdvice(received, expected), 1); + } + } + + notLooksLike( + received: unknown, + expected: unknown, + message: string = 't.notLooksLike() failed, using !prettyFormat semantics', + ): void { + const actualInspect = prettyFormat(received); + const expectedInspect = prettyFormat(expected); + + if (actualInspect === expectedInspect) { + this.fail(message, this.buildMatchAdvice(received, expected), 1); + } + } + + throws( + thrower: SyncThrower, + expected?: ExpectedError, + message: string = 't.throws() failed, callback did not throw an error', + ): void { + try { + thrower(); + } catch (err) { + if (matchExpectedError(err, expected)) { + return undefined; + } else { + this.fail( + `t.throws() expected an error to be thrown that matches ${formatExpectedError( + expected, + )} but got ${err.name}: ${JSON.stringify(err.message)}`, + getErrorStackAdvice(getErrorStructure(err), 'Incorrect error stack trace'), + 1, + ); + } + } + + this.fail(message, undefined, 1); + } + + async throwsAsync( + thrower: AsyncFunc, + expected?: ExpectedError, + message: string = 't.throws() failed, callback did not throw an error', + ): Promise { + try { + await thrower(); + } catch (err) { + if (matchExpectedError(err, expected)) { + return undefined; + } else { + this.fail( + `t.throws() expected an error to be thrown that matches ${formatExpectedError( + expected, + )} but got ${err.name}: ${JSON.stringify(err.message)}`, + getErrorStackAdvice(getErrorStructure(err), 'Incorrect error stack trace'), + 1, + ); + } + } + this.fail(message, undefined, 1); + } + + notThrows( + nonThrower: SyncThrower, + message: string = 't.notThrows() failed, callback threw an error', + ): void { + try { + nonThrower(); + } catch (err) { + const advice = getErrorStackAdvice( + getErrorStructure(err), + `t.notThrows did not expect an error to be thrown but got ${err.name}: ${JSON.stringify( + err.message, + )}`, + ); + this.fail(message, advice, 1); + } + } + + async notThrowsAsync( + nonThrower: AsyncFunc, + message: string = 't.notThrowsAsync failed, callback threw an error', + ): Promise { + try { + await nonThrower(); + } catch (err) { + const advice = getErrorStackAdvice( + getErrorStructure(err), + `t.notThrowsAsync did not expect an error to be thrown but got ${err.name}: ${JSON.stringify( + err.message, + )}`, + ); + this.fail(message, advice, 1); + } + } + + regex( + contents: string, + regex: RegExp, + message: string = 't.regex failed, using RegExp.test semantics', + ): void { + if (!regex.test(contents)) { + this.fail( + message, + [ + { + type: 'log', + category: 'info', + text: `Expected`, + }, + { + type: 'code', + code: prettyFormat(contents), + }, + { + type: 'log', + category: 'info', + text: `to match pattern`, + }, + { + type: 'code', + code: prettyFormat(regex.source), + }, + ], + 1, + ); + } + } + + notRegex( + contents: string, + regex: RegExp, + message: string = 't.regex failed, using RegExp.test semantics', + ): void { + if (regex.test(contents)) { + this.fail( + message, + [ + { + type: 'log', + category: 'info', + text: `Expected`, + }, + { + type: 'code', + code: prettyFormat(contents), + }, + { + type: 'log', + category: 'info', + text: `to not match pattern`, + }, + { + type: 'code', + code: prettyFormat(regex.source), + }, + ], + 1, + ); + } + } + + inlineSnapshot(received: unknown, snapshot?: string | boolean | number) { + const callFrame = getErrorStructure(new Error()).frames[1]; + const callError = getErrorStructure(new Error(), 1); + + this.onTeardown(async () => { + const {status} = this.snapshotManager.testInlineSnapshot( + callFrame, + received, + snapshot, + ); + + if (status === 'UPDATE' && this.options.freezeSnapshots) { + await this.emitDiagnostic( + deriveDiagnosticFromErrorStructure( + callError, + { + description: descriptions.SNAPSHOTS.INLINE_FROZEN, + }, + ), + ); + } + + if (status === 'NO_MATCH') { + await this.emitDiagnostic( + deriveDiagnosticFromErrorStructure( + callError, + { + description: { + ...descriptions.SNAPSHOTS.INLINE_BAD_MATCH, + advice: this.buildMatchAdvice( + received, + snapshot, + { + receivedAlias: 'What the code gave us', + expectedAlias: 'Existing inline snapshot', + }, + ), + }, + }, + ), + ); + } + }); + } + + snapshot( + expected: unknown, + message?: string, + opts?: TestSnapshotOptions, + ): string { + const id = this.snapshotCounter++; + return this.bufferSnapshot({ + entryName: String(id), + expected, + message, + opts, + }); + } + + namedSnapshot( + entryName: string, + expected: unknown, + message?: string, + opts?: TestSnapshotOptions, + ): string { + return this.bufferSnapshot({ + entryName, + expected, + message, + opts, + }); + } + + bufferSnapshot( + { + entryName, + message, + expected, + opts = {}, + }: SnapshotOptions, + ): string { + let language: undefined | string = opts.language; + + let formatted = ''; + if (typeof expected === 'string') { + formatted = expected; + } else { + language = 'javascript'; + formatted = prettyFormat(expected); + } + + const callError = getErrorStructure(new Error(), 2); + + this.onTeardown(async () => { + // Get the current snapshot + const existingSnapshot = await this.snapshotManager.get( + this.testName, + entryName, + opts.filename, + ); + if (existingSnapshot === undefined) { + if (this.options.freezeSnapshots) { + await this.emitDiagnostic( + deriveDiagnosticFromErrorStructure( + callError, + { + description: descriptions.SNAPSHOTS.FROZEN, + }, + ), + ); + } else { + // No snapshot exists, let's save this one! + this.snapshotManager.set({ + testName: this.testName, + entryName, + value: formatted, + language, + optionalFilename: opts.filename, + }); + } + return; + } + + // Compare the snapshots + if (formatted !== existingSnapshot) { + const advice: DiagnosticAdvice = this.buildMatchAdvice( + formatted, + existingSnapshot, + { + receivedAlias: 'What the code gave us', + expectedAlias: 'Existing snapshot', + }, + ); + + if (message === undefined) { + message = markup`Snapshot ${entryName} at doesn't match`; + } else { + message = escapeMarkup(message); + + advice.push({ + type: 'log', + category: 'info', + text: markup`Snapshot can be found at `, + }); + } + + advice.push({ + type: 'log', + category: 'info', + text: markup`Run rome test --update-snapshots to update this snapshot`, + }); + + await this.emitDiagnostic( + deriveDiagnosticFromErrorStructure( + callError, + { + description: { + category: 'tests/snapshots/incorrect', + message: createBlessedDiagnosticMessage(message), + advice, + }, + }, + ), + ); + } + }); + + return entryName; + } } diff --git a/packages/@romejs/core/test-worker/TestWorker.ts b/packages/@romejs/core/test-worker/TestWorker.ts index fd0c0854862..2e56f0772e1 100644 --- a/packages/@romejs/core/test-worker/TestWorker.ts +++ b/packages/@romejs/core/test-worker/TestWorker.ts @@ -6,9 +6,9 @@ */ import { - TestWorkerPrepareTestOptions, - TestWorkerPrepareTestResult, - TestWorkerRunTestOptions, + TestWorkerPrepareTestOptions, + TestWorkerPrepareTestResult, + TestWorkerRunTestOptions, } from '../common/bridges/TestWorkerBridge'; import {deriveDiagnosticFromError} from '@romejs/diagnostics'; import {TestWorkerBridge} from '@romejs/core'; @@ -17,81 +17,81 @@ import TestWorkerRunner, {TestWorkerFileResult} from './TestWorkerRunner'; import inspector = require('inspector'); export type TestWorkerFlags = { - inspectorPort: number; + inspectorPort: number; }; export default class TestWorker { - constructor() { - this.bridge = this.buildBridge(); - this.runners = new Map(); - } + constructor() { + this.bridge = this.buildBridge(); + this.runners = new Map(); + } - runners: Map; - bridge: TestWorkerBridge; + runners: Map; + bridge: TestWorkerBridge; - async init(flags: TestWorkerFlags) { - inspector.open(flags.inspectorPort); + async init(flags: TestWorkerFlags) { + inspector.open(flags.inspectorPort); - await this.bridge.handshake(); - } + await this.bridge.handshake(); + } - buildBridge(): TestWorkerBridge { - const bridge = createBridgeFromParentProcess( - TestWorkerBridge, - { - type: 'server', - }, - ); + buildBridge(): TestWorkerBridge { + const bridge = createBridgeFromParentProcess( + TestWorkerBridge, + { + type: 'server', + }, + ); - process.on( - 'unhandledRejection', - (err) => { - bridge.testDiagnostic.send({ - origin: undefined, - diagnostic: deriveDiagnosticFromError( - err, - { - description: { - category: 'tests/unhandledRejection', - }, - }, - ), - }); - }, - ); + process.on( + 'unhandledRejection', + (err) => { + bridge.testDiagnostic.send({ + origin: undefined, + diagnostic: deriveDiagnosticFromError( + err, + { + description: { + category: 'tests/unhandledRejection', + }, + }, + ), + }); + }, + ); - bridge.inspectorDetails.subscribe(() => { - return { - inspectorUrl: inspector.url(), - }; - }); + bridge.inspectorDetails.subscribe(() => { + return { + inspectorUrl: inspector.url(), + }; + }); - bridge.prepareTest.subscribe((data) => { - return this.prepareTest(data); - }); + bridge.prepareTest.subscribe((data) => { + return this.prepareTest(data); + }); - bridge.runTest.subscribe((opts) => { - return this.runTest(opts); - }); + bridge.runTest.subscribe((opts) => { + return this.runTest(opts); + }); - return bridge; - } + return bridge; + } - async runTest(opts: TestWorkerRunTestOptions): Promise { - const {id} = opts; - const runner = this.runners.get(id); - if (runner === undefined) { - throw new Error(`No runner ${id} found`); - } else { - return await runner.run(opts); - } - } + async runTest(opts: TestWorkerRunTestOptions): Promise { + const {id} = opts; + const runner = this.runners.get(id); + if (runner === undefined) { + throw new Error(`No runner ${id} found`); + } else { + return await runner.run(opts); + } + } - async prepareTest( - opts: TestWorkerPrepareTestOptions, - ): Promise { - const runner = new TestWorkerRunner(opts, this.bridge); - this.runners.set(opts.id, runner); - return await runner.prepare(); - } + async prepareTest( + opts: TestWorkerPrepareTestOptions, + ): Promise { + const runner = new TestWorkerRunner(opts, this.bridge); + this.runners.set(opts.id, runner); + return await runner.prepare(); + } } diff --git a/packages/@romejs/core/test-worker/TestWorkerRunner.ts b/packages/@romejs/core/test-worker/TestWorkerRunner.ts index 376f2f1d100..d19ea98d61f 100644 --- a/packages/@romejs/core/test-worker/TestWorkerRunner.ts +++ b/packages/@romejs/core/test-worker/TestWorkerRunner.ts @@ -7,644 +7,636 @@ import {UnknownObject} from '@romejs/typescript-helpers'; import { - Diagnostic, - DiagnosticAdvice, - DiagnosticLocation, - DiagnosticLogCategory, - DiagnosticOrigin, - INTERNAL_ERROR_LOG_ADVICE, - catchDiagnostics, - createBlessedDiagnosticMessage, - createSingleDiagnosticError, - deriveDiagnosticFromErrorStructure, - descriptions, - getErrorStackAdvice, + Diagnostic, + DiagnosticAdvice, + DiagnosticLocation, + DiagnosticLogCategory, + DiagnosticOrigin, + INTERNAL_ERROR_LOG_ADVICE, + catchDiagnostics, + createBlessedDiagnosticMessage, + createSingleDiagnosticError, + deriveDiagnosticFromErrorStructure, + descriptions, + getErrorStackAdvice, } from '@romejs/diagnostics'; import { - GlobalTestOptions, - TestCallback, - TestOptions, + GlobalTestOptions, + TestCallback, + TestOptions, } from '@romejs-runtime/rome/test'; import { - TestRef, - default as TestWorkerBridge, - TestWorkerPrepareTestOptions, - TestWorkerPrepareTestResult, - TestWorkerRunTestOptions, + TestRef, + default as TestWorkerBridge, + TestWorkerPrepareTestOptions, + TestWorkerPrepareTestResult, + TestWorkerRunTestOptions, } from '../common/bridges/TestWorkerBridge'; import {TestMasterRunnerOptions} from '../master/testing/types'; import SnapshotManager, { - InlineSnapshotUpdate, - SnapshotCounts, + InlineSnapshotUpdate, + SnapshotCounts, } from './SnapshotManager'; import TestAPI, {OnTimeout} from './TestAPI'; import executeMain from '../common/utils/executeMain'; import { - FileReference, - convertTransportFileReference, + FileReference, + convertTransportFileReference, } from '../common/types/files'; import {AbsoluteFilePath, createAbsoluteFilePath} from '@romejs/path'; import {escapeMarkup, markup} from '@romejs/string-markup'; import { - ErrorFrames, - StructuredError, - getErrorStructure, - getSourceLocationFromErrorFrame, + ErrorFrames, + StructuredError, + getErrorStructure, + getSourceLocationFromErrorFrame, } from '@romejs/v8'; import prettyFormat from '@romejs/pretty-format'; const MAX_RUNNING_TESTS = 20; function cleanFrames(frames: ErrorFrames): ErrorFrames { - // TODO we should actually get the frames before module init and do it that way - // Remove everything before the original module factory - let latestTestWorkerFrame = frames.find((frame, i) => { - if ( - frame.typeName === 'global' && - frame.methodName === undefined && - frame.functionName === undefined - ) { - // We are the global. frame - // Now check for Script.runInContext - const nextFrame = frames[i + 1]; - if ( - nextFrame !== undefined && - nextFrame.typeName === 'Script' && - nextFrame.methodName === 'runInContext' - ) { - // Yes! - // TODO also check for ___$romejs$core$common$utils$executeMain_ts$default (packages/romejs/core/common/utils/executeMain.ts:69:17) - return true; - } - } - - return false; - }); - - // And if there was no module factory frame, then we must be inside of a test - if (latestTestWorkerFrame === undefined) { - latestTestWorkerFrame = frames.find((frame) => { - return ( - frame.typeName !== undefined && - frame.typeName.includes('$TestWorkerRunner') - ); - }); - } - - if (latestTestWorkerFrame === undefined) { - return frames; - } - - return frames.slice(0, frames.indexOf(latestTestWorkerFrame)); + // TODO we should actually get the frames before module init and do it that way + // Remove everything before the original module factory + let latestTestWorkerFrame = frames.find((frame, i) => { + if ( + frame.typeName === 'global' && + frame.methodName === undefined && + frame.functionName === undefined + ) { + // We are the global. frame + // Now check for Script.runInContext + const nextFrame = frames[i + 1]; + if ( + nextFrame !== undefined && + nextFrame.typeName === 'Script' && + nextFrame.methodName === 'runInContext' + ) { + // Yes! + // TODO also check for ___$romejs$core$common$utils$executeMain_ts$default (packages/romejs/core/common/utils/executeMain.ts:69:17) + return true; + } + } + + return false; + }); + + // And if there was no module factory frame, then we must be inside of a test + if (latestTestWorkerFrame === undefined) { + latestTestWorkerFrame = frames.find((frame) => { + return ( + frame.typeName !== undefined && frame.typeName.includes('$TestWorkerRunner') + ); + }); + } + + if (latestTestWorkerFrame === undefined) { + return frames; + } + + return frames.slice(0, frames.indexOf(latestTestWorkerFrame)); } export type TestWorkerFileResult = { - snapshotCounts: SnapshotCounts; - inlineSnapshotUpdates: Array; + snapshotCounts: SnapshotCounts; + inlineSnapshotUpdates: Array; }; type FoundTest = { - options: TestOptions; - callback: TestCallback; + options: TestOptions; + callback: TestCallback; }; export type FocusedTest = { - testName: string; - location: DiagnosticLocation; + testName: string; + location: DiagnosticLocation; }; export default class TestWorkerRunner { - constructor(opts: TestWorkerPrepareTestOptions, bridge: TestWorkerBridge) { - this.opts = opts; - this.locked = false; - this.file = convertTransportFileReference(opts.file); - this.options = opts.options; - this.bridge = bridge; - this.projectFolder = createAbsoluteFilePath(opts.projectFolder); - - this.snapshotManager = new SnapshotManager( - this, - createAbsoluteFilePath(opts.file.real), - ); - - this.hasDiagnostics = false; - this.consoleAdvice = []; - this.hasFocusedTests = false; - this.focusedTests = []; - this.pendingDiagnostics = []; - this.foundTests = new Map(); - } - - foundTests: Map; - hasFocusedTests: boolean; - focusedTests: Array; - bridge: TestWorkerBridge; - projectFolder: AbsoluteFilePath; - file: FileReference; - options: TestMasterRunnerOptions; - snapshotManager: SnapshotManager; - opts: TestWorkerPrepareTestOptions; - locked: boolean; - consoleAdvice: DiagnosticAdvice; - hasDiagnostics: boolean; - pendingDiagnostics: Array; - - createConsole(): Partial { - const addDiagnostic = ( - category: DiagnosticLogCategory, - args: Array, - ) => { - let textParts: Array = []; - if (args.length === 1 && typeof args[0] === 'string') { - textParts.push(escapeMarkup(args[0])); - } else { - textParts = args.map((arg) => prettyFormat(arg, {markup: true})); - } - const text = textParts.join(' '); - - const err = new Error(); - - // Remove the first two frames to get to the actual source - const frames = cleanFrames(getErrorStructure(err).frames.slice(2)); - - this.consoleAdvice.push({ - type: 'log', - category, - text, - }); - this.consoleAdvice = this.consoleAdvice.concat( - getErrorStackAdvice( - getErrorStructure({ - ...err, - frames, - }), - ), - ); - }; - - function log(...args: Array): void { - addDiagnostic('none', args); - } - - return { - assert(expression: unknown, ...args: Array): void { - if (!expression) { - args[0] = `Assertion failed${args.length === 0 ? '' : `: ${args[0]}`}`; - addDiagnostic('warn', args); - } - }, - dir(obj: unknown): void { - addDiagnostic('info', [obj]); - }, - error: (...args: Array): void => { - addDiagnostic('error', args); - }, - warn: (...args: Array): void => { - addDiagnostic('warn', args); - }, - dirxml: log, - debug: log, - info: (...args: Array): void => { - addDiagnostic('info', args); - }, - log, - trace: log, - // Noop - count(): void {}, - countReset(): void {}, - table(): void {}, - time(): void {}, - timeEnd(): void {}, - timeLog(): void {}, - clear(): void {}, - group(): void {}, - groupCollapsed(): void {}, - groupEnd(): void {}, - profile(): void {}, - profileEnd(): void {}, - timeStamp(): void {}, - }; - } - - // Global variables to expose to tests - getEnvironment(): UnknownObject { - const testOptions: GlobalTestOptions = { - dirname: this.file.real.getParent().join(), - register: ( - callsiteError: Error, - opts: TestOptions, - callback: TestCallback, - ) => { - this.registerTest(callsiteError, opts, callback); - }, - }; - - return { - __ROME__TEST_OPTIONS__: testOptions, - console: this.createConsole(), - }; - } - - // execute the test file and discover tests - async discoverTests() { - const {code} = this.opts; - - try { - const res = await executeMain({ - path: this.file.real, - code, - globals: this.getEnvironment(), - }); - - if (res.syntaxError !== undefined) { - const message = `A bundle was generated that contained a syntax error: ${res.syntaxError.description.message.value}`; - - throw createSingleDiagnosticError({ - ...res.syntaxError, - description: { - ...res.syntaxError.description, - message: createBlessedDiagnosticMessage(message), - advice: [INTERNAL_ERROR_LOG_ADVICE], - }, - location: { - ...res.syntaxError.location, - filename: this.file.uid, - }, - }); - } - } catch (err) { - await this.onError( - undefined, - { - error: err, - firstAdvice: [], - lastAdvice: [ - { - type: 'log', - category: 'info', - text: markup`Error occured while executing test file `, - }, - ], - }, - ); - } - } - - lockTests() { - this.locked = true; - } - - registerTest( - callsiteError: Error, - options: TestOptions, - callback: TestCallback, - ) { - if (this.locked) { - throw new Error("Test can't be added outside of init"); - } - - let testName = options.name; - if (Array.isArray(testName)) { - testName = testName.join(' > '); - } - - if (this.foundTests.has(testName)) { - throw new Error(`Test ${testName} has already been defined`); - } - - this.foundTests.set( - testName, - { - callback, - options, - }, - ); - - if (options.only === true) { - const callsiteStruct = getErrorStructure(callsiteError, 1); - - this.focusedTests.push({ - testName, - location: getSourceLocationFromErrorFrame(callsiteStruct.frames[0]), - }); - - this.hasFocusedTests = true; - - if (!this.options.focusAllowed) { - const diag = this.deriveDiagnosticFromErrorStructure(callsiteStruct); - - this.pendingDiagnostics.push({ - ...diag, - description: { - ...diag.description, - message: createBlessedDiagnosticMessage( - 'Focused tests are not allowed due to a set flag', - ), - }, - }); - } - } - } - - async emitDiagnostic( - diag: Diagnostic, - ref?: TestRef, - advice?: DiagnosticAdvice, - ) { - let origin: DiagnosticOrigin = { - category: 'test/error', - message: 'Generated from a test worker without being attached to a test', - }; - - if (ref !== undefined) { - origin.message = markup`Generated from the file and test name "${ref.testName}"`; - } - - let label = diag.label; - if (label !== undefined && ref !== undefined) { - label = escapeMarkup(ref.testName); - } - - diag = { - ...diag, - label, - description: { - ...diag.description, - advice: [...diag.description.advice, ...(advice || [])], - }, - }; - - this.hasDiagnostics = true; - await this.bridge.testDiagnostic.call({diagnostic: diag, origin}); - } - - deriveDiagnosticFromErrorStructure(struct: StructuredError): Diagnostic { - return deriveDiagnosticFromErrorStructure( - struct, - { - description: { - category: 'tests/failure', - }, - filename: this.file.real.join(), - cleanFrames, - }, - ); - } - - async onError( - testName: undefined | string, - opts: { - error: Error; - firstAdvice?: DiagnosticAdvice; - lastAdvice?: DiagnosticAdvice; - }, - ): Promise { - let diagnostic = this.deriveDiagnosticFromErrorStructure( - getErrorStructure(opts.error), - ); - - diagnostic = { - ...diagnostic, - unique: true, - description: { - ...diagnostic.description, - advice: [ - ...(opts.firstAdvice || []), - ...diagnostic.description.advice, - ...(opts.lastAdvice || []), - ], - }, - }; - - await this.emitDiagnostic( - diagnostic, - testName === undefined ? undefined : this.createTestRef(testName), - ); - } - - async teardownTest(testName: string, api: TestAPI): Promise { - api.clearTimeout(); - - try { - await api.teardownEvent.callOptional(); - return true; - } catch (err) { - await this.onError( - testName, - { - error: err, - firstAdvice: [], - lastAdvice: [ - { - type: 'log', - category: 'info', - text: `Error occured while running teardown for test ${testName}`, - }, - ...api.advice, - ], - }, - ); - return false; - } - } - - createTestRef(testName: string): TestRef { - return { - testName, - filename: this.file.real.join(), - }; - } - - async runTest(testName: string, callback: TestCallback) { - let onTimeout: OnTimeout = () => { - throw new Error("Promise wasn't created. Should be impossible."); - }; - - const timeoutPromise = new Promise((resolve, reject) => { - onTimeout = (time: number) => { - reject(new Error(`Test timeout - exceeded ${String(time)}ms`)); - }; - }); - - const ref = this.createTestRef(testName); - - const emitDiagnostic = (diag: Diagnostic): Promise => { - return this.emitDiagnostic(diag, ref, api.advice); - }; - - const api = new TestAPI({ - file: this.file, - testName, - onTimeout, - snapshotManager: this.snapshotManager, - options: this.options, - emitDiagnostic, - }); - - let testSuccess = false; - - try { - const {diagnostics} = await catchDiagnostics(async () => { - const res = callback(api); - - // Ducktyping this to detect a cross-realm Promise - if (res !== undefined && typeof res.then === 'function') { - await Promise.race([timeoutPromise, res]); - } - }); - - if (diagnostics !== undefined) { - for (const diag of diagnostics) { - await emitDiagnostic(diag); - } - } - - testSuccess = true; - } catch (err) { - await this.onError( - testName, - { - error: err, - firstAdvice: [], - lastAdvice: api.advice, - }, - ); - } finally { - const teardownSuccess = await this.teardownTest(testName, api); - await this.bridge.testFinish.call({ - success: testSuccess && teardownSuccess, - ref, - }); - } - } - - async run(opts: TestWorkerRunTestOptions): Promise { - const promises: Set> = new Set(); - - const {foundTests} = this; - - // Emit error about no found tests. If we already have diagnostics then there was an issue - // during initialization. - if (foundTests.size === 0 && !this.hasDiagnostics) { - this.emitDiagnostic({ - location: { - filename: this.file.uid, - }, - description: descriptions.TESTS.UNDECLARED, - }); - } - - // We could be pretending we have focused tests here but at least one file was execueted with - // focused tests - if (opts.onlyFocusedTests) { - this.hasFocusedTests = true; - } - - // Execute all the tests - for (const [testName, test] of foundTests) { - const {options, callback} = test; - if (this.hasFocusedTests && !test.options.only) { - continue; - } - - this.bridge.testStart.send({ - ref: { - filename: this.file.real.join(), - testName, - }, - timeout: options.timeout, - }); - - const promise = this.runTest(testName, callback); - - if (this.options.syncTests) { - await promise; - } else { - promise.then(() => { - promises.delete(promise); - }); - promises.add(promise); - - // if there's 5 promises, then wait for one of them to finish - if (promises.size > MAX_RUNNING_TESTS) { - await Promise.race(Array.from(promises)); - } - } - } - - // Execute the remaining tests - await Promise.all(Array.from(promises)); - - // Save the snapshot - await this.snapshotManager.save(); - - if (this.hasDiagnostics && this.consoleAdvice.length > 0) { - await this.emitDiagnostic({ - description: descriptions.TESTS.LOGS(this.consoleAdvice), - location: { - filename: this.file.uid, - }, - }); - } - - for (const diag of this.pendingDiagnostics) { - await this.emitDiagnostic(diag); - } - - return { - inlineSnapshotUpdates: this.snapshotManager.inlineSnapshotsUpdates, - snapshotCounts: this.snapshotManager.snapshotCounts, - }; - } - - async emitFoundTests() { - const tests: Array = []; - - for (const testName of this.foundTests.keys()) { - tests.push({ - filename: this.file.real.join(), - testName, - }); - } - - await this.bridge.testsFound.call(tests); - } - - async wrap(callback: () => Promise): Promise { - try { - const {diagnostics} = await catchDiagnostics(callback); - - if (diagnostics !== undefined) { - for (const diagnostic of diagnostics) { - await this.emitDiagnostic(diagnostic); - } - } - } catch (err) { - await this.onError( - undefined, - { - error: err, - firstAdvice: [], - lastAdvice: [ - { - type: 'log', - category: 'info', - text: markup`Error occured while executing test file `, - }, - INTERNAL_ERROR_LOG_ADVICE, - ], - }, - ); - } - } - - async prepare(): Promise { - await this.wrap(async () => { - await this.snapshotManager.init(); - await this.discoverTests(); - await this.emitFoundTests(); - this.lockTests(); - }); - return {focusedTests: this.focusedTests}; - } + constructor(opts: TestWorkerPrepareTestOptions, bridge: TestWorkerBridge) { + this.opts = opts; + this.locked = false; + this.file = convertTransportFileReference(opts.file); + this.options = opts.options; + this.bridge = bridge; + this.projectFolder = createAbsoluteFilePath(opts.projectFolder); + + this.snapshotManager = new SnapshotManager( + this, + createAbsoluteFilePath(opts.file.real), + ); + + this.hasDiagnostics = false; + this.consoleAdvice = []; + this.hasFocusedTests = false; + this.focusedTests = []; + this.pendingDiagnostics = []; + this.foundTests = new Map(); + } + + foundTests: Map; + hasFocusedTests: boolean; + focusedTests: Array; + bridge: TestWorkerBridge; + projectFolder: AbsoluteFilePath; + file: FileReference; + options: TestMasterRunnerOptions; + snapshotManager: SnapshotManager; + opts: TestWorkerPrepareTestOptions; + locked: boolean; + consoleAdvice: DiagnosticAdvice; + hasDiagnostics: boolean; + pendingDiagnostics: Array; + + createConsole(): Partial { + const addDiagnostic = (category: DiagnosticLogCategory, args: Array) => { + let textParts: Array = []; + if (args.length === 1 && typeof args[0] === 'string') { + textParts.push(escapeMarkup(args[0])); + } else { + textParts = args.map((arg) => prettyFormat(arg, {markup: true})); + } + const text = textParts.join(' '); + + const err = new Error(); + + // Remove the first two frames to get to the actual source + const frames = cleanFrames(getErrorStructure(err).frames.slice(2)); + + this.consoleAdvice.push({ + type: 'log', + category, + text, + }); + this.consoleAdvice = this.consoleAdvice.concat( + getErrorStackAdvice( + getErrorStructure({ + ...err, + frames, + }), + ), + ); + }; + + function log(...args: Array): void { + addDiagnostic('none', args); + } + + return { + assert(expression: unknown, ...args: Array): void { + if (!expression) { + args[0] = `Assertion failed${args.length === 0 ? '' : `: ${args[0]}`}`; + addDiagnostic('warn', args); + } + }, + dir(obj: unknown): void { + addDiagnostic('info', [obj]); + }, + error: (...args: Array): void => { + addDiagnostic('error', args); + }, + warn: (...args: Array): void => { + addDiagnostic('warn', args); + }, + dirxml: log, + debug: log, + info: (...args: Array): void => { + addDiagnostic('info', args); + }, + log, + trace: log, + // Noop + count(): void {}, + countReset(): void {}, + table(): void {}, + time(): void {}, + timeEnd(): void {}, + timeLog(): void {}, + clear(): void {}, + group(): void {}, + groupCollapsed(): void {}, + groupEnd(): void {}, + profile(): void {}, + profileEnd(): void {}, + timeStamp(): void {}, + }; + } + + // Global variables to expose to tests + getEnvironment(): UnknownObject { + const testOptions: GlobalTestOptions = { + dirname: this.file.real.getParent().join(), + register: (callsiteError: Error, opts: TestOptions, callback: TestCallback) => { + this.registerTest(callsiteError, opts, callback); + }, + }; + + return { + __ROME__TEST_OPTIONS__: testOptions, + console: this.createConsole(), + }; + } + + // execute the test file and discover tests + async discoverTests() { + const {code} = this.opts; + + try { + const res = await executeMain({ + path: this.file.real, + code, + globals: this.getEnvironment(), + }); + + if (res.syntaxError !== undefined) { + const message = `A bundle was generated that contained a syntax error: ${res.syntaxError.description.message.value}`; + + throw createSingleDiagnosticError({ + ...res.syntaxError, + description: { + ...res.syntaxError.description, + message: createBlessedDiagnosticMessage(message), + advice: [INTERNAL_ERROR_LOG_ADVICE], + }, + location: { + ...res.syntaxError.location, + filename: this.file.uid, + }, + }); + } + } catch (err) { + await this.onError( + undefined, + { + error: err, + firstAdvice: [], + lastAdvice: [ + { + type: 'log', + category: 'info', + text: markup`Error occured while executing test file `, + }, + ], + }, + ); + } + } + + lockTests() { + this.locked = true; + } + + registerTest( + callsiteError: Error, + options: TestOptions, + callback: TestCallback, + ) { + if (this.locked) { + throw new Error("Test can't be added outside of init"); + } + + let testName = options.name; + if (Array.isArray(testName)) { + testName = testName.join(' > '); + } + + if (this.foundTests.has(testName)) { + throw new Error(`Test ${testName} has already been defined`); + } + + this.foundTests.set( + testName, + { + callback, + options, + }, + ); + + if (options.only === true) { + const callsiteStruct = getErrorStructure(callsiteError, 1); + + this.focusedTests.push({ + testName, + location: getSourceLocationFromErrorFrame(callsiteStruct.frames[0]), + }); + + this.hasFocusedTests = true; + + if (!this.options.focusAllowed) { + const diag = this.deriveDiagnosticFromErrorStructure(callsiteStruct); + + this.pendingDiagnostics.push({ + ...diag, + description: { + ...diag.description, + message: createBlessedDiagnosticMessage( + 'Focused tests are not allowed due to a set flag', + ), + }, + }); + } + } + } + + async emitDiagnostic( + diag: Diagnostic, + ref?: TestRef, + advice?: DiagnosticAdvice, + ) { + let origin: DiagnosticOrigin = { + category: 'test/error', + message: 'Generated from a test worker without being attached to a test', + }; + + if (ref !== undefined) { + origin.message = markup`Generated from the file and test name "${ref.testName}"`; + } + + let label = diag.label; + if (label !== undefined && ref !== undefined) { + label = escapeMarkup(ref.testName); + } + + diag = { + ...diag, + label, + description: { + ...diag.description, + advice: [...diag.description.advice, ...(advice || [])], + }, + }; + + this.hasDiagnostics = true; + await this.bridge.testDiagnostic.call({diagnostic: diag, origin}); + } + + deriveDiagnosticFromErrorStructure(struct: StructuredError): Diagnostic { + return deriveDiagnosticFromErrorStructure( + struct, + { + description: { + category: 'tests/failure', + }, + filename: this.file.real.join(), + cleanFrames, + }, + ); + } + + async onError( + testName: undefined | string, + opts: { + error: Error; + firstAdvice?: DiagnosticAdvice; + lastAdvice?: DiagnosticAdvice; + }, + ): Promise { + let diagnostic = this.deriveDiagnosticFromErrorStructure( + getErrorStructure(opts.error), + ); + + diagnostic = { + ...diagnostic, + unique: true, + description: { + ...diagnostic.description, + advice: [ + ...(opts.firstAdvice || []), + ...diagnostic.description.advice, + ...(opts.lastAdvice || []), + ], + }, + }; + + await this.emitDiagnostic( + diagnostic, + testName === undefined ? undefined : this.createTestRef(testName), + ); + } + + async teardownTest(testName: string, api: TestAPI): Promise { + api.clearTimeout(); + + try { + await api.teardownEvent.callOptional(); + return true; + } catch (err) { + await this.onError( + testName, + { + error: err, + firstAdvice: [], + lastAdvice: [ + { + type: 'log', + category: 'info', + text: `Error occured while running teardown for test ${testName}`, + }, + ...api.advice, + ], + }, + ); + return false; + } + } + + createTestRef(testName: string): TestRef { + return { + testName, + filename: this.file.real.join(), + }; + } + + async runTest(testName: string, callback: TestCallback) { + let onTimeout: OnTimeout = () => { + throw new Error("Promise wasn't created. Should be impossible."); + }; + + const timeoutPromise = new Promise((resolve, reject) => { + onTimeout = (time: number) => { + reject(new Error(`Test timeout - exceeded ${String(time)}ms`)); + }; + }); + + const ref = this.createTestRef(testName); + + const emitDiagnostic = (diag: Diagnostic): Promise => { + return this.emitDiagnostic(diag, ref, api.advice); + }; + + const api = new TestAPI({ + file: this.file, + testName, + onTimeout, + snapshotManager: this.snapshotManager, + options: this.options, + emitDiagnostic, + }); + + let testSuccess = false; + + try { + const {diagnostics} = await catchDiagnostics(async () => { + const res = callback(api); + + // Ducktyping this to detect a cross-realm Promise + if (res !== undefined && typeof res.then === 'function') { + await Promise.race([timeoutPromise, res]); + } + }); + + if (diagnostics !== undefined) { + for (const diag of diagnostics) { + await emitDiagnostic(diag); + } + } + + testSuccess = true; + } catch (err) { + await this.onError( + testName, + { + error: err, + firstAdvice: [], + lastAdvice: api.advice, + }, + ); + } finally { + const teardownSuccess = await this.teardownTest(testName, api); + await this.bridge.testFinish.call({ + success: testSuccess && teardownSuccess, + ref, + }); + } + } + + async run(opts: TestWorkerRunTestOptions): Promise { + const promises: Set> = new Set(); + + const {foundTests} = this; + + // Emit error about no found tests. If we already have diagnostics then there was an issue + // during initialization. + if (foundTests.size === 0 && !this.hasDiagnostics) { + this.emitDiagnostic({ + location: { + filename: this.file.uid, + }, + description: descriptions.TESTS.UNDECLARED, + }); + } + + // We could be pretending we have focused tests here but at least one file was execueted with + // focused tests + if (opts.onlyFocusedTests) { + this.hasFocusedTests = true; + } + + // Execute all the tests + for (const [testName, test] of foundTests) { + const {options, callback} = test; + if (this.hasFocusedTests && !test.options.only) { + continue; + } + + this.bridge.testStart.send({ + ref: { + filename: this.file.real.join(), + testName, + }, + timeout: options.timeout, + }); + + const promise = this.runTest(testName, callback); + + if (this.options.syncTests) { + await promise; + } else { + promise.then(() => { + promises.delete(promise); + }); + promises.add(promise); + + // if there's 5 promises, then wait for one of them to finish + if (promises.size > MAX_RUNNING_TESTS) { + await Promise.race(Array.from(promises)); + } + } + } + + // Execute the remaining tests + await Promise.all(Array.from(promises)); + + // Save the snapshot + await this.snapshotManager.save(); + + if (this.hasDiagnostics && this.consoleAdvice.length > 0) { + await this.emitDiagnostic({ + description: descriptions.TESTS.LOGS(this.consoleAdvice), + location: { + filename: this.file.uid, + }, + }); + } + + for (const diag of this.pendingDiagnostics) { + await this.emitDiagnostic(diag); + } + + return { + inlineSnapshotUpdates: this.snapshotManager.inlineSnapshotsUpdates, + snapshotCounts: this.snapshotManager.snapshotCounts, + }; + } + + async emitFoundTests() { + const tests: Array = []; + + for (const testName of this.foundTests.keys()) { + tests.push({ + filename: this.file.real.join(), + testName, + }); + } + + await this.bridge.testsFound.call(tests); + } + + async wrap(callback: () => Promise): Promise { + try { + const {diagnostics} = await catchDiagnostics(callback); + + if (diagnostics !== undefined) { + for (const diagnostic of diagnostics) { + await this.emitDiagnostic(diagnostic); + } + } + } catch (err) { + await this.onError( + undefined, + { + error: err, + firstAdvice: [], + lastAdvice: [ + { + type: 'log', + category: 'info', + text: markup`Error occured while executing test file `, + }, + INTERNAL_ERROR_LOG_ADVICE, + ], + }, + ); + } + } + + async prepare(): Promise { + await this.wrap(async () => { + await this.snapshotManager.init(); + await this.discoverTests(); + await this.emitFoundTests(); + this.lockTests(); + }); + return {focusedTests: this.focusedTests}; + } } diff --git a/packages/@romejs/core/test-worker/utils.ts b/packages/@romejs/core/test-worker/utils.ts index a32be5e012d..9832a6baaf6 100644 --- a/packages/@romejs/core/test-worker/utils.ts +++ b/packages/@romejs/core/test-worker/utils.ts @@ -8,9 +8,9 @@ import prettyFormat from '@romejs/pretty-format'; export function format(value: unknown): string { - if (typeof value === 'string') { - return value; - } else { - return prettyFormat(value); - } + if (typeof value === 'string') { + return value; + } else { + return prettyFormat(value); + } } diff --git a/packages/@romejs/core/worker/Worker.ts b/packages/@romejs/core/worker/Worker.ts index 8feadb8a4df..26c3944ef9d 100644 --- a/packages/@romejs/core/worker/Worker.ts +++ b/packages/@romejs/core/worker/Worker.ts @@ -7,11 +7,11 @@ import {ModuleSignature, TypeCheckProvider} from '@romejs/js-analysis'; import WorkerBridge, { - PrefetchedModuleSignatures, - WorkerParseOptions, - WorkerPartialManifest, - WorkerPartialManifests, - WorkerProjects, + PrefetchedModuleSignatures, + WorkerParseOptions, + WorkerPartialManifest, + WorkerPartialManifests, + WorkerProjects, } from '../common/bridges/WorkerBridge'; import {ConstProgramSyntax, ConstSourceType, Program} from '@romejs/js-ast'; import Logger from '../common/utils/Logger'; @@ -24,229 +24,229 @@ import {UserConfig, loadUserConfig} from '../common/userConfig'; import {hydrateJSONProjectConfig} from '@romejs/project'; import {Diagnostics, DiagnosticsError} from '@romejs/diagnostics'; import { - AbsoluteFilePath, - AbsoluteFilePathMap, - UnknownFilePathMap, - createAbsoluteFilePath, - createUnknownFilePath, + AbsoluteFilePath, + AbsoluteFilePathMap, + UnknownFilePathMap, + createAbsoluteFilePath, + createUnknownFilePath, } from '@romejs/path'; import {lstat, readFileText, writeFile} from '@romejs/fs'; import { - FileReference, - convertTransportFileReference, + FileReference, + convertTransportFileReference, } from '../common/types/files'; import {getFileHandlerAssert} from '../common/file-handlers/index'; import {TransformProjectDefinition} from '@romejs/js-compiler'; export type ParseJSResult = { - ast: Program; - project: TransformProjectDefinition; - path: AbsoluteFilePath; - lastAccessed: number; - sourceText: string; - generated: boolean; + ast: Program; + project: TransformProjectDefinition; + path: AbsoluteFilePath; + lastAccessed: number; + sourceText: string; + generated: boolean; }; type WorkerOptions = { - globalErrorHandlers: boolean; - bridge: WorkerBridge; + globalErrorHandlers: boolean; + bridge: WorkerBridge; }; export default class Worker { - constructor(opts: WorkerOptions) { - this.bridge = opts.bridge; - - this.userConfig = loadUserConfig(); - this.partialManifests = new Map(); - this.projects = new Map(); - this.astCache = new AbsoluteFilePathMap(); - this.moduleSignatureCache = new UnknownFilePathMap(); - this.buffers = new AbsoluteFilePathMap(); - - this.logger = new Logger( - 'worker', - () => opts.bridge.log.hasSubscribers(), - { - streams: [ - { - type: 'all', - format: 'none', - columns: Reporter.DEFAULT_COLUMNS, - unicode: true, - write(chunk) { - opts.bridge.log.send(chunk.toString()); - }, - }, - ], - }, - ); - - // - this.api = new WorkerAPI(this); - - if (opts.globalErrorHandlers) { - setupGlobalErrorHandlers((err) => { - // TODO - err; - }); - } - } - - userConfig: UserConfig; - - bridge: WorkerBridge; - api: WorkerAPI; - logger: Logger; - - partialManifests: Map; - projects: Map; - astCache: AbsoluteFilePathMap; - moduleSignatureCache: UnknownFilePathMap; - buffers: AbsoluteFilePathMap; - - getPartialManifest(id: number): WorkerPartialManifest { - const manifest = this.partialManifests.get(id); - if (manifest === undefined) { - throw new Error(`Requested manifest ${id} but we don't have it`); - } - return manifest; - } - - end() { - // This will only actually be called when a Worker is created inside of the Master - // Clear internal maps for memory, in case the Worker instance sticks around - this.astCache.clear(); - this.projects.clear(); - this.moduleSignatureCache.clear(); - } - - async init() { - const bridge: WorkerBridge = this.bridge; - - bridge.endEvent.subscribe(() => { - this.end(); - }); - - let profiler: undefined | Profiler; - bridge.profilingStart.subscribe(async (data) => { - if (profiler !== undefined) { - throw new Error('Expected no profiler to be running'); - } - profiler = new Profiler(); - await profiler.startProfiling(data.samplingInterval); - }); - - bridge.profilingStop.subscribe(async () => { - if (profiler === undefined) { - throw new Error('Expected a profiler to be running'); - } - const workerProfile = await profiler.stopProfiling(); - profiler = undefined; - return workerProfile; - }); - - bridge.compileJS.subscribe((payload) => { - return this.api.compileJS( - convertTransportFileReference(payload.file), - payload.stage, - payload.options, - payload.parseOptions, - ); - }); - - bridge.parseJS.subscribe((payload) => { - return this.api.parseJS( - convertTransportFileReference(payload.file), - payload.options, - ); - }); - - bridge.lint.subscribe((payload) => { - return this.api.lint( - convertTransportFileReference(payload.file), - payload.options, - payload.parseOptions, - ); - }); - - bridge.format.subscribe((payload) => { - return this.api.format( - convertTransportFileReference(payload.file), - payload.parseOptions, - ); - }); - - bridge.updateInlineSnapshots.subscribe((payload) => { - return this.api.updateInlineSnapshots( - convertTransportFileReference(payload.file), - payload.updates, - payload.parseOptions, - ); - }); - - bridge.analyzeDependencies.subscribe((payload) => { - return this.api.analyzeDependencies( - convertTransportFileReference(payload.file), - payload.parseOptions, - ); - }); - - bridge.evict.subscribe((payload) => { - this.evict(createAbsoluteFilePath(payload.filename)); - return undefined; - }); - - bridge.moduleSignatureJS.subscribe((payload) => { - return this.api.moduleSignatureJS( - convertTransportFileReference(payload.file), - payload.parseOptions, - ); - }); - - bridge.updateProjects.subscribe((payload) => { - return this.updateProjects(payload.projects); - }); - - bridge.updateManifests.subscribe((payload) => { - return this.updateManifests(payload.manifests); - }); - - bridge.status.subscribe(() => { - return { - astCacheSize: this.astCache.size, - pid: process.pid, - memoryUsage: process.memoryUsage(), - uptime: process.uptime(), - }; - }); - - bridge.updateBuffer.subscribe((payload) => { - return this.updateBuffer( - convertTransportFileReference(payload.file), - payload.content, - ); - }); - } - - async updateBuffer(ref: FileReference, content: string) { - const path = ref.real; - this.buffers.set(path, content); - - // Now outdated - this.astCache.delete(path); - this.moduleSignatureCache.delete(path); - } - - async getTypeCheckProvider( - projectId: number, - prefetchedModuleSignatures: PrefetchedModuleSignatures = {}, - parseOptions: WorkerParseOptions, - ): Promise { - const libs: Array = []; - - // TODO Figure out how to get the uids for the libraries, probably adding some additional stuff to ProjectConfig? - - /* + constructor(opts: WorkerOptions) { + this.bridge = opts.bridge; + + this.userConfig = loadUserConfig(); + this.partialManifests = new Map(); + this.projects = new Map(); + this.astCache = new AbsoluteFilePathMap(); + this.moduleSignatureCache = new UnknownFilePathMap(); + this.buffers = new AbsoluteFilePathMap(); + + this.logger = new Logger( + 'worker', + () => opts.bridge.log.hasSubscribers(), + { + streams: [ + { + type: 'all', + format: 'none', + columns: Reporter.DEFAULT_COLUMNS, + unicode: true, + write(chunk) { + opts.bridge.log.send(chunk.toString()); + }, + }, + ], + }, + ); + + // + this.api = new WorkerAPI(this); + + if (opts.globalErrorHandlers) { + setupGlobalErrorHandlers((err) => { + // TODO + err; + }); + } + } + + userConfig: UserConfig; + + bridge: WorkerBridge; + api: WorkerAPI; + logger: Logger; + + partialManifests: Map; + projects: Map; + astCache: AbsoluteFilePathMap; + moduleSignatureCache: UnknownFilePathMap; + buffers: AbsoluteFilePathMap; + + getPartialManifest(id: number): WorkerPartialManifest { + const manifest = this.partialManifests.get(id); + if (manifest === undefined) { + throw new Error(`Requested manifest ${id} but we don't have it`); + } + return manifest; + } + + end() { + // This will only actually be called when a Worker is created inside of the Master + // Clear internal maps for memory, in case the Worker instance sticks around + this.astCache.clear(); + this.projects.clear(); + this.moduleSignatureCache.clear(); + } + + async init() { + const bridge: WorkerBridge = this.bridge; + + bridge.endEvent.subscribe(() => { + this.end(); + }); + + let profiler: undefined | Profiler; + bridge.profilingStart.subscribe(async (data) => { + if (profiler !== undefined) { + throw new Error('Expected no profiler to be running'); + } + profiler = new Profiler(); + await profiler.startProfiling(data.samplingInterval); + }); + + bridge.profilingStop.subscribe(async () => { + if (profiler === undefined) { + throw new Error('Expected a profiler to be running'); + } + const workerProfile = await profiler.stopProfiling(); + profiler = undefined; + return workerProfile; + }); + + bridge.compileJS.subscribe((payload) => { + return this.api.compileJS( + convertTransportFileReference(payload.file), + payload.stage, + payload.options, + payload.parseOptions, + ); + }); + + bridge.parseJS.subscribe((payload) => { + return this.api.parseJS( + convertTransportFileReference(payload.file), + payload.options, + ); + }); + + bridge.lint.subscribe((payload) => { + return this.api.lint( + convertTransportFileReference(payload.file), + payload.options, + payload.parseOptions, + ); + }); + + bridge.format.subscribe((payload) => { + return this.api.format( + convertTransportFileReference(payload.file), + payload.parseOptions, + ); + }); + + bridge.updateInlineSnapshots.subscribe((payload) => { + return this.api.updateInlineSnapshots( + convertTransportFileReference(payload.file), + payload.updates, + payload.parseOptions, + ); + }); + + bridge.analyzeDependencies.subscribe((payload) => { + return this.api.analyzeDependencies( + convertTransportFileReference(payload.file), + payload.parseOptions, + ); + }); + + bridge.evict.subscribe((payload) => { + this.evict(createAbsoluteFilePath(payload.filename)); + return undefined; + }); + + bridge.moduleSignatureJS.subscribe((payload) => { + return this.api.moduleSignatureJS( + convertTransportFileReference(payload.file), + payload.parseOptions, + ); + }); + + bridge.updateProjects.subscribe((payload) => { + return this.updateProjects(payload.projects); + }); + + bridge.updateManifests.subscribe((payload) => { + return this.updateManifests(payload.manifests); + }); + + bridge.status.subscribe(() => { + return { + astCacheSize: this.astCache.size, + pid: process.pid, + memoryUsage: process.memoryUsage(), + uptime: process.uptime(), + }; + }); + + bridge.updateBuffer.subscribe((payload) => { + return this.updateBuffer( + convertTransportFileReference(payload.file), + payload.content, + ); + }); + } + + async updateBuffer(ref: FileReference, content: string) { + const path = ref.real; + this.buffers.set(path, content); + + // Now outdated + this.astCache.delete(path); + this.moduleSignatureCache.delete(path); + } + + async getTypeCheckProvider( + projectId: number, + prefetchedModuleSignatures: PrefetchedModuleSignatures = {}, + parseOptions: WorkerParseOptions, + ): Promise { + const libs: Array = []; + + // TODO Figure out how to get the uids for the libraries, probably adding some additional stuff to ProjectConfig? + + /* const projectConfig = this.getProjectConfig(projectId); for (const filename of projectConfig.typeChecking.libs) { const {ast, err} = await this.parse(filename, uid, projectId); @@ -258,242 +258,237 @@ export default class Worker { } } */ - const resolveGraph = async ( - key: string, - ): Promise => { - const value = prefetchedModuleSignatures[key]; - if (value === undefined) { - return undefined; - } - - switch (value.type) { - case 'RESOLVED': { - this.moduleSignatureCache.set( - createUnknownFilePath(value.graph.filename), - value.graph, - ); - return value.graph; - } - - case 'OWNED': - return this.api.moduleSignatureJS( - convertTransportFileReference(value.file), - parseOptions, - ); - - case 'POINTER': - return resolveGraph(value.key); - - case 'USE_CACHED': { - const cached = this.moduleSignatureCache.get( - createUnknownFilePath(value.filename), - ); - if (cached === undefined) { - throw new Error( - `Master told us we have the export types for ${value.filename} cached but we dont!`, - ); - } - return cached; - } - } - }; - - return { - getExportTypes: async ( - origin: string, - relative: string, - ): Promise => { - return resolveGraph(`${origin}:${relative}`); - }, - libs, - }; - } - - populateDiagnosticsMtime(diagnostics: Diagnostics): Diagnostics { - return diagnostics; - } - - async readFile(path: AbsoluteFilePath): Promise { - const buffer = this.buffers.get(path); - if (buffer === undefined) { - return await readFileText(path); - } else { - return buffer; - } - } - - async parseJS( - ref: FileReference, - options: WorkerParseOptions, - ): Promise { - const path = createAbsoluteFilePath(ref.real); - - const {project: projectId, uid} = ref; - const project = this.getProject(projectId); - - // Fetch and validate extension handler - const {handler} = getFileHandlerAssert(ref.real, project.config); - if (handler.toJavaScript === undefined) { - throw new Error(`We don't know how to convert the file ${path} to js`); - } - - // Get syntax - let syntax: Array = []; - if (options.syntax !== undefined) { - syntax = options.syntax; - } else if (handler.syntax !== undefined) { - syntax = handler.syntax; - } - - // Get source type - let sourceType: undefined | ConstSourceType; - if (options.sourceType !== undefined) { - sourceType = options.sourceType; - } else if (handler.sourceType !== undefined) { - sourceType = handler.sourceType; - } else { - sourceType = 'script'; - - if (ref.manifest !== undefined) { - const manifest = this.getPartialManifest(ref.manifest); - if (manifest.type === 'module') { - sourceType = 'module'; - } - } - } - - if (project.config.bundler.mode === 'legacy') { - sourceType = 'module'; - } - - const cacheEnabled = options.cache !== false; - - if (cacheEnabled) { - // Update the lastAccessed of the ast cache and return it, it will be evicted on - // any file change - const cachedResult: undefined | ParseJSResult = this.astCache.get(path); - if (cachedResult !== undefined) { - let useCached = true; - - if (cachedResult.ast.sourceType !== sourceType) { - useCached = false; - } - - if (useCached) { - this.astCache.set( - path, - { - ...cachedResult, - lastAccessed: Date.now(), - }, - ); - return cachedResult; - } - } - } - - this.logger.info(`Parsing:`, path); - - const stat = await lstat(path); - - const {sourceText, generated} = await handler.toJavaScript({ - file: ref, - worker: this, - project, - parseOptions: options, - }); - - let manifestPath: undefined | string; - if (ref.manifest !== undefined) { - manifestPath = this.getPartialManifest(ref.manifest).path; - } - - const ast = parseJS({ - input: sourceText, - mtime: stat.mtimeMs, - manifestPath, - path: createUnknownFilePath(uid), - sourceType, - syntax, - allowReturnOutsideFunction: sourceType === 'script', - }); - - // If the AST is corrupt then we don't under any circumstance allow it - if (ast.corrupt) { - throw new DiagnosticsError('Corrupt AST', ast.diagnostics); - } - - // Sometimes we may want to allow the "fixed" AST - const allowDiagnostics = options.allowParserDiagnostics === true; - if (!allowDiagnostics && ast.diagnostics.length > 0) { - throw new DiagnosticsError( - "AST diagnostics aren't allowed", - ast.diagnostics, - ); - } - - const res: ParseJSResult = { - ast, - lastAccessed: Date.now(), - sourceText, - project, - path, - generated, - }; - - if (cacheEnabled) { - this.astCache.set(path, res); - } - - return res; - } - - getProject(id: number): TransformProjectDefinition { - const config = this.projects.get(id); - if (config === undefined) { - throw new Error( - `Unknown project ${id}, known projects are ${this.projects.keys()}`, - ); - } - return config; - } - - async writeFile(path: AbsoluteFilePath, content: string): Promise { - // Write the file out - await writeFile(path, content); - - // We just wrote the file but the server watcher hasn't had time to notify us - this.evict(path); - } - - evict(path: AbsoluteFilePath) { - this.astCache.delete(path); - this.moduleSignatureCache.delete(path); - } - - updateManifests(manifests: WorkerPartialManifests) { - for (const {id, manifest} of manifests) { - if (manifest === undefined) { - this.partialManifests.delete(id); - } else { - this.partialManifests.set(id, manifest); - } - } - } - - updateProjects(projects: WorkerProjects) { - for (const {config, folder, id} of projects) { - if (config === undefined) { - this.projects.delete(id); - } else { - this.projects.set( - id, - { - folder: createAbsoluteFilePath(folder), - config: hydrateJSONProjectConfig(config), - }, - ); - } - } - } + const resolveGraph = async (key: string): Promise => { + const value = prefetchedModuleSignatures[key]; + if (value === undefined) { + return undefined; + } + + switch (value.type) { + case 'RESOLVED': { + this.moduleSignatureCache.set( + createUnknownFilePath(value.graph.filename), + value.graph, + ); + return value.graph; + } + + case 'OWNED': + return this.api.moduleSignatureJS( + convertTransportFileReference(value.file), + parseOptions, + ); + + case 'POINTER': + return resolveGraph(value.key); + + case 'USE_CACHED': { + const cached = this.moduleSignatureCache.get( + createUnknownFilePath(value.filename), + ); + if (cached === undefined) { + throw new Error( + `Master told us we have the export types for ${value.filename} cached but we dont!`, + ); + } + return cached; + } + } + }; + + return { + getExportTypes: async ( + origin: string, + relative: string, + ): Promise => { + return resolveGraph(`${origin}:${relative}`); + }, + libs, + }; + } + + populateDiagnosticsMtime(diagnostics: Diagnostics): Diagnostics { + return diagnostics; + } + + async readFile(path: AbsoluteFilePath): Promise { + const buffer = this.buffers.get(path); + if (buffer === undefined) { + return await readFileText(path); + } else { + return buffer; + } + } + + async parseJS( + ref: FileReference, + options: WorkerParseOptions, + ): Promise { + const path = createAbsoluteFilePath(ref.real); + + const {project: projectId, uid} = ref; + const project = this.getProject(projectId); + + // Fetch and validate extension handler + const {handler} = getFileHandlerAssert(ref.real, project.config); + if (handler.toJavaScript === undefined) { + throw new Error(`We don't know how to convert the file ${path} to js`); + } + + // Get syntax + let syntax: Array = []; + if (options.syntax !== undefined) { + syntax = options.syntax; + } else if (handler.syntax !== undefined) { + syntax = handler.syntax; + } + + // Get source type + let sourceType: undefined | ConstSourceType; + if (options.sourceType !== undefined) { + sourceType = options.sourceType; + } else if (handler.sourceType !== undefined) { + sourceType = handler.sourceType; + } else { + sourceType = 'script'; + + if (ref.manifest !== undefined) { + const manifest = this.getPartialManifest(ref.manifest); + if (manifest.type === 'module') { + sourceType = 'module'; + } + } + } + + if (project.config.bundler.mode === 'legacy') { + sourceType = 'module'; + } + + const cacheEnabled = options.cache !== false; + + if (cacheEnabled) { + // Update the lastAccessed of the ast cache and return it, it will be evicted on + // any file change + const cachedResult: undefined | ParseJSResult = this.astCache.get(path); + if (cachedResult !== undefined) { + let useCached = true; + + if (cachedResult.ast.sourceType !== sourceType) { + useCached = false; + } + + if (useCached) { + this.astCache.set( + path, + { + ...cachedResult, + lastAccessed: Date.now(), + }, + ); + return cachedResult; + } + } + } + + this.logger.info(`Parsing:`, path); + + const stat = await lstat(path); + + const {sourceText, generated} = await handler.toJavaScript({ + file: ref, + worker: this, + project, + parseOptions: options, + }); + + let manifestPath: undefined | string; + if (ref.manifest !== undefined) { + manifestPath = this.getPartialManifest(ref.manifest).path; + } + + const ast = parseJS({ + input: sourceText, + mtime: stat.mtimeMs, + manifestPath, + path: createUnknownFilePath(uid), + sourceType, + syntax, + allowReturnOutsideFunction: sourceType === 'script', + }); + + // If the AST is corrupt then we don't under any circumstance allow it + if (ast.corrupt) { + throw new DiagnosticsError('Corrupt AST', ast.diagnostics); + } + + // Sometimes we may want to allow the "fixed" AST + const allowDiagnostics = options.allowParserDiagnostics === true; + if (!allowDiagnostics && ast.diagnostics.length > 0) { + throw new DiagnosticsError("AST diagnostics aren't allowed", ast.diagnostics); + } + + const res: ParseJSResult = { + ast, + lastAccessed: Date.now(), + sourceText, + project, + path, + generated, + }; + + if (cacheEnabled) { + this.astCache.set(path, res); + } + + return res; + } + + getProject(id: number): TransformProjectDefinition { + const config = this.projects.get(id); + if (config === undefined) { + throw new Error( + `Unknown project ${id}, known projects are ${this.projects.keys()}`, + ); + } + return config; + } + + async writeFile(path: AbsoluteFilePath, content: string): Promise { + // Write the file out + await writeFile(path, content); + + // We just wrote the file but the server watcher hasn't had time to notify us + this.evict(path); + } + + evict(path: AbsoluteFilePath) { + this.astCache.delete(path); + this.moduleSignatureCache.delete(path); + } + + updateManifests(manifests: WorkerPartialManifests) { + for (const {id, manifest} of manifests) { + if (manifest === undefined) { + this.partialManifests.delete(id); + } else { + this.partialManifests.set(id, manifest); + } + } + } + + updateProjects(projects: WorkerProjects) { + for (const {config, folder, id} of projects) { + if (config === undefined) { + this.projects.delete(id); + } else { + this.projects.set( + id, + { + folder: createAbsoluteFilePath(folder), + config: hydrateJSONProjectConfig(config), + }, + ); + } + } + } } diff --git a/packages/@romejs/core/worker/WorkerAPI.ts b/packages/@romejs/core/worker/WorkerAPI.ts index f90963aa7fc..066c7104351 100644 --- a/packages/@romejs/core/worker/WorkerAPI.ts +++ b/packages/@romejs/core/worker/WorkerAPI.ts @@ -9,31 +9,31 @@ import {FileReference, Worker} from '@romejs/core'; import {AnyNode, Program} from '@romejs/js-ast'; import {Diagnostics, catchDiagnostics, descriptions} from '@romejs/diagnostics'; import { - CompileResult, - CompilerContext, - CompilerOptions, - Path, - TransformStageName, - compile, + CompileResult, + CompilerContext, + CompilerOptions, + Path, + TransformStageName, + compile, } from '@romejs/js-compiler'; import { - WorkerCompilerOptions, - WorkerFormatResult, - WorkerLintOptions, - WorkerLintResult, - WorkerParseOptions, + WorkerCompilerOptions, + WorkerFormatResult, + WorkerLintOptions, + WorkerLintResult, + WorkerParseOptions, } from '../common/bridges/WorkerBridge'; import Logger from '../common/utils/Logger'; import * as jsAnalysis from '@romejs/js-analysis'; import {ExtensionLintResult} from '../common/file-handlers/types'; import {getFileHandlerAssert} from '../common/file-handlers/index'; import { - AnalyzeDependencyResult, - UNKNOWN_ANALYZE_DEPENDENCIES_RESULT, + AnalyzeDependencyResult, + UNKNOWN_ANALYZE_DEPENDENCIES_RESULT, } from '../common/types/analyzeDependencies'; import { - InlineSnapshotUpdate, - InlineSnapshotUpdates, + InlineSnapshotUpdate, + InlineSnapshotUpdates, } from '../test-worker/SnapshotManager'; import {formatJS} from '@romejs/js-formatter'; import {getNodeReferenceParts, valueToNode} from '@romejs/js-ast-utils'; @@ -41,397 +41,391 @@ import {getNodeReferenceParts, valueToNode} from '@romejs/js-ast-utils'; // Some Windows git repos will automatically convert Unix line endings to Windows // This retains the line endings for the formatted code if they were present in the source function normalizeFormattedLineEndings( - sourceText: string, - formatted: string, + sourceText: string, + formatted: string, ): string { - if (sourceText.includes('\r')) { - return formatted.replace(/\n/g, '\r\n'); - } else { - return formatted; - } + if (sourceText.includes('\r')) { + return formatted.replace(/\n/g, '\r\n'); + } else { + return formatted; + } } export default class WorkerAPI { - constructor(worker: Worker) { - this.worker = worker; - this.logger = worker.logger; - } - - worker: Worker; - logger: Logger; - - interceptAndAddGeneratedToDiagnostics(val: T, generated: boolean): T { - if (generated) { - const diagnostics = val.diagnostics.map((diag) => { - return { - ...diag, - metadata: { - ...diag.description, - advice: [ - ...diag.description.advice, - { - type: 'log', - category: 'warn', - text: 'This diagnostic was generated on a file that has been converted to JavaScript. The source locations are most likely incorrect', - }, - ], - }, - }; - }); - - return {...val, diagnostics}; - } else { - return val; - } - } - - async moduleSignatureJS(ref: FileReference, parseOptions: WorkerParseOptions) { - const {ast, project} = await this.worker.parseJS(ref, parseOptions); - - this.logger.info(`Generating export types:`, ref.real); - - return await jsAnalysis.getModuleSignature({ - ast, - project, - provider: await this.worker.getTypeCheckProvider( - ref.project, - {}, - parseOptions, - ), - }); - } - - async updateInlineSnapshots( - ref: FileReference, - updates: InlineSnapshotUpdates, - parseOptions: WorkerParseOptions, - ): Promise { - let {ast, sourceText} = await this.worker.parseJS(ref, parseOptions); - - const appliedUpdatesToCallees: Set = new Set(); - const pendingUpdates: Set = new Set(updates); - const context = new CompilerContext({ - ast, - ref, - }); - ast = context.reduceRoot( - ast, - { - name: 'updateInlineSnapshots', - enter(path: Path): AnyNode { - const {node} = path; - if (node.type !== 'CallExpression' || pendingUpdates.size === 0) { - return node; - } - - let matchedUpdate: undefined | InlineSnapshotUpdate; - - const {callee} = node; - for (const {node} of getNodeReferenceParts(callee).parts) { - const {loc} = node; - if (loc === undefined) { - continue; - } - - for (const update of pendingUpdates) { - if ( - loc.start.column === update.column && - loc.start.line === update.line - ) { - matchedUpdate = update; - break; - } - } - - if (matchedUpdate !== undefined) { - break; - } - } - - if (matchedUpdate !== undefined) { - if (appliedUpdatesToCallees.has(callee)) { - context.addNodeDiagnostic( - node, - descriptions.SNAPSHOTS.INLINE_COLLISION, - ); - return node; - } - - pendingUpdates.delete(matchedUpdate); - appliedUpdatesToCallees.add(callee); - - const args = node.arguments; - if (args.length < 1) { - context.addNodeDiagnostic( - node, - descriptions.SNAPSHOTS.INLINE_MISSING_RECEIVED, - ); - return node; - } - - return { - ...node, - arguments: [args[0], valueToNode(matchedUpdate.snapshot)], - }; - } - - return node; - }, - }, - ); - - const diags = context.diagnostics.getDiagnostics(); - - if (pendingUpdates.size > 0 && diags.length === 0) { - throw new Error('Left over inline snapshots that were not updated'); - } - - if (diags.length === 0) { - const formatted = formatJS(ast, {sourceText}).code; - await this.worker.writeFile(ref.real, formatted); - } - - return diags; - } - - async analyzeDependencies( - ref: FileReference, - parseOptions: WorkerParseOptions, - ): Promise { - const project = this.worker.getProject(ref.project); - const {handler} = getFileHandlerAssert(ref.real, project.config); - this.logger.info(`Analyze dependencies:`, ref.real); - - const {analyzeDependencies} = handler; - if (analyzeDependencies === undefined) { - return UNKNOWN_ANALYZE_DEPENDENCIES_RESULT; - } - - return await analyzeDependencies({ - file: ref, - project, - worker: this.worker, - parseOptions, - }); - } - - async workerCompilerOptionsToCompilerOptions( - ref: FileReference, - workerOptions: WorkerCompilerOptions, - parseOptions: WorkerParseOptions, - ): Promise { - const {bundle, ...options} = workerOptions; - - if (bundle === undefined) { - return options; - } else { - return { - ...options, - bundle: { - ...bundle, - analyze: await this.analyzeDependencies(ref, parseOptions), - }, - }; - } - } - - async compileJS( - ref: FileReference, - stage: TransformStageName, - options: WorkerCompilerOptions, - parseOptions: WorkerParseOptions, - ): Promise { - const {ast, project, sourceText, generated} = await this.worker.parseJS( - ref, - parseOptions, - ); - this.logger.info(`Compiling:`, ref.real); - - const compilerOptions = await this.workerCompilerOptionsToCompilerOptions( - ref, - options, - parseOptions, - ); - return this.interceptAndAddGeneratedToDiagnostics( - await compile({ - ref, - ast, - sourceText, - options: compilerOptions, - project, - stage, - }), - generated, - ); - } - - async parseJS(ref: FileReference, opts: WorkerParseOptions): Promise { - let {ast, generated} = await this.worker.parseJS( - ref, - { - ...opts, - sourceType: opts.sourceType, - cache: false, - }, - ); - - return this.interceptAndAddGeneratedToDiagnostics(ast, generated); - } - - async format( - ref: FileReference, - opts: WorkerParseOptions, - ): Promise { - const res = await this._format(ref, opts); - if (res === undefined) { - return undefined; - } else { - return { - formatted: normalizeFormattedLineEndings(res.sourceText, res.formatted), - original: res.sourceText, - diagnostics: res.diagnostics, - }; - } - } - - async _format( - ref: FileReference, - parseOptions: WorkerParseOptions, - ): Promise { - const project = this.worker.getProject(ref.project); - this.logger.info(`Formatting:`, ref.real); - - const {handler} = getFileHandlerAssert(ref.real, project.config); - const {format} = handler; - if (format === undefined) { - return; - } - - const res = await format({ - file: ref, - project, - worker: this.worker, - parseOptions, - }); - - return res; - } - - async lint( - ref: FileReference, - options: WorkerLintOptions, - parseOptions: WorkerParseOptions, - ): Promise { - const project = this.worker.getProject(ref.project); - this.logger.info(`Linting:`, ref.real); - - // Get the extension handler - const {handler} = getFileHandlerAssert(ref.real, project.config); - - const {lint} = handler; - if (lint === undefined && handler.format === undefined) { - return { - saved: false, - diagnostics: [], - suppressions: [], - }; - } - - // Catch any diagnostics, in the case of syntax errors etc - const res = await catchDiagnostics( - () => { - if (lint === undefined) { - return this._format(ref, parseOptions); - } else { - return lint({ - file: ref, - project, - worker: this.worker, - options, - parseOptions, - }); - } - }, - { - category: 'lint', - message: 'Caught by WorkerAPI.lint', - }, - ); - - // These are fatal diagnostics - if (res.diagnostics !== undefined) { - return { - saved: false, - suppressions: [], - diagnostics: res.diagnostics, - }; - } - - // `format` could have return undefined - if (res.value === undefined) { - return { - saved: false, - diagnostics: [], - suppressions: [], - }; - } - - // These are normal diagnostics returned from the linter - const { - sourceText, - diagnostics, - suppressions, - }: ExtensionLintResult = res.value; - - const formatted = normalizeFormattedLineEndings( - sourceText, - res.value.formatted, - ); - - // If the file has pending fixes - const needsSave = formatted !== sourceText; - - // Autofix if necessary - if (options.save && needsSave) { - // Save the file and evict it from the cache - await this.worker.writeFile(ref.real, formatted); - - // Relint this file without fixing it, we do this to prevent false positive error messages - return { - ...(await this.lint(ref, {...options, save: false}, parseOptions)), - saved: true, - }; - } - - // If there's no pending fix then no need for diagnostics - if (!needsSave) { - return { - saved: false, - diagnostics, - suppressions, - }; - } - - // Add pending autofix diagnostic - return { - saved: false, - suppressions, - diagnostics: [ - ...diagnostics, - { - fixable: true, - location: { - filename: ref.uid, - }, - description: descriptions.LINT.PENDING_FIXES( - ref.relative.join(), - sourceText, - formatted, - ), - }, - ], - }; - } + constructor(worker: Worker) { + this.worker = worker; + this.logger = worker.logger; + } + + worker: Worker; + logger: Logger; + + interceptAndAddGeneratedToDiagnostics(val: T, generated: boolean): T { + if (generated) { + const diagnostics = val.diagnostics.map((diag) => { + return { + ...diag, + metadata: { + ...diag.description, + advice: [ + ...diag.description.advice, + { + type: 'log', + category: 'warn', + text: 'This diagnostic was generated on a file that has been converted to JavaScript. The source locations are most likely incorrect', + }, + ], + }, + }; + }); + + return {...val, diagnostics}; + } else { + return val; + } + } + + async moduleSignatureJS(ref: FileReference, parseOptions: WorkerParseOptions) { + const {ast, project} = await this.worker.parseJS(ref, parseOptions); + + this.logger.info(`Generating export types:`, ref.real); + + return await jsAnalysis.getModuleSignature({ + ast, + project, + provider: await this.worker.getTypeCheckProvider( + ref.project, + {}, + parseOptions, + ), + }); + } + + async updateInlineSnapshots( + ref: FileReference, + updates: InlineSnapshotUpdates, + parseOptions: WorkerParseOptions, + ): Promise { + let {ast, sourceText} = await this.worker.parseJS(ref, parseOptions); + + const appliedUpdatesToCallees: Set = new Set(); + const pendingUpdates: Set = new Set(updates); + const context = new CompilerContext({ + ast, + ref, + }); + ast = context.reduceRoot( + ast, + { + name: 'updateInlineSnapshots', + enter(path: Path): AnyNode { + const {node} = path; + if (node.type !== 'CallExpression' || pendingUpdates.size === 0) { + return node; + } + + let matchedUpdate: undefined | InlineSnapshotUpdate; + + const {callee} = node; + for (const {node} of getNodeReferenceParts(callee).parts) { + const {loc} = node; + if (loc === undefined) { + continue; + } + + for (const update of pendingUpdates) { + if (loc.start.column === update.column && loc.start.line === update.line) { + matchedUpdate = update; + break; + } + } + + if (matchedUpdate !== undefined) { + break; + } + } + + if (matchedUpdate !== undefined) { + if (appliedUpdatesToCallees.has(callee)) { + context.addNodeDiagnostic(node, descriptions.SNAPSHOTS.INLINE_COLLISION); + return node; + } + + pendingUpdates.delete(matchedUpdate); + appliedUpdatesToCallees.add(callee); + + const args = node.arguments; + if (args.length < 1) { + context.addNodeDiagnostic( + node, + descriptions.SNAPSHOTS.INLINE_MISSING_RECEIVED, + ); + return node; + } + + return { + ...node, + arguments: [args[0], valueToNode(matchedUpdate.snapshot)], + }; + } + + return node; + }, + }, + ); + + const diags = context.diagnostics.getDiagnostics(); + + if (pendingUpdates.size > 0 && diags.length === 0) { + throw new Error('Left over inline snapshots that were not updated'); + } + + if (diags.length === 0) { + const formatted = formatJS(ast, {sourceText}).code; + await this.worker.writeFile(ref.real, formatted); + } + + return diags; + } + + async analyzeDependencies( + ref: FileReference, + parseOptions: WorkerParseOptions, + ): Promise { + const project = this.worker.getProject(ref.project); + const {handler} = getFileHandlerAssert(ref.real, project.config); + this.logger.info(`Analyze dependencies:`, ref.real); + + const {analyzeDependencies} = handler; + if (analyzeDependencies === undefined) { + return UNKNOWN_ANALYZE_DEPENDENCIES_RESULT; + } + + return await analyzeDependencies({ + file: ref, + project, + worker: this.worker, + parseOptions, + }); + } + + async workerCompilerOptionsToCompilerOptions( + ref: FileReference, + workerOptions: WorkerCompilerOptions, + parseOptions: WorkerParseOptions, + ): Promise { + const {bundle, ...options} = workerOptions; + + if (bundle === undefined) { + return options; + } else { + return { + ...options, + bundle: { + ...bundle, + analyze: await this.analyzeDependencies(ref, parseOptions), + }, + }; + } + } + + async compileJS( + ref: FileReference, + stage: TransformStageName, + options: WorkerCompilerOptions, + parseOptions: WorkerParseOptions, + ): Promise { + const {ast, project, sourceText, generated} = await this.worker.parseJS( + ref, + parseOptions, + ); + this.logger.info(`Compiling:`, ref.real); + + const compilerOptions = await this.workerCompilerOptionsToCompilerOptions( + ref, + options, + parseOptions, + ); + return this.interceptAndAddGeneratedToDiagnostics( + await compile({ + ref, + ast, + sourceText, + options: compilerOptions, + project, + stage, + }), + generated, + ); + } + + async parseJS(ref: FileReference, opts: WorkerParseOptions): Promise { + let {ast, generated} = await this.worker.parseJS( + ref, + { + ...opts, + sourceType: opts.sourceType, + cache: false, + }, + ); + + return this.interceptAndAddGeneratedToDiagnostics(ast, generated); + } + + async format( + ref: FileReference, + opts: WorkerParseOptions, + ): Promise { + const res = await this._format(ref, opts); + if (res === undefined) { + return undefined; + } else { + return { + formatted: normalizeFormattedLineEndings(res.sourceText, res.formatted), + original: res.sourceText, + diagnostics: res.diagnostics, + }; + } + } + + async _format( + ref: FileReference, + parseOptions: WorkerParseOptions, + ): Promise { + const project = this.worker.getProject(ref.project); + this.logger.info(`Formatting:`, ref.real); + + const {handler} = getFileHandlerAssert(ref.real, project.config); + const {format} = handler; + if (format === undefined) { + return; + } + + const res = await format({ + file: ref, + project, + worker: this.worker, + parseOptions, + }); + + return res; + } + + async lint( + ref: FileReference, + options: WorkerLintOptions, + parseOptions: WorkerParseOptions, + ): Promise { + const project = this.worker.getProject(ref.project); + this.logger.info(`Linting:`, ref.real); + + // Get the extension handler + const {handler} = getFileHandlerAssert(ref.real, project.config); + + const {lint} = handler; + if (lint === undefined && handler.format === undefined) { + return { + saved: false, + diagnostics: [], + suppressions: [], + }; + } + + // Catch any diagnostics, in the case of syntax errors etc + const res = await catchDiagnostics( + () => { + if (lint === undefined) { + return this._format(ref, parseOptions); + } else { + return lint({ + file: ref, + project, + worker: this.worker, + options, + parseOptions, + }); + } + }, + { + category: 'lint', + message: 'Caught by WorkerAPI.lint', + }, + ); + + // These are fatal diagnostics + if (res.diagnostics !== undefined) { + return { + saved: false, + suppressions: [], + diagnostics: res.diagnostics, + }; + } + + // `format` could have return undefined + if (res.value === undefined) { + return { + saved: false, + diagnostics: [], + suppressions: [], + }; + } + + // These are normal diagnostics returned from the linter + const { + sourceText, + diagnostics, + suppressions, + }: ExtensionLintResult = res.value; + + const formatted = normalizeFormattedLineEndings( + sourceText, + res.value.formatted, + ); + + // If the file has pending fixes + const needsSave = formatted !== sourceText; + + // Autofix if necessary + if (options.save && needsSave) { + // Save the file and evict it from the cache + await this.worker.writeFile(ref.real, formatted); + + // Relint this file without fixing it, we do this to prevent false positive error messages + return { + ...(await this.lint(ref, {...options, save: false}, parseOptions)), + saved: true, + }; + } + + // If there's no pending fix then no need for diagnostics + if (!needsSave) { + return { + saved: false, + diagnostics, + suppressions, + }; + } + + // Add pending autofix diagnostic + return { + saved: false, + suppressions, + diagnostics: [ + ...diagnostics, + { + fixable: true, + location: { + filename: ref.uid, + }, + description: descriptions.LINT.PENDING_FIXES( + ref.relative.join(), + sourceText, + formatted, + ), + }, + ], + }; + } } diff --git a/packages/@romejs/diagnostics/DiagnosticsNormalizer.ts b/packages/@romejs/diagnostics/DiagnosticsNormalizer.ts index 967815d13e8..3e23080ab1f 100644 --- a/packages/@romejs/diagnostics/DiagnosticsNormalizer.ts +++ b/packages/@romejs/diagnostics/DiagnosticsNormalizer.ts @@ -8,215 +8,213 @@ import {Diagnostic, DiagnosticAdviceItem, DiagnosticLocation} from './types'; import {SourceMapConsumerCollection} from '@romejs/codec-source-map'; import { - MarkupFormatNormalizeOptions, - normalizeMarkup, + MarkupFormatNormalizeOptions, + normalizeMarkup, } from '@romejs/string-markup'; import {createBlessedDiagnosticMessage} from './descriptions'; export default class DiagnosticsNormalizer { - constructor( - markupOptions?: MarkupFormatNormalizeOptions, - sourceMaps?: SourceMapConsumerCollection, - ) { - this.sourceMaps = sourceMaps; - this.markupOptions = markupOptions || {}; - this.hasMarkupOptions = markupOptions !== undefined; - } - - sourceMaps: undefined | SourceMapConsumerCollection; - markupOptions: MarkupFormatNormalizeOptions; - hasMarkupOptions: boolean; - - normalizeFilename(filename: undefined | string): undefined | string { - const {markupOptions} = this; - if (markupOptions === undefined || filename === undefined) { - return filename; - } - const {normalizeFilename} = markupOptions; - if (normalizeFilename === undefined) { - return filename; - } - - return normalizeFilename(filename); - } - - normalizePositionValue(value: T): undefined | T { - if (this.markupOptions !== undefined && this.markupOptions.stripPositions) { - return undefined; - } else { - return value; - } - } - - normalizeLocation(location: DiagnosticLocation): DiagnosticLocation { - const {sourceMaps} = this; - if (sourceMaps === undefined) { - return location; - } - - let {marker, filename, start, end} = location; - - if (filename !== undefined) { - if (start !== undefined) { - const resolved = sourceMaps.approxOriginalPositionFor( - filename, - start.line, - start.column, - ); - if (resolved !== undefined) { - filename = resolved.source; - start = { - ...start, - line: resolved.line, - column: resolved.column, - }; - } - } - - if (end !== undefined) { - const resolved = sourceMaps.approxOriginalPositionFor( - filename, - end.line, - end.column, - ); - if (resolved !== undefined) { - // TODO confirm this is the same as `start` if it resolved - filename = resolved.source; - end = { - ...end, - line: resolved.line, - column: resolved.column, - }; - } - } - } - - return { - ...location, - filename: this.normalizeFilename(filename), - marker: this.maybeNormalizeMarkup(marker), - start: this.normalizePositionValue(start), - end: this.normalizePositionValue(end), - }; - } - - normalizeMarkup(markup: string): string { - return normalizeMarkup(markup, this.markupOptions); - } - - maybeNormalizeMarkup(markup: undefined | string): undefined | string { - return markup === undefined ? undefined : this.normalizeMarkup(markup); - } - - normalizeDiagnosticAdviceItem( - item: DiagnosticAdviceItem, - ): DiagnosticAdviceItem { - const {sourceMaps} = this; - - switch (item.type) { - case 'frame': - return { - ...item, - location: this.normalizeLocation(item.location), - }; - - case 'list': - return { - ...item, - list: item.list.map((markup) => this.normalizeMarkup(markup)), - }; - - case 'log': - return { - ...item, - text: this.normalizeMarkup(item.text), - }; - - case 'action': - if (this.markupOptions.stripPositions) { - return { - ...item, - // Command flags could have position information - commandFlags: {}, - }; - } else { - return item; - } - - case 'stacktrace': - return { - ...item, - frames: item.frames.map((frame) => { - const {filename, line, column} = frame; - - if ( - filename === undefined || - line === undefined || - column === undefined || - (sourceMaps !== undefined && !sourceMaps.has(filename)) - ) { - return { - ...frame, - start: this.normalizePositionValue(line), - column: this.normalizePositionValue(column), - filename: this.normalizeFilename(filename), - }; - } - - if (sourceMaps !== undefined) { - const resolved = sourceMaps.approxOriginalPositionFor( - filename, - line, - column, - ); - if (resolved !== undefined) { - return { - ...frame, - filename: this.normalizeFilename(resolved.source), - line: this.normalizePositionValue(resolved.line), - column: this.normalizePositionValue(resolved.column), - }; - } - } - - return frame; - }), - }; - } - - return item; - } - - normalizeDiagnostic(diag: Diagnostic): Diagnostic { - const {sourceMaps} = this; - - // Fast path for a common case - if ( - !this.hasMarkupOptions && - (sourceMaps === undefined || !sourceMaps.hasAny()) - ) { - return diag; - } - - const {description} = diag; - - const advice = description.advice.map((item) => { - return this.normalizeDiagnosticAdviceItem(item); - }); - - diag = { - ...diag, - label: this.maybeNormalizeMarkup(diag.label), - location: this.normalizeLocation(diag.location), - description: { - ...description, - message: createBlessedDiagnosticMessage( - this.normalizeMarkup(description.message.value), - ), - advice, - }, - }; - - return diag; - } + constructor( + markupOptions?: MarkupFormatNormalizeOptions, + sourceMaps?: SourceMapConsumerCollection, + ) { + this.sourceMaps = sourceMaps; + this.markupOptions = markupOptions || {}; + this.hasMarkupOptions = markupOptions !== undefined; + } + + sourceMaps: undefined | SourceMapConsumerCollection; + markupOptions: MarkupFormatNormalizeOptions; + hasMarkupOptions: boolean; + + normalizeFilename(filename: undefined | string): undefined | string { + const {markupOptions} = this; + if (markupOptions === undefined || filename === undefined) { + return filename; + } + const {normalizeFilename} = markupOptions; + if (normalizeFilename === undefined) { + return filename; + } + + return normalizeFilename(filename); + } + + normalizePositionValue(value: T): undefined | T { + if (this.markupOptions !== undefined && this.markupOptions.stripPositions) { + return undefined; + } else { + return value; + } + } + + normalizeLocation(location: DiagnosticLocation): DiagnosticLocation { + const {sourceMaps} = this; + if (sourceMaps === undefined) { + return location; + } + + let {marker, filename, start, end} = location; + + if (filename !== undefined) { + if (start !== undefined) { + const resolved = sourceMaps.approxOriginalPositionFor( + filename, + start.line, + start.column, + ); + if (resolved !== undefined) { + filename = resolved.source; + start = { + ...start, + line: resolved.line, + column: resolved.column, + }; + } + } + + if (end !== undefined) { + const resolved = sourceMaps.approxOriginalPositionFor( + filename, + end.line, + end.column, + ); + if (resolved !== undefined) { + // TODO confirm this is the same as `start` if it resolved + filename = resolved.source; + end = { + ...end, + line: resolved.line, + column: resolved.column, + }; + } + } + } + + return { + ...location, + filename: this.normalizeFilename(filename), + marker: this.maybeNormalizeMarkup(marker), + start: this.normalizePositionValue(start), + end: this.normalizePositionValue(end), + }; + } + + normalizeMarkup(markup: string): string { + return normalizeMarkup(markup, this.markupOptions); + } + + maybeNormalizeMarkup(markup: undefined | string): undefined | string { + return markup === undefined ? undefined : this.normalizeMarkup(markup); + } + + normalizeDiagnosticAdviceItem(item: DiagnosticAdviceItem): DiagnosticAdviceItem { + const {sourceMaps} = this; + + switch (item.type) { + case 'frame': + return { + ...item, + location: this.normalizeLocation(item.location), + }; + + case 'list': + return { + ...item, + list: item.list.map((markup) => this.normalizeMarkup(markup)), + }; + + case 'log': + return { + ...item, + text: this.normalizeMarkup(item.text), + }; + + case 'action': + if (this.markupOptions.stripPositions) { + return { + ...item, + // Command flags could have position information + commandFlags: {}, + }; + } else { + return item; + } + + case 'stacktrace': + return { + ...item, + frames: item.frames.map((frame) => { + const {filename, line, column} = frame; + + if ( + filename === undefined || + line === undefined || + column === undefined || + (sourceMaps !== undefined && !sourceMaps.has(filename)) + ) { + return { + ...frame, + start: this.normalizePositionValue(line), + column: this.normalizePositionValue(column), + filename: this.normalizeFilename(filename), + }; + } + + if (sourceMaps !== undefined) { + const resolved = sourceMaps.approxOriginalPositionFor( + filename, + line, + column, + ); + if (resolved !== undefined) { + return { + ...frame, + filename: this.normalizeFilename(resolved.source), + line: this.normalizePositionValue(resolved.line), + column: this.normalizePositionValue(resolved.column), + }; + } + } + + return frame; + }), + }; + } + + return item; + } + + normalizeDiagnostic(diag: Diagnostic): Diagnostic { + const {sourceMaps} = this; + + // Fast path for a common case + if ( + !this.hasMarkupOptions && + (sourceMaps === undefined || !sourceMaps.hasAny()) + ) { + return diag; + } + + const {description} = diag; + + const advice = description.advice.map((item) => { + return this.normalizeDiagnosticAdviceItem(item); + }); + + diag = { + ...diag, + label: this.maybeNormalizeMarkup(diag.label), + location: this.normalizeLocation(diag.location), + description: { + ...description, + message: createBlessedDiagnosticMessage( + this.normalizeMarkup(description.message.value), + ), + advice, + }, + }; + + return diag; + } } diff --git a/packages/@romejs/diagnostics/DiagnosticsProcessor.ts b/packages/@romejs/diagnostics/DiagnosticsProcessor.ts index 97411a7e1e0..de83ee3f935 100644 --- a/packages/@romejs/diagnostics/DiagnosticsProcessor.ts +++ b/packages/@romejs/diagnostics/DiagnosticsProcessor.ts @@ -6,12 +6,12 @@ */ import { - Diagnostic, - DiagnosticFilterWithTest, - DiagnosticOrigin, - DiagnosticSuppression, - DiagnosticSuppressions, - Diagnostics, + Diagnostic, + DiagnosticFilterWithTest, + DiagnosticOrigin, + DiagnosticSuppression, + DiagnosticSuppressions, + Diagnostics, } from './types'; import {addOriginsToDiagnostics} from './derive'; import {naturalCompare} from '@romejs/string-utils'; @@ -25,402 +25,402 @@ import DiagnosticsNormalizer from './DiagnosticsNormalizer'; import {MarkupFormatNormalizeOptions} from '@romejs/string-markup'; type UniquePart = - | 'filename' - | 'message' - | 'start.line' - | 'start.column' - | 'category'; + | 'filename' + | 'message' + | 'start.line' + | 'start.column' + | 'category'; type UniqueRule = Array; type UniqueRules = Array; export type DiagnosticsProcessorOptions = { - filters?: Array; - unique?: UniqueRules; - max?: number; - onDiagnostics?: (diags: Diagnostics) => void; - origins?: Array; - markupOptions?: MarkupFormatNormalizeOptions; + filters?: Array; + unique?: UniqueRules; + max?: number; + onDiagnostics?: (diags: Diagnostics) => void; + origins?: Array; + markupOptions?: MarkupFormatNormalizeOptions; }; const DEFAULT_UNIQUE: UniqueRules = [ - ['category', 'filename', 'message', 'start.line', 'start.column'], + ['category', 'filename', 'message', 'start.line', 'start.column'], ]; type DiagnosticsByFilename = Map; export default class DiagnosticsProcessor { - constructor(options: DiagnosticsProcessorOptions = {}) { - this.filters = []; - this.options = options; - this.includedKeys = new Set(); - this.unique = options.unique === undefined ? DEFAULT_UNIQUE : options.unique; - this.throwAfter = undefined; - this.locked = false; - this.origins = options.origins === undefined ? [] : [...options.origins]; - this.allowedUnusedSuppressionPrefixes = new Set(); - this.usedSuppressions = new Set(); - this.suppressions = new Set(); - this.sourceMaps = new SourceMapConsumerCollection(); - this.normalizer = new DiagnosticsNormalizer( - options.markupOptions, - this.sourceMaps, - ); - - this.diagnostics = []; - this.cachedDiagnostics = undefined; - } - - locked: boolean; - normalizer: DiagnosticsNormalizer; - sourceMaps: SourceMapConsumerCollection; - unique: UniqueRules; - includedKeys: Set; - diagnostics: Diagnostics; - filters: Array; - allowedUnusedSuppressionPrefixes: Set; - usedSuppressions: Set; - suppressions: Set; - options: DiagnosticsProcessorOptions; - throwAfter: undefined | number; - origins: Array; - cachedDiagnostics: undefined | Diagnostics; - - static createImmediateThrower( - origins: Array, - ): DiagnosticsProcessor { - const diagnostics = new DiagnosticsProcessor({ - origins, - onDiagnostics() { - diagnostics.maybeThrowDiagnosticsError(); - }, - }); - return diagnostics; - } - - lock() { - this.locked = true; - } - - unshiftOrigin(origin: DiagnosticOrigin) { - this.origins.unshift(origin); - } - - setThrowAfter(num: undefined | number) { - this.throwAfter = num; - } - - maybeThrowDiagnosticsError() { - if (this.hasDiagnostics()) { - throw new DiagnosticsError( - 'Thrown by DiagnosticsProcessor', - this.getDiagnostics(), - ); - } - } - - hasDiagnostics(): boolean { - return this.getDiagnostics().length > 0; - } - - assertEmpty() { - if (this.hasDiagnostics()) { - throw new Error('Expected no diagnostics for this operation'); - } - } - - addAllowedUnusedSuppressionPrefix(prefix: DiagnosticCategoryPrefix) { - this.assertEmpty(); - this.allowedUnusedSuppressionPrefixes.add(prefix); - } - - addSuppressions(suppressions: DiagnosticSuppressions) { - this.cachedDiagnostics = undefined; - for (const suppression of suppressions) { - this.suppressions.add(suppression); - } - } - - addFilters(filters: Array) { - this.cachedDiagnostics = undefined; - this.filters = this.filters.concat(filters); - } - - addFilter(filter: DiagnosticFilterWithTest) { - this.cachedDiagnostics = undefined; - this.filters.push(filter); - } - - doesMatchFilter(diag: Diagnostic): boolean { - for (const suppression of this.suppressions) { - if (matchesSuppression(diag.location, suppression)) { - this.usedSuppressions.add(suppression); - return true; - } - } - - for (const filter of this.filters) { - if ( - filter.message !== undefined && - filter.message !== diag.description.message.value - ) { - continue; - } - - if ( - filter.filename !== undefined && - filter.filename !== diag.location.filename - ) { - continue; - } - - if ( - filter.category !== undefined && - filter.category !== diag.description.category - ) { - continue; - } - - if (filter.start !== undefined && diag.location.start !== undefined) { - if ( - filter.start.line !== diag.location.start.line || - filter.start.column !== diag.location.start.column - ) { - continue; - } - } - - if ( - filter.line !== undefined && - diag.location.start !== undefined && - diag.location.start.line !== filter.line - ) { - continue; - } - - if (filter.test !== undefined && filter.test(diag)) { - continue; - } - - return true; - } - - return false; - } - - buildDedupeKeys(diag: Diagnostic): Array { - if (diag.unique) { - return []; - } - - // We don't do anything with `end` in this method, it's fairly meaningless for deduping errors - let {start} = diag.location; - - const keys: Array = []; - - for (const rule of this.unique) { - const parts = []; - - if (rule.includes('category')) { - parts.push(`category:${diag.description.category}`); - } - - if (rule.includes('filename')) { - parts.push(`filename:${String(diag.location.filename)}`); - } - - if (rule.includes('message')) { - parts.push(`message:${diag.description.message}`); - } - - if (start !== undefined) { - if (rule.includes('start.line')) { - parts.push(`start.line:${start.line}`); - } - - if (rule.includes('start.column')) { - parts.push(`start.column:${start.column}`); - } - } - - const key = parts.join(','); - keys.push(key); - } - - return keys; - } - - addDiagnosticAssert(diag: Diagnostic, origin?: DiagnosticOrigin): Diagnostic { - return this.addDiagnostics([diag], origin, true)[0]; - } - - addDiagnostic( - diag: Diagnostic, - origin?: DiagnosticOrigin, - ): undefined | Diagnostic { - return this.addDiagnostics([diag], origin)[0]; - } - - addDiagnostics( - diags: Diagnostics, - origin?: DiagnosticOrigin, - force?: boolean, - ): Diagnostics { - if (diags.length === 0) { - return diags; - } - - this.cachedDiagnostics = undefined; - - if (this.locked) { - throw new Error( - 'DiagnosticsProcessor is locked and cannot accept anymore diagnostics', - ); - } - - const {max} = this.options; - const added: Diagnostics = []; - - // Add origins to diagnostics - const origins: Array = [...this.origins]; - if (origin !== undefined) { - origins.push(origin); - } - diags = addOriginsToDiagnostics(origins, diags); - - // Filter diagnostics - diagLoop: for (let diag of diags) { - if (!force && max !== undefined && this.diagnostics.length > max) { - break; - } - - // Check before normalization - if (!force && this.doesMatchFilter(diag)) { - continue; - } - - diag = this.normalizer.normalizeDiagnostic(diag); - - // Check after normalization - if (!force && this.doesMatchFilter(diag)) { - continue; - } - - const keys = this.buildDedupeKeys(diag); - - if (!force) { - for (const key of keys) { - if (this.includedKeys.has(key)) { - continue diagLoop; - } - } - } - - this.diagnostics.push(diag); - added.push(diag); - - for (const key of keys) { - this.includedKeys.add(key); - } - } - - const {onDiagnostics} = this.options; - if (onDiagnostics !== undefined && added.length > 0) { - onDiagnostics(added); - } - - const {throwAfter} = this; - if (throwAfter !== undefined && this.diagnostics.length >= throwAfter) { - this.maybeThrowDiagnosticsError(); - } - - return added; - } - - getDiagnosticsByFilename(): DiagnosticsByFilename { - const byFilename: DiagnosticsByFilename = new Map(); - - for (const diag of this.getDiagnostics()) { - const {filename} = diag.location; - - let filenameDiagnostics = byFilename.get(filename); - if (filenameDiagnostics === undefined) { - filenameDiagnostics = []; - byFilename.set(filename, filenameDiagnostics); - } - filenameDiagnostics.push(diag); - } - - return byFilename; - } - - getDiagnostics(): Diagnostics { - const {cachedDiagnostics} = this; - if (cachedDiagnostics !== undefined) { - return cachedDiagnostics; - } - - const diagnostics: Diagnostics = [...this.diagnostics]; - - // Add errors for remaining suppressions - for (const suppression of this.suppressions) { - if (this.usedSuppressions.has(suppression)) { - continue; - } - - const [categoryPrefix] = suppression.category.split('/'); - if (this.allowedUnusedSuppressionPrefixes.has(categoryPrefix)) { - continue; - } - - diagnostics.push({ - location: suppression.commentLocation, - description: descriptions.SUPPRESSIONS.UNUSED(suppression), - }); - } - - this.cachedDiagnostics = diagnostics; - - return diagnostics; - } - - getSortedDiagnostics(): Diagnostics { - const diagnosticsByFilename = this.getDiagnosticsByFilename(); - - // Get all filenames and sort them - const filenames: Array = Array.from( - diagnosticsByFilename.keys(), - ).sort((a, b) => { - if (a === undefined || b === undefined) { - return 0; - } else { - return naturalCompare(a, b); - } - }); - - let sortedDiagnostics: Diagnostics = []; - - for (const filename of filenames) { - const fileDiagnostics = diagnosticsByFilename.get(filename); - if (fileDiagnostics === undefined) { - throw new Error('We use keys() so should be present'); - } - - // Sort all file diagnostics by location start index - const sortedFileDiagnostics = fileDiagnostics.sort((a, b) => { - const aStart = a.location.start; - const bStart = b.location.start; - if (aStart === undefined || bStart === undefined) { - return 0; - } else { - return ob1Get0(aStart.index) - ob1Get0(bStart.index); - } - }); - - sortedDiagnostics = [...sortedDiagnostics, ...sortedFileDiagnostics]; - } - - return sortedDiagnostics; - } + constructor(options: DiagnosticsProcessorOptions = {}) { + this.filters = []; + this.options = options; + this.includedKeys = new Set(); + this.unique = options.unique === undefined ? DEFAULT_UNIQUE : options.unique; + this.throwAfter = undefined; + this.locked = false; + this.origins = options.origins === undefined ? [] : [...options.origins]; + this.allowedUnusedSuppressionPrefixes = new Set(); + this.usedSuppressions = new Set(); + this.suppressions = new Set(); + this.sourceMaps = new SourceMapConsumerCollection(); + this.normalizer = new DiagnosticsNormalizer( + options.markupOptions, + this.sourceMaps, + ); + + this.diagnostics = []; + this.cachedDiagnostics = undefined; + } + + locked: boolean; + normalizer: DiagnosticsNormalizer; + sourceMaps: SourceMapConsumerCollection; + unique: UniqueRules; + includedKeys: Set; + diagnostics: Diagnostics; + filters: Array; + allowedUnusedSuppressionPrefixes: Set; + usedSuppressions: Set; + suppressions: Set; + options: DiagnosticsProcessorOptions; + throwAfter: undefined | number; + origins: Array; + cachedDiagnostics: undefined | Diagnostics; + + static createImmediateThrower( + origins: Array, + ): DiagnosticsProcessor { + const diagnostics = new DiagnosticsProcessor({ + origins, + onDiagnostics() { + diagnostics.maybeThrowDiagnosticsError(); + }, + }); + return diagnostics; + } + + lock() { + this.locked = true; + } + + unshiftOrigin(origin: DiagnosticOrigin) { + this.origins.unshift(origin); + } + + setThrowAfter(num: undefined | number) { + this.throwAfter = num; + } + + maybeThrowDiagnosticsError() { + if (this.hasDiagnostics()) { + throw new DiagnosticsError( + 'Thrown by DiagnosticsProcessor', + this.getDiagnostics(), + ); + } + } + + hasDiagnostics(): boolean { + return this.getDiagnostics().length > 0; + } + + assertEmpty() { + if (this.hasDiagnostics()) { + throw new Error('Expected no diagnostics for this operation'); + } + } + + addAllowedUnusedSuppressionPrefix(prefix: DiagnosticCategoryPrefix) { + this.assertEmpty(); + this.allowedUnusedSuppressionPrefixes.add(prefix); + } + + addSuppressions(suppressions: DiagnosticSuppressions) { + this.cachedDiagnostics = undefined; + for (const suppression of suppressions) { + this.suppressions.add(suppression); + } + } + + addFilters(filters: Array) { + this.cachedDiagnostics = undefined; + this.filters = this.filters.concat(filters); + } + + addFilter(filter: DiagnosticFilterWithTest) { + this.cachedDiagnostics = undefined; + this.filters.push(filter); + } + + doesMatchFilter(diag: Diagnostic): boolean { + for (const suppression of this.suppressions) { + if (matchesSuppression(diag.location, suppression)) { + this.usedSuppressions.add(suppression); + return true; + } + } + + for (const filter of this.filters) { + if ( + filter.message !== undefined && + filter.message !== diag.description.message.value + ) { + continue; + } + + if ( + filter.filename !== undefined && + filter.filename !== diag.location.filename + ) { + continue; + } + + if ( + filter.category !== undefined && + filter.category !== diag.description.category + ) { + continue; + } + + if (filter.start !== undefined && diag.location.start !== undefined) { + if ( + filter.start.line !== diag.location.start.line || + filter.start.column !== diag.location.start.column + ) { + continue; + } + } + + if ( + filter.line !== undefined && + diag.location.start !== undefined && + diag.location.start.line !== filter.line + ) { + continue; + } + + if (filter.test !== undefined && filter.test(diag)) { + continue; + } + + return true; + } + + return false; + } + + buildDedupeKeys(diag: Diagnostic): Array { + if (diag.unique) { + return []; + } + + // We don't do anything with `end` in this method, it's fairly meaningless for deduping errors + let {start} = diag.location; + + const keys: Array = []; + + for (const rule of this.unique) { + const parts = []; + + if (rule.includes('category')) { + parts.push(`category:${diag.description.category}`); + } + + if (rule.includes('filename')) { + parts.push(`filename:${String(diag.location.filename)}`); + } + + if (rule.includes('message')) { + parts.push(`message:${diag.description.message}`); + } + + if (start !== undefined) { + if (rule.includes('start.line')) { + parts.push(`start.line:${start.line}`); + } + + if (rule.includes('start.column')) { + parts.push(`start.column:${start.column}`); + } + } + + const key = parts.join(','); + keys.push(key); + } + + return keys; + } + + addDiagnosticAssert(diag: Diagnostic, origin?: DiagnosticOrigin): Diagnostic { + return this.addDiagnostics([diag], origin, true)[0]; + } + + addDiagnostic( + diag: Diagnostic, + origin?: DiagnosticOrigin, + ): undefined | Diagnostic { + return this.addDiagnostics([diag], origin)[0]; + } + + addDiagnostics( + diags: Diagnostics, + origin?: DiagnosticOrigin, + force?: boolean, + ): Diagnostics { + if (diags.length === 0) { + return diags; + } + + this.cachedDiagnostics = undefined; + + if (this.locked) { + throw new Error( + 'DiagnosticsProcessor is locked and cannot accept anymore diagnostics', + ); + } + + const {max} = this.options; + const added: Diagnostics = []; + + // Add origins to diagnostics + const origins: Array = [...this.origins]; + if (origin !== undefined) { + origins.push(origin); + } + diags = addOriginsToDiagnostics(origins, diags); + + // Filter diagnostics + diagLoop: for (let diag of diags) { + if (!force && max !== undefined && this.diagnostics.length > max) { + break; + } + + // Check before normalization + if (!force && this.doesMatchFilter(diag)) { + continue; + } + + diag = this.normalizer.normalizeDiagnostic(diag); + + // Check after normalization + if (!force && this.doesMatchFilter(diag)) { + continue; + } + + const keys = this.buildDedupeKeys(diag); + + if (!force) { + for (const key of keys) { + if (this.includedKeys.has(key)) { + continue diagLoop; + } + } + } + + this.diagnostics.push(diag); + added.push(diag); + + for (const key of keys) { + this.includedKeys.add(key); + } + } + + const {onDiagnostics} = this.options; + if (onDiagnostics !== undefined && added.length > 0) { + onDiagnostics(added); + } + + const {throwAfter} = this; + if (throwAfter !== undefined && this.diagnostics.length >= throwAfter) { + this.maybeThrowDiagnosticsError(); + } + + return added; + } + + getDiagnosticsByFilename(): DiagnosticsByFilename { + const byFilename: DiagnosticsByFilename = new Map(); + + for (const diag of this.getDiagnostics()) { + const {filename} = diag.location; + + let filenameDiagnostics = byFilename.get(filename); + if (filenameDiagnostics === undefined) { + filenameDiagnostics = []; + byFilename.set(filename, filenameDiagnostics); + } + filenameDiagnostics.push(diag); + } + + return byFilename; + } + + getDiagnostics(): Diagnostics { + const {cachedDiagnostics} = this; + if (cachedDiagnostics !== undefined) { + return cachedDiagnostics; + } + + const diagnostics: Diagnostics = [...this.diagnostics]; + + // Add errors for remaining suppressions + for (const suppression of this.suppressions) { + if (this.usedSuppressions.has(suppression)) { + continue; + } + + const [categoryPrefix] = suppression.category.split('/'); + if (this.allowedUnusedSuppressionPrefixes.has(categoryPrefix)) { + continue; + } + + diagnostics.push({ + location: suppression.commentLocation, + description: descriptions.SUPPRESSIONS.UNUSED(suppression), + }); + } + + this.cachedDiagnostics = diagnostics; + + return diagnostics; + } + + getSortedDiagnostics(): Diagnostics { + const diagnosticsByFilename = this.getDiagnosticsByFilename(); + + // Get all filenames and sort them + const filenames: Array = Array.from( + diagnosticsByFilename.keys(), + ).sort((a, b) => { + if (a === undefined || b === undefined) { + return 0; + } else { + return naturalCompare(a, b); + } + }); + + let sortedDiagnostics: Diagnostics = []; + + for (const filename of filenames) { + const fileDiagnostics = diagnosticsByFilename.get(filename); + if (fileDiagnostics === undefined) { + throw new Error('We use keys() so should be present'); + } + + // Sort all file diagnostics by location start index + const sortedFileDiagnostics = fileDiagnostics.sort((a, b) => { + const aStart = a.location.start; + const bStart = b.location.start; + if (aStart === undefined || bStart === undefined) { + return 0; + } else { + return ob1Get0(aStart.index) - ob1Get0(bStart.index); + } + }); + + sortedDiagnostics = [...sortedDiagnostics, ...sortedFileDiagnostics]; + } + + return sortedDiagnostics; + } } diff --git a/packages/@romejs/diagnostics/categories.ts b/packages/@romejs/diagnostics/categories.ts index f09da358b20..2ee50acc7ba 100644 --- a/packages/@romejs/diagnostics/categories.ts +++ b/packages/@romejs/diagnostics/categories.ts @@ -9,148 +9,148 @@ // all category names are defined. This allows the naming scheme to be more easily reviewed and // made consistent. export type DiagnosticCategory = - | LintDiagnosticCategory - | 'analyzeDependencies/cjsExportInES' - | 'args/fileNotFound' - | 'args/invalid' - | 'bundler/moduleCycle' - | 'bundler/topLevelAwait' - | 'compile/classes' - | 'compile/jsx' - | 'flags/invalid' - | 'format/disabled' - | 'internalError/httpServer' - | 'internalError/request' - | 'lint/disabled' - | 'lint/pendingFixes' - | 'lsp/parse' - | 'parse/js' - | 'parse/json' - | 'parse/manifest' - | 'parse/patchMatch' - | 'parse/regex' - | 'parse/semver' - | 'parse/snapshots' - | 'parse/spdxLicense' - | 'parse/stringMarkup' - | 'parse/url' - | 'parse/url/query' - | 'projectManager/incorrectConfigFilename' - | 'projectManager/missing' - | 'projectManager/nameCollision' - | 'projectManager/vscMissing' - | 'resolver/fetchFailed' - | 'resolver/importTypeMismatch' - | 'resolver/notFound' - | 'resolver/unknownExport' - | 'resolver/unsupported' - | 'suppressions/duplicate' - | 'suppressions/incorrectPrefix' - | 'suppressions/missingSpace' - | 'suppressions/missingTarget' - | 'suppressions/unused' - | 'tests/cancelled' - | 'tests/disabled' - | 'tests/failure' - | 'tests/fixtureOptions' - | 'tests/logs' - | 'tests/noneDeclared' - | 'tests/snapshots/frozen' - | 'tests/snapshots/inlineMissingReceived' - | 'tests/snapshots/inlineCollision' - | 'tests/snapshots/incorrect' - | 'tests/snapshots/missing' - | 'tests/snapshots/redundant' - | 'tests/timeout' - | 'tests/unhandledRejection' - | 'typeCheck/incompatible' - | 'typeCheck/missingCondition' - | 'typeCheck/notExhaustive' - | 'typeCheck/uncallable' - | 'typeCheck/undeclaredVariable' - | 'typeCheck/unknownImport' - | 'typeCheck/unknownProperty' - | 'vsc/dirty' - | 'v8/syntaxError'; + | LintDiagnosticCategory + | 'analyzeDependencies/cjsExportInES' + | 'args/fileNotFound' + | 'args/invalid' + | 'bundler/moduleCycle' + | 'bundler/topLevelAwait' + | 'compile/classes' + | 'compile/jsx' + | 'flags/invalid' + | 'format/disabled' + | 'internalError/httpServer' + | 'internalError/request' + | 'lint/disabled' + | 'lint/pendingFixes' + | 'lsp/parse' + | 'parse/js' + | 'parse/json' + | 'parse/manifest' + | 'parse/patchMatch' + | 'parse/regex' + | 'parse/semver' + | 'parse/snapshots' + | 'parse/spdxLicense' + | 'parse/stringMarkup' + | 'parse/url' + | 'parse/url/query' + | 'projectManager/incorrectConfigFilename' + | 'projectManager/missing' + | 'projectManager/nameCollision' + | 'projectManager/vscMissing' + | 'resolver/fetchFailed' + | 'resolver/importTypeMismatch' + | 'resolver/notFound' + | 'resolver/unknownExport' + | 'resolver/unsupported' + | 'suppressions/duplicate' + | 'suppressions/incorrectPrefix' + | 'suppressions/missingSpace' + | 'suppressions/missingTarget' + | 'suppressions/unused' + | 'tests/cancelled' + | 'tests/disabled' + | 'tests/failure' + | 'tests/fixtureOptions' + | 'tests/logs' + | 'tests/noneDeclared' + | 'tests/snapshots/frozen' + | 'tests/snapshots/inlineMissingReceived' + | 'tests/snapshots/inlineCollision' + | 'tests/snapshots/incorrect' + | 'tests/snapshots/missing' + | 'tests/snapshots/redundant' + | 'tests/timeout' + | 'tests/unhandledRejection' + | 'typeCheck/incompatible' + | 'typeCheck/missingCondition' + | 'typeCheck/notExhaustive' + | 'typeCheck/uncallable' + | 'typeCheck/undeclaredVariable' + | 'typeCheck/unknownImport' + | 'typeCheck/unknownProperty' + | 'vsc/dirty' + | 'v8/syntaxError'; export type DiagnosticCategoryPrefix = - | 'analyzeDependencies' - | 'args' - | 'bundler' - | 'compiler' - | 'flags' - | 'internalError' - | 'lint' - | 'lsp' - | 'parse' - | 'projectManager' - | 'resolver' - | 'tests' - | 'typeCheck' - | 'v8'; + | 'analyzeDependencies' + | 'args' + | 'bundler' + | 'compiler' + | 'flags' + | 'internalError' + | 'lint' + | 'lsp' + | 'parse' + | 'projectManager' + | 'resolver' + | 'tests' + | 'typeCheck' + | 'v8'; // EVERYTHING BELOW IS AUTOGENERATED. SEE SCRIPTS FOLDER FOR UPDATE SCRIPTS type LintDiagnosticCategory = - | 'lint/camelCase' - | 'lint/caseSingleStatement' - | 'lint/defaultExportSameBasename' - | 'lint/doubleEquals' - | 'lint/duplicateImportSource' - | 'lint/emptyBlocks' - | 'lint/emptyMatches' - | 'lint/getterReturn' - | 'lint/importDefaultBasename' - | 'lint/inconsiderateLanguage' - | 'lint/jsxA11yHTMLHasLang' - | 'lint/jsxA11yIframeHasTitle' - | 'lint/jsxA11yImgRedundantAlt' - | 'lint/jsxKey' - | 'lint/jsxNoCommentText' - | 'lint/negationElse' - | 'lint/noArguments' - | 'lint/noAsyncPromiseExecutor' - | 'lint/noCatchAssign' - | 'lint/noChildrenProp' - | 'lint/noCommaOperator' - | 'lint/noCompareNegZero' - | 'lint/noCondAssign' - | 'lint/noDanger' - | 'lint/noDangerWithChildren' - | 'lint/noDebugger' - | 'lint/noDelete' - | 'lint/noDeleteVars' - | 'lint/noDupeArgs' - | 'lint/noDuplicateCase' - | 'lint/noDuplicateGroupNamesInRegularExpressions' - | 'lint/noDuplicateKeys' - | 'lint/noEmptyCharacterClass' - | 'lint/noExplicitAny' - | 'lint/noExtraBooleanCast' - | 'lint/noFindDOMNode' - | 'lint/noFunctionAssign' - | 'lint/noImportAssign' - | 'lint/noLabelVar' - | 'lint/noMultipleSpacesInRegularExpressionLiterals' - | 'lint/noPosixInRegularExpression' - | 'lint/noReferenceToNonExistingGroup' - | 'lint/noSetterReturn' - | 'lint/noShadowRestrictedNames' - | 'lint/noShorthandArrayType' - | 'lint/noTemplateCurlyInString' - | 'lint/noUnsafeFinally' - | 'lint/noVar' - | 'lint/preferBlockStatements' - | 'lint/preferFunctionDeclarations' - | 'lint/preferTemplate' - | 'lint/preferWhile' - | 'lint/reactInJsxScope' - | 'lint/restrictedGlobals' - | 'lint/singleVarDeclarator' - | 'lint/sortImportExportSpecifiers' - | 'lint/sparseArray' - | 'lint/stylePropObject' - | 'lint/undeclaredVariables' - | 'lint/unsafeNegation' - | 'lint/unusedVariables' - | 'lint/voidDomElementsNoChildren'; + | 'lint/camelCase' + | 'lint/caseSingleStatement' + | 'lint/defaultExportSameBasename' + | 'lint/doubleEquals' + | 'lint/duplicateImportSource' + | 'lint/emptyBlocks' + | 'lint/emptyMatches' + | 'lint/getterReturn' + | 'lint/importDefaultBasename' + | 'lint/inconsiderateLanguage' + | 'lint/jsxA11yHTMLHasLang' + | 'lint/jsxA11yIframeHasTitle' + | 'lint/jsxA11yImgRedundantAlt' + | 'lint/jsxKey' + | 'lint/jsxNoCommentText' + | 'lint/negationElse' + | 'lint/noArguments' + | 'lint/noAsyncPromiseExecutor' + | 'lint/noCatchAssign' + | 'lint/noChildrenProp' + | 'lint/noCommaOperator' + | 'lint/noCompareNegZero' + | 'lint/noCondAssign' + | 'lint/noDanger' + | 'lint/noDangerWithChildren' + | 'lint/noDebugger' + | 'lint/noDelete' + | 'lint/noDeleteVars' + | 'lint/noDupeArgs' + | 'lint/noDuplicateCase' + | 'lint/noDuplicateGroupNamesInRegularExpressions' + | 'lint/noDuplicateKeys' + | 'lint/noEmptyCharacterClass' + | 'lint/noExplicitAny' + | 'lint/noExtraBooleanCast' + | 'lint/noFindDOMNode' + | 'lint/noFunctionAssign' + | 'lint/noImportAssign' + | 'lint/noLabelVar' + | 'lint/noMultipleSpacesInRegularExpressionLiterals' + | 'lint/noPosixInRegularExpression' + | 'lint/noReferenceToNonExistingGroup' + | 'lint/noSetterReturn' + | 'lint/noShadowRestrictedNames' + | 'lint/noShorthandArrayType' + | 'lint/noTemplateCurlyInString' + | 'lint/noUnsafeFinally' + | 'lint/noVar' + | 'lint/preferBlockStatements' + | 'lint/preferFunctionDeclarations' + | 'lint/preferTemplate' + | 'lint/preferWhile' + | 'lint/reactInJsxScope' + | 'lint/restrictedGlobals' + | 'lint/singleVarDeclarator' + | 'lint/sortImportExportSpecifiers' + | 'lint/sparseArray' + | 'lint/stylePropObject' + | 'lint/undeclaredVariables' + | 'lint/unsafeNegation' + | 'lint/unusedVariables' + | 'lint/voidDomElementsNoChildren'; diff --git a/packages/@romejs/diagnostics/constants.ts b/packages/@romejs/diagnostics/constants.ts index 3787f4b0433..d8788ddcca9 100644 --- a/packages/@romejs/diagnostics/constants.ts +++ b/packages/@romejs/diagnostics/constants.ts @@ -8,7 +8,7 @@ import {DiagnosticAdviceItem} from './types'; export const INTERNAL_ERROR_LOG_ADVICE: DiagnosticAdviceItem = { - type: 'log', - category: 'warn', - text: "This diagnostic was derived from an internal Rome error. The problem likely isn't with your code. Please report this if necessary", + type: 'log', + category: 'warn', + text: "This diagnostic was derived from an internal Rome error. The problem likely isn't with your code. Please report this if necessary", }; diff --git a/packages/@romejs/diagnostics/derive.ts b/packages/@romejs/diagnostics/derive.ts index 84ddd205f91..d78a571810f 100644 --- a/packages/@romejs/diagnostics/derive.ts +++ b/packages/@romejs/diagnostics/derive.ts @@ -6,18 +6,18 @@ */ import { - Diagnostic, - DiagnosticAdvice, - DiagnosticDescription, - DiagnosticOrigin, - Diagnostics, + Diagnostic, + DiagnosticAdvice, + DiagnosticDescription, + DiagnosticOrigin, + Diagnostics, } from './types'; import {escapeMarkup} from '@romejs/string-markup'; import { - ErrorFrames, - StructuredError, - getErrorStructure, - getSourceLocationFromErrorFrame, + ErrorFrames, + StructuredError, + getErrorStructure, + getSourceLocationFromErrorFrame, } from '@romejs/v8'; import {createBlessedDiagnosticMessage} from './descriptions'; import DiagnosticsNormalizer from './DiagnosticsNormalizer'; @@ -25,296 +25,296 @@ import {diagnosticLocationToMarkupFilelink} from './helpers'; import {RequiredProps} from '@romejs/typescript-helpers'; function normalizeArray(val: undefined | Array): Array { - if (Array.isArray(val)) { - return val; - } else { - return []; - } + if (Array.isArray(val)) { + return val; + } else { + return []; + } } export function mergeDiagnostics( - rootDiag: Diagnostic, - ...diags: Array + rootDiag: Diagnostic, + ...diags: Array ): Diagnostic { - let mergedAdvice: DiagnosticAdvice = [ - ...normalizeArray(rootDiag.description.advice), - ]; - - for (const diag of diags) { - mergedAdvice = [ - ...mergedAdvice, - ...deriveRootAdviceFromDiagnostic(diag).advice, - ...normalizeArray(diag.description.advice), - ]; - } - - return { - ...rootDiag, - description: { - ...rootDiag.description, - advice: mergedAdvice, - }, - }; + let mergedAdvice: DiagnosticAdvice = [ + ...normalizeArray(rootDiag.description.advice), + ]; + + for (const diag of diags) { + mergedAdvice = [ + ...mergedAdvice, + ...deriveRootAdviceFromDiagnostic(diag).advice, + ...normalizeArray(diag.description.advice), + ]; + } + + return { + ...rootDiag, + description: { + ...rootDiag.description, + advice: mergedAdvice, + }, + }; } export function derivePositionlessKeyFromDiagnostic(diag: Diagnostic): string { - const normalizer = new DiagnosticsNormalizer({ - stripPositions: true, - }); + const normalizer = new DiagnosticsNormalizer({ + stripPositions: true, + }); - return JSON.stringify(normalizer.normalizeDiagnostic(diag)); + return JSON.stringify(normalizer.normalizeDiagnostic(diag)); } export function deriveRootAdviceFromDiagnostic( - diag: Diagnostic, - opts: { - skipFrame: boolean; - includeHeaderInAdvice: boolean; - outdated: boolean; - } = { - skipFrame: false, - includeHeaderInAdvice: true, - outdated: false, - }, + diag: Diagnostic, + opts: { + skipFrame: boolean; + includeHeaderInAdvice: boolean; + outdated: boolean; + } = { + skipFrame: false, + includeHeaderInAdvice: true, + outdated: false, + }, ): { - advice: DiagnosticAdvice; - header: string; + advice: DiagnosticAdvice; + header: string; } { - const advice: DiagnosticAdvice = []; - const {description, fixable, location} = diag; - - let header = diagnosticLocationToMarkupFilelink(location); - - if (diag.label !== undefined) { - header += ` ${diag.label}`; - - if (description.category !== undefined) { - header += ` ${description.category}`; - } - } else { - if (description.category !== undefined) { - header += ` ${description.category}`; - } - } - - if (fixable === true) { - header += ` FIXABLE`; - } - - if (opts.outdated === true) { - header += ` OUTDATED`; - } - - if (opts.includeHeaderInAdvice === true) { - advice.push({ - type: 'log', - category: 'none', - text: header, - }); - } - - advice.push({ - type: 'log', - category: 'error', - text: description.message.value, - }); - - if (opts.skipFrame === false) { - if (location.start !== undefined && location.end !== undefined) { - advice.push({ - type: 'frame', - location: diag.location, - }); - } else if (location.marker !== undefined) { - // If we have no start/end, but we do have a marker then output is a log error - advice.push({ - type: 'log', - category: 'error', - text: location.marker, - }); - } - } - - return {header, advice}; + const advice: DiagnosticAdvice = []; + const {description, fixable, location} = diag; + + let header = diagnosticLocationToMarkupFilelink(location); + + if (diag.label !== undefined) { + header += ` ${diag.label}`; + + if (description.category !== undefined) { + header += ` ${description.category}`; + } + } else { + if (description.category !== undefined) { + header += ` ${description.category}`; + } + } + + if (fixable === true) { + header += ` FIXABLE`; + } + + if (opts.outdated === true) { + header += ` OUTDATED`; + } + + if (opts.includeHeaderInAdvice === true) { + advice.push({ + type: 'log', + category: 'none', + text: header, + }); + } + + advice.push({ + type: 'log', + category: 'error', + text: description.message.value, + }); + + if (opts.skipFrame === false) { + if (location.start !== undefined && location.end !== undefined) { + advice.push({ + type: 'frame', + location: diag.location, + }); + } else if (location.marker !== undefined) { + // If we have no start/end, but we do have a marker then output is a log error + advice.push({ + type: 'log', + category: 'error', + text: location.marker, + }); + } + } + + return {header, advice}; } type DeriveErrorDiagnosticOpts = { - description: RequiredProps, 'category'>; - label?: string; - filename?: string; - cleanFrames?: (frames: ErrorFrames) => ErrorFrames; + description: RequiredProps, 'category'>; + label?: string; + filename?: string; + cleanFrames?: (frames: ErrorFrames) => ErrorFrames; }; export function deriveDiagnosticFromErrorStructure( - struct: StructuredError, - opts: DeriveErrorDiagnosticOpts, + struct: StructuredError, + opts: DeriveErrorDiagnosticOpts, ): Diagnostic { - const {filename} = opts; - - let targetFilename: undefined | string = filename; - let targetCode = undefined; - let targetLoc = undefined; - - let {frames, message = 'Unknown error'} = struct; - - const {cleanFrames} = opts; - if (cleanFrames !== undefined) { - frames = cleanFrames(frames); - } - - // Point the target to the closest frame with a filename - for (const frame of frames) { - if (frame.filename === undefined) { - continue; - } - - targetFilename = frame.filename; - targetLoc = getSourceLocationFromErrorFrame(frame); - break; - } - - const advice = getErrorStackAdvice({ - ...struct, - frames, - }); - - return { - description: { - message: createBlessedDiagnosticMessage(escapeMarkup(message)), - ...opts.description, - advice: [...advice, ...(opts.description?.advice || [])], - }, - location: { - filename: targetFilename, - start: targetLoc === undefined ? undefined : targetLoc.start, - end: targetLoc === undefined ? undefined : targetLoc.end, - sourceText: targetCode, - }, - label: opts.label, - }; + const {filename} = opts; + + let targetFilename: undefined | string = filename; + let targetCode = undefined; + let targetLoc = undefined; + + let {frames, message = 'Unknown error'} = struct; + + const {cleanFrames} = opts; + if (cleanFrames !== undefined) { + frames = cleanFrames(frames); + } + + // Point the target to the closest frame with a filename + for (const frame of frames) { + if (frame.filename === undefined) { + continue; + } + + targetFilename = frame.filename; + targetLoc = getSourceLocationFromErrorFrame(frame); + break; + } + + const advice = getErrorStackAdvice({ + ...struct, + frames, + }); + + return { + description: { + message: createBlessedDiagnosticMessage(escapeMarkup(message)), + ...opts.description, + advice: [...advice, ...(opts.description?.advice || [])], + }, + location: { + filename: targetFilename, + start: targetLoc === undefined ? undefined : targetLoc.start, + end: targetLoc === undefined ? undefined : targetLoc.end, + sourceText: targetCode, + }, + label: opts.label, + }; } export function deriveDiagnosticFromError( - error: unknown, - opts: DeriveErrorDiagnosticOpts, + error: unknown, + opts: DeriveErrorDiagnosticOpts, ): Diagnostic { - return deriveDiagnosticFromErrorStructure(getErrorStructure(error), opts); + return deriveDiagnosticFromErrorStructure(getErrorStructure(error), opts); } export function getErrorStackAdvice( - error: StructuredError, - title?: string, + error: StructuredError, + title?: string, ): DiagnosticAdvice { - const advice: DiagnosticAdvice = []; - const {frames, stack} = error; - - if (frames.length === 0 && stack !== undefined) { - // Just in case we didn't get the frames for some reason - if (title !== undefined) { - advice.push({ - type: 'log', - category: 'info', - text: title, - }); - } - - // Remove the `message` from the `stack` - let cleanStack = stack; - let removeMessage = `${error.name}: ${error.message}`; - if (cleanStack.startsWith(removeMessage)) { - cleanStack = cleanStack.slice(removeMessage.length); - } - cleanStack = cleanStack.trim(); - - advice.push({ - type: 'log', - category: 'warn', - text: 'Raw stack trace is being displayed as we did not receive any frames', - }); - - advice.push({ - type: 'list', - list: cleanStack.split('\n').map((line) => escapeMarkup(line.trim())), - }); - } else { - const adviceFrames = frames.map((frame) => { - const { - typeName, - functionName, - methodName, - filename, - lineNumber, - columnNumber, - isEval, - isNative, - isConstructor, - isAsync, - } = frame; - - const prefixes = []; - if (isAsync) { - prefixes.push('await'); - } - if (isEval) { - prefixes.push('eval'); - } - if (isConstructor) { - prefixes.push('new'); - } - const prefix = prefixes.length === 0 ? undefined : prefixes.join(' '); - - let object = typeName; - let property = ''; - if (functionName !== undefined) { - property = functionName; - } - if (methodName !== undefined) { - property = methodName; - } - - let suffix; - if (isNative) { - suffix = 'native'; - } - - return { - suffix, - prefix, - object, - property, - filename, - line: lineNumber, - column: columnNumber, - }; - }); - - advice.push({ - type: 'stacktrace', - title, - frames: adviceFrames, - }); - } - - return advice; + const advice: DiagnosticAdvice = []; + const {frames, stack} = error; + + if (frames.length === 0 && stack !== undefined) { + // Just in case we didn't get the frames for some reason + if (title !== undefined) { + advice.push({ + type: 'log', + category: 'info', + text: title, + }); + } + + // Remove the `message` from the `stack` + let cleanStack = stack; + let removeMessage = `${error.name}: ${error.message}`; + if (cleanStack.startsWith(removeMessage)) { + cleanStack = cleanStack.slice(removeMessage.length); + } + cleanStack = cleanStack.trim(); + + advice.push({ + type: 'log', + category: 'warn', + text: 'Raw stack trace is being displayed as we did not receive any frames', + }); + + advice.push({ + type: 'list', + list: cleanStack.split('\n').map((line) => escapeMarkup(line.trim())), + }); + } else { + const adviceFrames = frames.map((frame) => { + const { + typeName, + functionName, + methodName, + filename, + lineNumber, + columnNumber, + isEval, + isNative, + isConstructor, + isAsync, + } = frame; + + const prefixes = []; + if (isAsync) { + prefixes.push('await'); + } + if (isEval) { + prefixes.push('eval'); + } + if (isConstructor) { + prefixes.push('new'); + } + const prefix = prefixes.length === 0 ? undefined : prefixes.join(' '); + + let object = typeName; + let property = ''; + if (functionName !== undefined) { + property = functionName; + } + if (methodName !== undefined) { + property = methodName; + } + + let suffix; + if (isNative) { + suffix = 'native'; + } + + return { + suffix, + prefix, + object, + property, + filename, + line: lineNumber, + column: columnNumber, + }; + }); + + advice.push({ + type: 'stacktrace', + title, + frames: adviceFrames, + }); + } + + return advice; } export function addOriginsToDiagnostics( - origins: Array, - diagnostics: Diagnostics, + origins: Array, + diagnostics: Diagnostics, ): Diagnostics { - return diagnostics.map((diag) => { - return addOriginsToDiagnostic(origins, diag); - }); + return diagnostics.map((diag) => { + return addOriginsToDiagnostic(origins, diag); + }); } export function addOriginsToDiagnostic( - origins: Array, - diag: Diagnostic, + origins: Array, + diag: Diagnostic, ): Diagnostic { - const newOrigins = - diag.origins === undefined ? origins : [...origins, ...diag.origins]; - return { - ...diag, - origins: newOrigins, - }; + const newOrigins = + diag.origins === undefined ? origins : [...origins, ...diag.origins]; + return { + ...diag, + origins: newOrigins, + }; } diff --git a/packages/@romejs/diagnostics/descriptions.ts b/packages/@romejs/diagnostics/descriptions.ts index 52c9792a75e..2acdd8fb9d8 100644 --- a/packages/@romejs/diagnostics/descriptions.ts +++ b/packages/@romejs/diagnostics/descriptions.ts @@ -6,12 +6,12 @@ */ import { - DiagnosticAdvice, - DiagnosticAdviceAction, - DiagnosticBlessedMessage, - DiagnosticDescription, - DiagnosticLocation, - DiagnosticSuppression, + DiagnosticAdvice, + DiagnosticAdviceAction, + DiagnosticBlessedMessage, + DiagnosticDescription, + DiagnosticLocation, + DiagnosticSuppression, } from './types'; import {escapeMarkup, markup} from '@romejs/string-markup'; import stringDiff from '@romejs/string-diff'; @@ -23,1650 +23,1641 @@ import {UnknownNumber} from '@romejs/ob1'; import {toKebabCase} from '@romejs/string-utils'; type DiagnosticMetadataString = Omit, 'message'> & { - message: string; + message: string; }; // The purpose of this is so that we're explicit whenever we want to create a diagnostic message outside of this file export function createBlessedDiagnosticMessage( - value: string, + value: string, ): DiagnosticBlessedMessage { - return { - type: 'PARTIAL_BLESSED_DIAGNOSTIC_MESSAGE', - value, - }; + return { + type: 'PARTIAL_BLESSED_DIAGNOSTIC_MESSAGE', + value, + }; } function join(conjunction: string, items: Array): string { - if (items.length === 0) { - return ''; - } else if (items.length === 1) { - return items[0]; - } else { - const popped = items.pop()!; - return [...items, `${conjunction} ${popped}`].join(', '); - } + if (items.length === 0) { + return ''; + } else if (items.length === 1) { + return items[0]; + } else { + const popped = items.pop()!; + return [...items, `${conjunction} ${popped}`].join(', '); + } } function andJoin(items: Array): string { - return join('and', items); + return join('and', items); } andJoin; function orJoin(items: Array): string { - return join('or', items); + return join('or', items); } function addEmphasis(items: Array): Array { - return items.map((item) => `${item}`); + return items.map((item) => `${item}`); } // rome-ignore lint/AEciilnnoptxy; type InputMessagesFactory = (...params: Array) => DiagnosticMetadataString; type InputMessagesCategory = { - [key: string]: string | DiagnosticMetadataString | InputMessagesFactory; + [key: string]: string | DiagnosticMetadataString | InputMessagesFactory; }; type InputMessages = { - [category: string]: InputMessagesCategory; + [category: string]: InputMessagesCategory; }; type OuputMessagesFactoryReturn = Omit< - Ret, - 'message' | 'advice' + Ret, + 'message' | 'advice' > & { - advice: DiagnosticAdvice; - message: DiagnosticBlessedMessage; + advice: DiagnosticAdvice; + message: DiagnosticBlessedMessage; }; type OutputMessagesFactory = ( - ...params: Parameters + ...params: Parameters ) => OuputMessagesFactoryReturn>; type OutputMessagesValue = Value extends string - ? { - message: DiagnosticBlessedMessage; - advice: DiagnosticAdvice; - } - : Value extends DiagnosticMetadataString - ? OuputMessagesFactoryReturn - : Value extends InputMessagesFactory - ? OutputMessagesFactory - : never; + ? { + message: DiagnosticBlessedMessage; + advice: DiagnosticAdvice; + } + : Value extends DiagnosticMetadataString + ? OuputMessagesFactoryReturn + : Value extends InputMessagesFactory + ? OutputMessagesFactory + : never; type OutputMessagesCategory = { - [Key in keyof Input]: OutputMessagesValue + [Key in keyof Input]: OutputMessagesValue }; type OutputMessages = { - [Key in keyof Input]: OutputMessagesCategory + [Key in keyof Input]: OutputMessagesCategory }; // This is a lot of gross meta programming function createMessages( - messages: Input, + messages: Input, ): OutputMessages { - // rome-ignore lint/noExplicitAny - const out: OutputMessages = ({} as any); + // rome-ignore lint/noExplicitAny + const out: OutputMessages = ({} as any); - for (const categoryName in messages) { - // rome-ignore lint/noExplicitAny - const category: OutputMessagesCategory = {}; - out[categoryName] = category; + for (const categoryName in messages) { + // rome-ignore lint/noExplicitAny + const category: OutputMessagesCategory = {}; + out[categoryName] = category; - const inputCategory = messages[categoryName]; - for (const key in inputCategory) { - const value = inputCategory[key]; + const inputCategory = messages[categoryName]; + for (const key in inputCategory) { + const value = inputCategory[key]; - if (typeof value === 'string') { - category[key] = { - advice: [], - message: createBlessedDiagnosticMessage(value), - }; - } else if (typeof value === 'function') { - // rome-ignore lint/noExplicitAny - const callback: InputMessagesFactory = (value as any); + if (typeof value === 'string') { + category[key] = { + advice: [], + message: createBlessedDiagnosticMessage(value), + }; + } else if (typeof value === 'function') { + // rome-ignore lint/noExplicitAny + const callback: InputMessagesFactory = (value as any); - category[key] = function(...params) { - const {message, ...ret} = callback(...params); - return { - advice: [], - ...ret, - message: createBlessedDiagnosticMessage(message), - }; - }; - } else { - // rome-ignore lint/noExplicitAny - const {message, ...obj} = (value as any); - category[key] = { - advice: [], - ...obj, - message: createBlessedDiagnosticMessage(message), - }; - } - } - } + category[key] = function(...params) { + const {message, ...ret} = callback(...params); + return { + advice: [], + ...ret, + message: createBlessedDiagnosticMessage(message), + }; + }; + } else { + // rome-ignore lint/noExplicitAny + const {message, ...obj} = (value as any); + category[key] = { + advice: [], + ...obj, + message: createBlessedDiagnosticMessage(message), + }; + } + } + } - return out; + return out; } function buildJSXOpeningAdvice( - name: string, - openingLoc: SourceLocation, + name: string, + openingLoc: SourceLocation, ): DiagnosticAdvice { - return [ - { - type: 'log', - category: 'info', - text: name === '' - ? 'Originated from this opening tag' - : `Originated from opening tag of ${name}`, - }, - { - type: 'frame', - location: openingLoc, - }, - ]; + return [ + { + type: 'log', + category: 'info', + text: name === '' + ? 'Originated from this opening tag' + : `Originated from opening tag of ${name}`, + }, + { + type: 'frame', + location: openingLoc, + }, + ]; } export const descriptions = createMessages({ - FLAGS: { - UNSUPPORTED_SHORTHANDS: `Shorthand flags are not supported`, - INCORRECT_CASED_FLAG: (flag: string) => ({ - message: `Incorrect cased flag name`, - advice: [ - { - type: 'log', - category: 'info', - text: markup`Use ${toKebabCase(flag)} instead`, - }, - ], - }), - INCORRECT_ARG_COUNT: (excessive: boolean, message: string) => ({ - message: excessive ? 'Too many arguments' : 'Missing arguments', - advice: [ - { - type: 'log', - category: 'info', - text: message, - }, - ], - }), - DISALLOWED_REVIEW_FLAG: (key: string) => ({ - message: `Flag ${key} is not allowed with review`, - }), - DISALLOWED_REQUEST_FLAG: (key: string) => ({ - message: `This command does not support the ${key} flag`, - }), - UNKNOWN_ACTION: (action: string) => ({ - message: `Unknown action ${action}`, - }), - NO_FILES_FOUND: (noun: undefined | string) => ({ - message: noun === undefined - ? 'No files found' - : `No files to ${noun} found`, - }), - }, - // @romejs/parser-core - PARSER_CORE: { - EXPECTED_SPACE: 'Expected no space between', - EXPECTED_EOF: 'Expected end of file', - UNEXPECTED_EOF: 'Unexpected end of file', - UNEXPECTED: (type: string) => ({ - message: markup`Unexpected ${type}`, - }), - UNEXPECTED_CHARACTER: (char: string) => ({ - message: markup`Unexpected character ${char}`, - }), - EXPECTED_TOKEN: (got: string, expected: string) => { - return { - message: markup`Expected token ${expected} but got ${got}`, - }; - }, - }, - // @romejs/codec-js-regexp - REGEX_PARSER: { - INVALID_CAPTURE_GROUP_MODIFIER: 'Invalid capture group modifier', - UNCLOSED_GROUP: 'Unclosed group', - UNOPENED_GROUP: 'Unopened group', - INVALID_QUANTIFIER_TARGET: 'Invalid target for quantifier', - UNKNOWN_REGEX_PART: 'Unknown regex part', - REVERSED_CHAR_SET_RANGE: 'Range values reversed. Start char code is greater than end char code', - UNCLOSED_CHAR_SET: 'Unclosed character set', - DUPLICATE_FLAG: 'Duplicate regular expression flag', - INVALID_FLAG: 'Invalid regular expression flag', - REVERSED_QUANTIFIER_RANGE: 'Quantifier minimum is greater than maximum', - NO_TARGET_QUANTIFIER: 'Nothing to repeat', - INVALID_NAMED_CAPTURE: 'Invalid named capture referenced', - UNCLOSED_NAMED_CAPTURE: 'Unclosed named capture', - }, - // @romejs/codec-json - JSON: { - SINGLE_QUOTE_USAGE: 'You can only use double quoted strings', - TRAILING_COMMA_VALUE: 'Trailing comma is only allowed after a value', - UNCLOSED_STRING: 'Unclosed string', - UNCLOSED_BLOCK_COMMENT: 'Unclosed block comment', - MISTAKEN_ARRAY_IDENTITY: 'Trying to use an array element as an object property. Did you mean to make an object?', - REDUNDANT_COMMA: 'Redundant comma', - EMPTY_INPUT_IN_JSON: 'Empty input', - PROPERTY_KEY_UNQUOTED_IN_JSON: 'Property keys must be quoted in JSON', - IMPLICIT_OBJECT_IN_JSON: 'Objects must be wrapped in curly braces in JSON', - COMMENTS_IN_JSON: "Comments aren't allowed in JSON", - TRAILING_COMMA_IN_JSON: "Trailing commas aren't allowed in JSON", - REGEX_IN_JSON: "Regular expressions aren't allowed in JSON", - UNKNOWN_WORD_IN_JSON: (word: string) => ({ - message: markup`${word} isn't a valid JSON word`, - }), - STRING_NEWLINES_IN_JSON: "Newlines aren't allowed in JSON, you insert a newline by escaping it like this \"\\n\"", - UNDEFINED_IN_JSON: "undefined isn't allowed in JSON, you could use null instead", - BIGINT_IN_JSON: "Bigints aren't allowed in JSON", - NUMERIC_SEPARATORS_IN_JSON: 'Numeric separators are not allowed in JSON', - }, - // @romejs/codec-semver - SEMVER: { - MISSING_MINOR_VERSION: 'A minor number is required for a version', - MISSING_PATCH_VERSION: 'A patch number is required for a version', - EXCESSIVE_VERSION_PARTS: 'Too many parts for version', - INVALID_QUANTIFIER_PART: 'Invalid version qualifier part', - WILDCARD_IN_VERSION: "Wildcard aren't allowed in a hard version", - INVALID_VERSION_NUMBER: "This isn't a valid version part, expected a number", - INVALID_RANGE: 'A semver range can only be defined with versions', - BARE_PIPE_WITHOUT_LOOSE: 'Bare pipes are only allowed in loose mode', - UNEXPECTED_WORD: (word: string) => ({ - message: markup`Unexpected word ${word}`, - }), - UNKNOWN_START: 'Unknown start of atom', - EXPECTED_VERSION: 'Unexpected value for version', - }, - V8: { - SYNTAX_ERROR: (message: string) => ({message, category: 'v8/syntaxError'}), - }, - // @romejs/core/master/commands/lint.ts - LINT_COMMAND: { - INVALID_DECISION_ACTION: (action: string) => ({ - message: markup`${action} is not a valid decision action`, - }), - INVALID_DECISION_PART_COUNT: (i: number) => ({ - message: `Segment ${i} contains an invalid number of decision parts`, - }), - }, - // @romejs/js-compiler - LINT: { - NO_DANGER: { - category: 'lint/noDanger', - message: 'dangerouslySetInnerHTML should be avoided', - }, - IMPORT_DEFAULT_BASENAME: (prev: string, basename: string) => ({ - category: 'lint/importDefaultBasename', - message: markup`When importing the default, use the basename ${basename}`, - advice: [ - { - type: 'log', - category: 'info', - text: 'If you really meant this then use this instead', - }, - { - type: 'code', - code: markup`import {default as ${prev}}`, - }, - ], - }), - NO_COMMA_OPERATOR: { - category: 'lint/noCommaOperator', - message: 'Avoid usage of the comma operator. It can lead to easy mistakes and ambiguous code.', - advice: [ - { - type: 'log', - category: 'info', - text: 'If you want multiple expressions then break it up.', - }, - ], - }, - NEGATION_ELSE: { - category: 'lint/negationElse', - message: 'Invert the blocks when you have a negation test', - }, - STYLE_PROP_OBJECT: { - category: 'lint/stylePropObject', - message: 'style property value must be an object.', - }, - NO_DANGER_WITH_CHILDREN: { - category: 'lint/noDangerWithChildren', - message: 'Only set one of children or props.dangerouslySetInnerHTML.', - }, - NO_FIND_DOM_NODE: { - category: 'lint/noFindDOMNode', - message: 'Do not use findDOMNode', - }, - PENDING_FIXES: ( - relativeFilename: string, - original: string, - formatted: string, - ) => ({ - category: 'lint/pendingFixes', - message: 'Pending formatting and recommended autofixes', - advice: [ - { - type: 'diff', - diff: stringDiff(original, formatted), - }, - ({ - type: 'action', - command: 'lint', - shortcut: 'f', - instruction: 'To apply fixes and formatting run', - noun: 'Apply fixes and format', - args: [relativeFilename], - commandFlags: { - save: true, - }, - } as DiagnosticAdviceAction), - ({ - type: 'action', - hidden: true, - command: 'lint', - shortcut: 'o', - instruction: 'To format this file without any fixes run', - noun: 'Only format', - args: [relativeFilename], - commandFlags: { - format: true, - }, - } as DiagnosticAdviceAction), - ], - }), - DUPLICATE_IMPORT_SOURCE: (seenLocation: DiagnosticLocation) => ({ - category: 'lint/duplicateImportSource', - message: 'This module has already been imported', - advice: [ - { - type: 'log', - category: 'info', - text: 'Previously imported here', - }, - { - type: 'frame', - location: seenLocation, - }, - ], - }), - NO_CHILDREN_PROP: { - category: 'lint/noChildrenProp', - message: 'children should not be passed as a prop', - }, - PREFER_BLOCK_STATEMENT: { - category: 'lint/preferBlockStatements', - message: 'Block statements are preferred in this position', - }, - PREFER_TEMPLATE: { - category: 'lint/preferTemplate', - message: 'Template literals are preferred over string concatenation', - }, - PREFER_WHILE: { - category: 'lint/preferWhile', - message: 'A while loop should be used over a for loop', - }, - REACT_IN_JSX_SCOPE: { - category: 'lint/reactInJsxScope', - message: `React must be in scope when using JSX`, - }, - REACT_JSX_A11Y_HTML_HAS_LANG: { - category: 'lint/jsxA11yHTMLHasLang', - message: `html elements must have a lang prop.`, - }, - REACT_JSX_A11Y_IMG_REDUNDANT_ALT: { - category: 'lint/jsxA11yImgRedundantAlt', - message: `img element alt descriptions must not contain "image", "picture", or "photo"`, - }, - REACT_JSX_VOID_DOM_ELEMENTS_NO_CHILDREN: ( - element: string, - properties: Array, - ) => ({ - category: 'lint/voidDomElementsNoChildren', - message: markup`${element} is a void element tag and must not have ${orJoin( - properties, - )}.`, - }), - REACT_JSX_NO_COMMENT_TEXT: { - category: 'lint/jsxNoCommentText', - message: 'Comments inside children should be placed in braces', - }, - REACT_JSX_A11Y_IFRAME_HAS_TITLE: { - category: 'lint/jsxA11yIframeHasTitle', - message: `iframe elements should have a title prop.`, - }, - REACT_JSX_KEY: (origin: string) => ({ - category: 'lint/jsxKey', - message: markup`Missing the "key" prop for element in ${origin}`, - }), - UNSAFE_NEGATION: { - category: 'lint/unsafeNegation', - message: 'Unsafe usage of negation operator in left side of binary expression', - }, - UNUSED_VARIABLES: (kind: string, name: string) => ({ - category: 'lint/unusedVariables', - message: markup`Unused ${kind} ${name}`, - }), - UNDECLARED_VARIABLES: (name: string) => ({ - category: 'lint/undeclaredVariables', - message: markup`Undeclared variable ${name}`, - }), - VARIABLE_CAMEL_CASE: (name: string, camelCaseName: string) => ({ - category: 'lint/camelCase', - message: markup`Variable ${name} should be camel cased as ${camelCaseName}`, - }), - IDENTIFIER_CAMEL_CASE: (name: string, camelCaseName: string) => ({ - category: 'lint/camelCase', - message: markup`Identifier ${name} should be camel cased as ${camelCaseName}`, - }), - CASE_SINGLE_STATEMENT: { - category: 'lint/caseSingleStatement', - message: 'A switch case should only have a single statement. If you want more then wrap it in a block.', - }, - INCONSIDERATE_LANGUAGE: ( - description: string, - word: string, - suggestion: string, - ) => ({ - category: 'lint/inconsiderateLanguage', - message: description, - advice: [ - { - type: 'log', - category: 'info', - text: markup`Instead of ${word} use ${suggestion}`, - }, - ], - }), - DOUBLE_EQUALS: { - category: 'lint/doubleEquals', - message: 'Use === instead of ==', - advice: [ - { - type: 'log', - category: 'info', - text: '== is only allowed when comparing against null', - }, - ], - }, - EMPTY_MATCHES: { - category: 'lint/emptyMatches', - message: 'The expression can return empty matches, and may match infinitely in some use cases', - }, - NEGATE_DOUBLE_EQUALS: { - category: 'lint/doubleEquals', - message: 'Use !== instead of !=', - advice: [ - { - type: 'log', - category: 'info', - text: '!= is only allowed when comparing against null', - }, - ], - }, - NO_CATCH_ASSIGN: { - category: 'lint/noCatchAssign', - message: "Don't reassign catch parameters", - }, - SPARSE_ARRAY: { - category: 'lint/sparseArray', - message: 'Your array contains an empty slot', - }, - SINGLE_VAR_DECLARATOR: { - category: 'lint/singleVarDeclarator', - message: 'Declare each variable separately', - }, - PREFER_FUNCTION_DECLARATIONS: { - category: 'lint/preferFunctionDeclarations', - message: 'Use a function declaration instead of a const function', - }, - NO_VAR: { - category: 'lint/noVar', - message: 'Variable declarations using `var` are disallowed, use `let` or `const` instead.', - }, - NO_SHORTHAND_ARRAY_TYPE: { - category: 'lint/noShorthandArrayType', - message: escapeMarkup('Use Array instead of shorthand T[]'), - }, - NO_UNSAFE_FINALLY: (type: string) => ({ - category: 'lint/noUnsafeFinally', - message: markup`Unsafe usage of ${type}.`, - }), - NO_TEMPLATE_CURLY_IN_STRING: { - category: 'lint/noTemplateCurlyInString', - message: `Unexpected template string expression.`, - }, - NO_SHADOW_RESTRICTED_NAMES: (name: string) => ({ - category: 'lint/noShadowRestrictedNames', - message: markup`Shadowing of global property ${name}`, - advice: [ - { - type: 'log', - category: 'info', - text: "Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.", - }, - ], - }), - NO_MULTIPLE_SPACES_IN_REGEX_LITERAL: (count: number) => ({ - category: 'lint/noMultipleSpacesInRegularExpressionLiterals', - message: 'Unclear multiple spaces in regular expression', - advice: [ - { - type: 'log', - category: 'info', - text: `It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {${String( - count, - )}}/`, - }, - ], - }), - NO_LABEL_VAR: { - category: 'lint/noLabelVar', - message: 'Labels should not be variable names', - }, - NO_IMPORT_ASSIGN: (name: string) => ({ - category: 'lint/noImportAssign', - message: markup`${name} is read-only`, - }), - NO_EXTRA_BOOLEAN_CAST: { - category: 'lint/noExtraBooleanCast', - message: `Redundant double negation.`, - }, - NO_FUNCTION_ASSIGN: { - category: 'lint/noFunctionAssign', - message: 'Reassignment of function declaration', - }, - NO_EXPLICIT_ANY: { - category: 'lint/noExplicitAny', - message: 'Unexpected any. Specify a different type.', - }, - NO_EMPTY_CHAR_SET: { - category: 'lint/noEmptyCharacterClass', - message: 'Empty character classes in regular expressions are not allowed', - }, - NO_DUPLICATE_KEYS: (key: string) => ({ - category: 'lint/noDuplicateKeys', - message: markup`Duplicate key ${key}`, - }), - NO_POSIX_IN_REGULAR_EXPRESSION: { - category: 'lint/noPosixInRegularExpression', - message: 'POSIX Character Classes and Collating Sequences are not supported in ECMAscript Regular Expressions', - }, - NO_DUPLICATE_CASE: (value: string) => ({ - category: 'lint/noDuplicateCase', - message: markup`Duplicate case ${value} not allowed.`, - }), - NO_DUPE_ARGS: (name: string) => ({ - category: 'lint/noDupeArgs', - message: markup`Duplicate argument ${name} in function definition`, - }), - NO_DELETE: { - category: 'lint/noDelete', - message: `Unexpected 'delete' operator.`, - }, - NO_DELETE_VARS: { - category: 'lint/noDeleteVars', - message: 'Variables should not be deleted.', - }, - NO_DEBUGGER: { - category: 'lint/noDebugger', - message: "Unexpected 'debugger' statement", - }, - NO_COND_ASSIGN: { - category: 'lint/noCondAssign', - message: 'Cannot assign variable in loop condition', - }, - NO_COMPARE_NEG_ZERO: (op: string) => ({ - category: 'lint/noCompareNegZero', - message: `Do not use the '${op}' operator to compare against -0`, - fixable: op === '===', - }), - NO_ASYNC_PROMISE_EXECUTOR: { - category: 'lint/noAsyncPromiseExecutor', - message: 'Promise executor functions should not be async.', - }, - GETTER_RETURN: (got: string) => ({ - category: 'lint/getterReturn', - message: `Expected a 'return' at end of a getter method but got ${got}`, - }), - NO_SETTER_RETURN: { - category: 'lint/noSetterReturn', - message: `Setter cannot return a value`, - }, - EMPTY_BLOCKS: { - category: 'lint/emptyBlocks', - message: 'Empty block', - }, - NO_ARGUMENTS: { - category: 'lint/noArguments', - message: "Use the rest parameters instead of 'arguments'", - }, - DUPLICATE_REGEX_GROUP_NAME: (name: string) => ({ - category: 'lint/noDuplicateGroupNamesInRegularExpressions', - message: `Duplicate group name ${name} in regular expression`, - }), - NO_REFERENCE_TO_NON_EXISTING_GROUP: (name: string) => ({ - category: 'lint/noReferenceToNonExistingGroup', - message: `Reference to non-existent group "${name}"`, - }), - DEFAULT_EXPORT_SAME_BASENAME: ( - { - defaultName, - defaultType, - actualFilename, - correctFilename, - }: { - defaultName: string; - defaultType: string; - actualFilename: string; - correctFilename: string; - }, - ) => { - let adviceMessage = ''; + FLAGS: { + UNSUPPORTED_SHORTHANDS: `Shorthand flags are not supported`, + INCORRECT_CASED_FLAG: (flag: string) => ({ + message: `Incorrect cased flag name`, + advice: [ + { + type: 'log', + category: 'info', + text: markup`Use ${toKebabCase(flag)} instead`, + }, + ], + }), + INCORRECT_ARG_COUNT: (excessive: boolean, message: string) => ({ + message: excessive ? 'Too many arguments' : 'Missing arguments', + advice: [ + { + type: 'log', + category: 'info', + text: message, + }, + ], + }), + DISALLOWED_REVIEW_FLAG: (key: string) => ({ + message: `Flag ${key} is not allowed with review`, + }), + DISALLOWED_REQUEST_FLAG: (key: string) => ({ + message: `This command does not support the ${key} flag`, + }), + UNKNOWN_ACTION: (action: string) => ({ + message: `Unknown action ${action}`, + }), + NO_FILES_FOUND: (noun: undefined | string) => ({ + message: noun === undefined ? 'No files found' : `No files to ${noun} found`, + }), + }, + // @romejs/parser-core + PARSER_CORE: { + EXPECTED_SPACE: 'Expected no space between', + EXPECTED_EOF: 'Expected end of file', + UNEXPECTED_EOF: 'Unexpected end of file', + UNEXPECTED: (type: string) => ({ + message: markup`Unexpected ${type}`, + }), + UNEXPECTED_CHARACTER: (char: string) => ({ + message: markup`Unexpected character ${char}`, + }), + EXPECTED_TOKEN: (got: string, expected: string) => { + return { + message: markup`Expected token ${expected} but got ${got}`, + }; + }, + }, + // @romejs/codec-js-regexp + REGEX_PARSER: { + INVALID_CAPTURE_GROUP_MODIFIER: 'Invalid capture group modifier', + UNCLOSED_GROUP: 'Unclosed group', + UNOPENED_GROUP: 'Unopened group', + INVALID_QUANTIFIER_TARGET: 'Invalid target for quantifier', + UNKNOWN_REGEX_PART: 'Unknown regex part', + REVERSED_CHAR_SET_RANGE: 'Range values reversed. Start char code is greater than end char code', + UNCLOSED_CHAR_SET: 'Unclosed character set', + DUPLICATE_FLAG: 'Duplicate regular expression flag', + INVALID_FLAG: 'Invalid regular expression flag', + REVERSED_QUANTIFIER_RANGE: 'Quantifier minimum is greater than maximum', + NO_TARGET_QUANTIFIER: 'Nothing to repeat', + INVALID_NAMED_CAPTURE: 'Invalid named capture referenced', + UNCLOSED_NAMED_CAPTURE: 'Unclosed named capture', + }, + // @romejs/codec-json + JSON: { + SINGLE_QUOTE_USAGE: 'You can only use double quoted strings', + TRAILING_COMMA_VALUE: 'Trailing comma is only allowed after a value', + UNCLOSED_STRING: 'Unclosed string', + UNCLOSED_BLOCK_COMMENT: 'Unclosed block comment', + MISTAKEN_ARRAY_IDENTITY: 'Trying to use an array element as an object property. Did you mean to make an object?', + REDUNDANT_COMMA: 'Redundant comma', + EMPTY_INPUT_IN_JSON: 'Empty input', + PROPERTY_KEY_UNQUOTED_IN_JSON: 'Property keys must be quoted in JSON', + IMPLICIT_OBJECT_IN_JSON: 'Objects must be wrapped in curly braces in JSON', + COMMENTS_IN_JSON: "Comments aren't allowed in JSON", + TRAILING_COMMA_IN_JSON: "Trailing commas aren't allowed in JSON", + REGEX_IN_JSON: "Regular expressions aren't allowed in JSON", + UNKNOWN_WORD_IN_JSON: (word: string) => ({ + message: markup`${word} isn't a valid JSON word`, + }), + STRING_NEWLINES_IN_JSON: "Newlines aren't allowed in JSON, you insert a newline by escaping it like this \"\\n\"", + UNDEFINED_IN_JSON: "undefined isn't allowed in JSON, you could use null instead", + BIGINT_IN_JSON: "Bigints aren't allowed in JSON", + NUMERIC_SEPARATORS_IN_JSON: 'Numeric separators are not allowed in JSON', + }, + // @romejs/codec-semver + SEMVER: { + MISSING_MINOR_VERSION: 'A minor number is required for a version', + MISSING_PATCH_VERSION: 'A patch number is required for a version', + EXCESSIVE_VERSION_PARTS: 'Too many parts for version', + INVALID_QUANTIFIER_PART: 'Invalid version qualifier part', + WILDCARD_IN_VERSION: "Wildcard aren't allowed in a hard version", + INVALID_VERSION_NUMBER: "This isn't a valid version part, expected a number", + INVALID_RANGE: 'A semver range can only be defined with versions', + BARE_PIPE_WITHOUT_LOOSE: 'Bare pipes are only allowed in loose mode', + UNEXPECTED_WORD: (word: string) => ({ + message: markup`Unexpected word ${word}`, + }), + UNKNOWN_START: 'Unknown start of atom', + EXPECTED_VERSION: 'Unexpected value for version', + }, + V8: { + SYNTAX_ERROR: (message: string) => ({message, category: 'v8/syntaxError'}), + }, + // @romejs/core/master/commands/lint.ts + LINT_COMMAND: { + INVALID_DECISION_ACTION: (action: string) => ({ + message: markup`${action} is not a valid decision action`, + }), + INVALID_DECISION_PART_COUNT: (i: number) => ({ + message: `Segment ${i} contains an invalid number of decision parts`, + }), + }, + // @romejs/js-compiler + LINT: { + NO_DANGER: { + category: 'lint/noDanger', + message: 'dangerouslySetInnerHTML should be avoided', + }, + IMPORT_DEFAULT_BASENAME: (prev: string, basename: string) => ({ + category: 'lint/importDefaultBasename', + message: markup`When importing the default, use the basename ${basename}`, + advice: [ + { + type: 'log', + category: 'info', + text: 'If you really meant this then use this instead', + }, + { + type: 'code', + code: markup`import {default as ${prev}}`, + }, + ], + }), + NO_COMMA_OPERATOR: { + category: 'lint/noCommaOperator', + message: 'Avoid usage of the comma operator. It can lead to easy mistakes and ambiguous code.', + advice: [ + { + type: 'log', + category: 'info', + text: 'If you want multiple expressions then break it up.', + }, + ], + }, + NEGATION_ELSE: { + category: 'lint/negationElse', + message: 'Invert the blocks when you have a negation test', + }, + STYLE_PROP_OBJECT: { + category: 'lint/stylePropObject', + message: 'style property value must be an object.', + }, + NO_DANGER_WITH_CHILDREN: { + category: 'lint/noDangerWithChildren', + message: 'Only set one of children or props.dangerouslySetInnerHTML.', + }, + NO_FIND_DOM_NODE: { + category: 'lint/noFindDOMNode', + message: 'Do not use findDOMNode', + }, + PENDING_FIXES: (relativeFilename: string, original: string, formatted: string) => ({ + category: 'lint/pendingFixes', + message: 'Pending formatting and recommended autofixes', + advice: [ + { + type: 'diff', + diff: stringDiff(original, formatted), + }, + ({ + type: 'action', + command: 'lint', + shortcut: 'f', + instruction: 'To apply fixes and formatting run', + noun: 'Apply fixes and format', + args: [relativeFilename], + commandFlags: { + save: true, + }, + } as DiagnosticAdviceAction), + ({ + type: 'action', + hidden: true, + command: 'lint', + shortcut: 'o', + instruction: 'To format this file without any fixes run', + noun: 'Only format', + args: [relativeFilename], + commandFlags: { + format: true, + }, + } as DiagnosticAdviceAction), + ], + }), + DUPLICATE_IMPORT_SOURCE: (seenLocation: DiagnosticLocation) => ({ + category: 'lint/duplicateImportSource', + message: 'This module has already been imported', + advice: [ + { + type: 'log', + category: 'info', + text: 'Previously imported here', + }, + { + type: 'frame', + location: seenLocation, + }, + ], + }), + NO_CHILDREN_PROP: { + category: 'lint/noChildrenProp', + message: 'children should not be passed as a prop', + }, + PREFER_BLOCK_STATEMENT: { + category: 'lint/preferBlockStatements', + message: 'Block statements are preferred in this position', + }, + PREFER_TEMPLATE: { + category: 'lint/preferTemplate', + message: 'Template literals are preferred over string concatenation', + }, + PREFER_WHILE: { + category: 'lint/preferWhile', + message: 'A while loop should be used over a for loop', + }, + REACT_IN_JSX_SCOPE: { + category: 'lint/reactInJsxScope', + message: `React must be in scope when using JSX`, + }, + REACT_JSX_A11Y_HTML_HAS_LANG: { + category: 'lint/jsxA11yHTMLHasLang', + message: `html elements must have a lang prop.`, + }, + REACT_JSX_A11Y_IMG_REDUNDANT_ALT: { + category: 'lint/jsxA11yImgRedundantAlt', + message: `img element alt descriptions must not contain "image", "picture", or "photo"`, + }, + REACT_JSX_VOID_DOM_ELEMENTS_NO_CHILDREN: ( + element: string, + properties: Array, + ) => ({ + category: 'lint/voidDomElementsNoChildren', + message: markup`${element} is a void element tag and must not have ${orJoin( + properties, + )}.`, + }), + REACT_JSX_NO_COMMENT_TEXT: { + category: 'lint/jsxNoCommentText', + message: 'Comments inside children should be placed in braces', + }, + REACT_JSX_A11Y_IFRAME_HAS_TITLE: { + category: 'lint/jsxA11yIframeHasTitle', + message: `iframe elements should have a title prop.`, + }, + REACT_JSX_KEY: (origin: string) => ({ + category: 'lint/jsxKey', + message: markup`Missing the "key" prop for element in ${origin}`, + }), + UNSAFE_NEGATION: { + category: 'lint/unsafeNegation', + message: 'Unsafe usage of negation operator in left side of binary expression', + }, + UNUSED_VARIABLES: (kind: string, name: string) => ({ + category: 'lint/unusedVariables', + message: markup`Unused ${kind} ${name}`, + }), + UNDECLARED_VARIABLES: (name: string) => ({ + category: 'lint/undeclaredVariables', + message: markup`Undeclared variable ${name}`, + }), + VARIABLE_CAMEL_CASE: (name: string, camelCaseName: string) => ({ + category: 'lint/camelCase', + message: markup`Variable ${name} should be camel cased as ${camelCaseName}`, + }), + IDENTIFIER_CAMEL_CASE: (name: string, camelCaseName: string) => ({ + category: 'lint/camelCase', + message: markup`Identifier ${name} should be camel cased as ${camelCaseName}`, + }), + CASE_SINGLE_STATEMENT: { + category: 'lint/caseSingleStatement', + message: 'A switch case should only have a single statement. If you want more then wrap it in a block.', + }, + INCONSIDERATE_LANGUAGE: ( + description: string, + word: string, + suggestion: string, + ) => ({ + category: 'lint/inconsiderateLanguage', + message: description, + advice: [ + { + type: 'log', + category: 'info', + text: markup`Instead of ${word} use ${suggestion}`, + }, + ], + }), + DOUBLE_EQUALS: { + category: 'lint/doubleEquals', + message: 'Use === instead of ==', + advice: [ + { + type: 'log', + category: 'info', + text: '== is only allowed when comparing against null', + }, + ], + }, + EMPTY_MATCHES: { + category: 'lint/emptyMatches', + message: 'The expression can return empty matches, and may match infinitely in some use cases', + }, + NEGATE_DOUBLE_EQUALS: { + category: 'lint/doubleEquals', + message: 'Use !== instead of !=', + advice: [ + { + type: 'log', + category: 'info', + text: '!= is only allowed when comparing against null', + }, + ], + }, + NO_CATCH_ASSIGN: { + category: 'lint/noCatchAssign', + message: "Don't reassign catch parameters", + }, + SPARSE_ARRAY: { + category: 'lint/sparseArray', + message: 'Your array contains an empty slot', + }, + SINGLE_VAR_DECLARATOR: { + category: 'lint/singleVarDeclarator', + message: 'Declare each variable separately', + }, + PREFER_FUNCTION_DECLARATIONS: { + category: 'lint/preferFunctionDeclarations', + message: 'Use a function declaration instead of a const function', + }, + NO_VAR: { + category: 'lint/noVar', + message: 'Variable declarations using `var` are disallowed, use `let` or `const` instead.', + }, + NO_SHORTHAND_ARRAY_TYPE: { + category: 'lint/noShorthandArrayType', + message: escapeMarkup('Use Array instead of shorthand T[]'), + }, + NO_UNSAFE_FINALLY: (type: string) => ({ + category: 'lint/noUnsafeFinally', + message: markup`Unsafe usage of ${type}.`, + }), + NO_TEMPLATE_CURLY_IN_STRING: { + category: 'lint/noTemplateCurlyInString', + message: `Unexpected template string expression.`, + }, + NO_SHADOW_RESTRICTED_NAMES: (name: string) => ({ + category: 'lint/noShadowRestrictedNames', + message: markup`Shadowing of global property ${name}`, + advice: [ + { + type: 'log', + category: 'info', + text: "Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.", + }, + ], + }), + NO_MULTIPLE_SPACES_IN_REGEX_LITERAL: (count: number) => ({ + category: 'lint/noMultipleSpacesInRegularExpressionLiterals', + message: 'Unclear multiple spaces in regular expression', + advice: [ + { + type: 'log', + category: 'info', + text: `It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {${String( + count, + )}}/`, + }, + ], + }), + NO_LABEL_VAR: { + category: 'lint/noLabelVar', + message: 'Labels should not be variable names', + }, + NO_IMPORT_ASSIGN: (name: string) => ({ + category: 'lint/noImportAssign', + message: markup`${name} is read-only`, + }), + NO_EXTRA_BOOLEAN_CAST: { + category: 'lint/noExtraBooleanCast', + message: `Redundant double negation.`, + }, + NO_FUNCTION_ASSIGN: { + category: 'lint/noFunctionAssign', + message: 'Reassignment of function declaration', + }, + NO_EXPLICIT_ANY: { + category: 'lint/noExplicitAny', + message: 'Unexpected any. Specify a different type.', + }, + NO_EMPTY_CHAR_SET: { + category: 'lint/noEmptyCharacterClass', + message: 'Empty character classes in regular expressions are not allowed', + }, + NO_DUPLICATE_KEYS: (key: string) => ({ + category: 'lint/noDuplicateKeys', + message: markup`Duplicate key ${key}`, + }), + NO_POSIX_IN_REGULAR_EXPRESSION: { + category: 'lint/noPosixInRegularExpression', + message: 'POSIX Character Classes and Collating Sequences are not supported in ECMAscript Regular Expressions', + }, + NO_DUPLICATE_CASE: (value: string) => ({ + category: 'lint/noDuplicateCase', + message: markup`Duplicate case ${value} not allowed.`, + }), + NO_DUPE_ARGS: (name: string) => ({ + category: 'lint/noDupeArgs', + message: markup`Duplicate argument ${name} in function definition`, + }), + NO_DELETE: { + category: 'lint/noDelete', + message: `Unexpected 'delete' operator.`, + }, + NO_DELETE_VARS: { + category: 'lint/noDeleteVars', + message: 'Variables should not be deleted.', + }, + NO_DEBUGGER: { + category: 'lint/noDebugger', + message: "Unexpected 'debugger' statement", + }, + NO_COND_ASSIGN: { + category: 'lint/noCondAssign', + message: 'Cannot assign variable in loop condition', + }, + NO_COMPARE_NEG_ZERO: (op: string) => ({ + category: 'lint/noCompareNegZero', + message: `Do not use the '${op}' operator to compare against -0`, + fixable: op === '===', + }), + NO_ASYNC_PROMISE_EXECUTOR: { + category: 'lint/noAsyncPromiseExecutor', + message: 'Promise executor functions should not be async.', + }, + GETTER_RETURN: (got: string) => ({ + category: 'lint/getterReturn', + message: `Expected a 'return' at end of a getter method but got ${got}`, + }), + NO_SETTER_RETURN: { + category: 'lint/noSetterReturn', + message: `Setter cannot return a value`, + }, + EMPTY_BLOCKS: { + category: 'lint/emptyBlocks', + message: 'Empty block', + }, + NO_ARGUMENTS: { + category: 'lint/noArguments', + message: "Use the rest parameters instead of 'arguments'", + }, + DUPLICATE_REGEX_GROUP_NAME: (name: string) => ({ + category: 'lint/noDuplicateGroupNamesInRegularExpressions', + message: `Duplicate group name ${name} in regular expression`, + }), + NO_REFERENCE_TO_NON_EXISTING_GROUP: (name: string) => ({ + category: 'lint/noReferenceToNonExistingGroup', + message: `Reference to non-existent group "${name}"`, + }), + DEFAULT_EXPORT_SAME_BASENAME: ( + { + defaultName, + defaultType, + actualFilename, + correctFilename, + }: { + defaultName: string; + defaultType: string; + actualFilename: string; + correctFilename: string; + }, + ) => { + let adviceMessage = ''; - if (defaultName === '*default*') { - adviceMessage += 'The'; - } else { - adviceMessage += `Filename should be ${correctFilename} or the`; - } + if (defaultName === '*default*') { + adviceMessage += 'The'; + } else { + adviceMessage += `Filename should be ${correctFilename} or the`; + } - adviceMessage += ` ${defaultType} name should be ${actualFilename}`; + adviceMessage += ` ${defaultType} name should be ${actualFilename}`; - return { - category: 'lint/defaultExportSameBasename', - message: `Filename and the name of a default ${defaultType} should match`, - advice: [ - { - type: 'log', - category: 'info', - text: adviceMessage, - }, - ], - }; - }, - RESTRICTED_GLOBALS: (globalName) => ({ - category: 'lint/restrictedGlobals', - message: markup`The use of the existing global variable ${globalName} is not allowed. Use local variable instead.`, - }), - SORT_EXPORT_SPECIFIERS: { - category: 'lint/sortImportExportSpecifiers', - message: `Specifiers of the export declaration should be sorted alphabetically.`, - }, - SORT_IMPORT_SPECIFIERS: { - category: 'lint/sortImportExportSpecifiers', - message: `Specifiers of the import declaration should be sorted alphabetically.`, - }, - }, - PROJECT_MANAGER: { - NO_VCS: (rootConfigLocation: undefined | DiagnosticLocation) => ({ - category: 'projectManager/vscMissing', - message: "Can't find any version control for this project", - advice: rootConfigLocation === undefined - ? [ - { - type: 'log', - category: 'info', - text: 'Version control root was set to the project root as it was not configured. To configure a different folder run', - }, - { - type: 'command', - command: 'rome config set-directory vcs.root DIRECTORY_HERE', - }, - ] - : [ - { - type: 'log', - category: 'info', - text: 'Version control root was set here', - }, - { - type: 'frame', - location: rootConfigLocation, - }, - ], - }), - DUPLICATE_PACKAGE: (packageName: string, existing: string) => ({ - category: 'projectManager/nameCollision', - message: `Duplicate package name ${packageName}`, - advice: [ - { - type: 'log', - category: 'info', - text: markup`Defined already by `, - }, - ], - }), - NOT_FOUND: { - category: 'projectManager/missing', - message: `Couldn't find a project`, - advice: [ - { - type: 'log', - category: 'info', - text: 'Run rome init in this folder to initialize a project', - }, - ], - }, - INCORRECT_CONFIG_FILENAME: (validFilenames: Array) => ({ - category: 'projectManager/incorrectConfigFilename', - message: markup`Invalid rome config filename, ${validFilenames.join( - ' or ', - )} are the only valid filename`, - }), - }, - FORMAT: { - DISABLED: { - category: 'format/disabled', - message: 'Format is disabled for this project', - // TODO advice and better error message - }, - }, - // @romejs/js-compiler - COMPILER: { - CLASSES_UNSUPPORTED: { - category: 'compile/classes', - message: "The classes transform doesn't know how to transform this", - }, - JSX_NOT_XML: { - category: 'compile/jsx', - message: 'JSX is not XML', - }, - }, - // @romejs/string-escape - STRING_ESCAPE: { - NOT_ENOUGH_CODE_POINTS: 'Not enough code point digits', - INVALID_STRING_CHARACTER: 'Invalid string character (U+0000 to U+001F)', - INVALID_HEX_DIGIT_FOR_ESCAPE: 'Invalid hex digit for unicode escape', - }, - ANALYZE_DEPENDENCIES: { - CJS_EXPORT_IN_ES: { - category: 'analyzeDependencies/cjsExportInES', - message: 'You cannot use CommonJS exports in an ES module', - }, - }, - // @romejs/string-markup - STRING_MARKUP: { - UNCLOSED_STRING: 'Unclosed string', - EXPECTED_CLOSING_TAG_NAME: 'Expected closing tag name', - UNKNOWN_START: 'Unknown child start', - EXPECTED_ATTRIBUTE_NAME: 'Expected attribute name', - INCORRECT_CLOSING_TAG_NAME: (expected: string, got: string) => ({ - message: markup`Expected to close ${expected} but found ${got}`, - }), - UNCLOSED_TAG: (tagName: string, openLocation: DiagnosticLocation) => ({ - message: markup`Unclosed ${tagName} tag`, - advice: [ - {type: 'log', category: 'info', text: 'Tag started here'}, - { - type: 'frame', - location: openLocation, - }, - ], - }), - INVALID_ATTRIBUTE_NAME_FOR_TAG: ( - tagName: string, - attributeName: string, - validAttributes: Array, - ) => ({ - message: markup`${attributeName} is not a valid attribute name for ${tagName}`, - advice: buildSuggestionAdvice(attributeName, validAttributes), - }), - UNKNOWN_TAG_NAME: (tagName: string) => ({ - message: markup`Unknown tag name ${tagName}`, - }), - RESTRICTED_CHILD: ( - tagName: string, - allowedParents: Array, - gotParentName: string = 'none', - ) => ({ - message: markup`The tag ${tagName} should only appear as a child of ${orJoin( - addEmphasis(allowedParents), - )} not ${gotParentName}`, - }), - RESTRICTED_PARENT: ( - tagName: string, - allowedChildren: Array, - gotChildName: string, - ) => ({ - message: markup`The tag ${tagName} should only contain the tags ${orJoin( - addEmphasis(allowedChildren), - )} not ${gotChildName}`, - }), - RESTRICTED_PARENT_TEXT: (tagName: string) => ({ - message: markup`The tag ${tagName} should not contain any text`, - }), - }, - // @romejs/path-match - PATH_MATCH: { - INVALID_PATTERN_SEGMENT_PART: 'Invalid pattern segment part', - INVALID_PATH_SEGMENT: 'Invalid path segment', - }, - TESTS: { - CANCELLED: { - category: 'tests/cancelled', - message: 'Test was cancelled', - }, - UNDECLARED: { - message: 'No tests declared in this file', - category: 'tests/noneDeclared', - }, - LOGS: (advice: DiagnosticAdvice) => ({ - message: 'Test file produced console logs', - category: 'tests/logs', - advice: [ - ...advice, - { - type: 'log', - category: 'info', - text: 'Only visible when this test file contains failures', - }, - ], - }), - }, - SUPPRESSIONS: { - UNUSED: (suppression: DiagnosticSuppression) => { - let description = ''; - if (suppression.startLine === suppression.endLine) { - description = `line ${suppression.startLine}`; - } else { - description += `lines ${suppression.startLine} to ${suppression.endLine}`; - } + return { + category: 'lint/defaultExportSameBasename', + message: `Filename and the name of a default ${defaultType} should match`, + advice: [ + { + type: 'log', + category: 'info', + text: adviceMessage, + }, + ], + }; + }, + RESTRICTED_GLOBALS: (globalName) => ({ + category: 'lint/restrictedGlobals', + message: markup`The use of the existing global variable ${globalName} is not allowed. Use local variable instead.`, + }), + SORT_EXPORT_SPECIFIERS: { + category: 'lint/sortImportExportSpecifiers', + message: `Specifiers of the export declaration should be sorted alphabetically.`, + }, + SORT_IMPORT_SPECIFIERS: { + category: 'lint/sortImportExportSpecifiers', + message: `Specifiers of the import declaration should be sorted alphabetically.`, + }, + }, + PROJECT_MANAGER: { + NO_VCS: (rootConfigLocation: undefined | DiagnosticLocation) => ({ + category: 'projectManager/vscMissing', + message: "Can't find any version control for this project", + advice: rootConfigLocation === undefined + ? [ + { + type: 'log', + category: 'info', + text: 'Version control root was set to the project root as it was not configured. To configure a different folder run', + }, + { + type: 'command', + command: 'rome config set-directory vcs.root DIRECTORY_HERE', + }, + ] + : [ + { + type: 'log', + category: 'info', + text: 'Version control root was set here', + }, + { + type: 'frame', + location: rootConfigLocation, + }, + ], + }), + DUPLICATE_PACKAGE: (packageName: string, existing: string) => ({ + category: 'projectManager/nameCollision', + message: `Duplicate package name ${packageName}`, + advice: [ + { + type: 'log', + category: 'info', + text: markup`Defined already by `, + }, + ], + }), + NOT_FOUND: { + category: 'projectManager/missing', + message: `Couldn't find a project`, + advice: [ + { + type: 'log', + category: 'info', + text: 'Run rome init in this folder to initialize a project', + }, + ], + }, + INCORRECT_CONFIG_FILENAME: (validFilenames: Array) => ({ + category: 'projectManager/incorrectConfigFilename', + message: markup`Invalid rome config filename, ${validFilenames.join( + ' or ', + )} are the only valid filename`, + }), + }, + FORMAT: { + DISABLED: { + category: 'format/disabled', + message: 'Format is disabled for this project', + // TODO advice and better error message + }, + }, + // @romejs/js-compiler + COMPILER: { + CLASSES_UNSUPPORTED: { + category: 'compile/classes', + message: "The classes transform doesn't know how to transform this", + }, + JSX_NOT_XML: { + category: 'compile/jsx', + message: 'JSX is not XML', + }, + }, + // @romejs/string-escape + STRING_ESCAPE: { + NOT_ENOUGH_CODE_POINTS: 'Not enough code point digits', + INVALID_STRING_CHARACTER: 'Invalid string character (U+0000 to U+001F)', + INVALID_HEX_DIGIT_FOR_ESCAPE: 'Invalid hex digit for unicode escape', + }, + ANALYZE_DEPENDENCIES: { + CJS_EXPORT_IN_ES: { + category: 'analyzeDependencies/cjsExportInES', + message: 'You cannot use CommonJS exports in an ES module', + }, + }, + // @romejs/string-markup + STRING_MARKUP: { + UNCLOSED_STRING: 'Unclosed string', + EXPECTED_CLOSING_TAG_NAME: 'Expected closing tag name', + UNKNOWN_START: 'Unknown child start', + EXPECTED_ATTRIBUTE_NAME: 'Expected attribute name', + INCORRECT_CLOSING_TAG_NAME: (expected: string, got: string) => ({ + message: markup`Expected to close ${expected} but found ${got}`, + }), + UNCLOSED_TAG: (tagName: string, openLocation: DiagnosticLocation) => ({ + message: markup`Unclosed ${tagName} tag`, + advice: [ + {type: 'log', category: 'info', text: 'Tag started here'}, + { + type: 'frame', + location: openLocation, + }, + ], + }), + INVALID_ATTRIBUTE_NAME_FOR_TAG: ( + tagName: string, + attributeName: string, + validAttributes: Array, + ) => ({ + message: markup`${attributeName} is not a valid attribute name for ${tagName}`, + advice: buildSuggestionAdvice(attributeName, validAttributes), + }), + UNKNOWN_TAG_NAME: (tagName: string) => ({ + message: markup`Unknown tag name ${tagName}`, + }), + RESTRICTED_CHILD: ( + tagName: string, + allowedParents: Array, + gotParentName: string = 'none', + ) => ({ + message: markup`The tag ${tagName} should only appear as a child of ${orJoin( + addEmphasis(allowedParents), + )} not ${gotParentName}`, + }), + RESTRICTED_PARENT: ( + tagName: string, + allowedChildren: Array, + gotChildName: string, + ) => ({ + message: markup`The tag ${tagName} should only contain the tags ${orJoin( + addEmphasis(allowedChildren), + )} not ${gotChildName}`, + }), + RESTRICTED_PARENT_TEXT: (tagName: string) => ({ + message: markup`The tag ${tagName} should not contain any text`, + }), + }, + // @romejs/path-match + PATH_MATCH: { + INVALID_PATTERN_SEGMENT_PART: 'Invalid pattern segment part', + INVALID_PATH_SEGMENT: 'Invalid path segment', + }, + TESTS: { + CANCELLED: { + category: 'tests/cancelled', + message: 'Test was cancelled', + }, + UNDECLARED: { + message: 'No tests declared in this file', + category: 'tests/noneDeclared', + }, + LOGS: (advice: DiagnosticAdvice) => ({ + message: 'Test file produced console logs', + category: 'tests/logs', + advice: [ + ...advice, + { + type: 'log', + category: 'info', + text: 'Only visible when this test file contains failures', + }, + ], + }), + }, + SUPPRESSIONS: { + UNUSED: (suppression: DiagnosticSuppression) => { + let description = ''; + if (suppression.startLine === suppression.endLine) { + description = `line ${suppression.startLine}`; + } else { + description += `lines ${suppression.startLine} to ${suppression.endLine}`; + } - return { - message: 'Unused suppression. Did not hide any errors.', - category: 'suppressions/unused', - advice: [ - { - type: 'log', - category: 'info', - text: `This suppression should hide ${description}`, - }, - ], - }; - }, - MISSING_SPACE: { - category: 'suppressions/missingSpace', - message: 'Missing space between prefix and suppression categories', - }, - MISSING_TARGET: { - category: 'suppressions/missingTarget', - message: 'We could not find a target for this suppression', - }, - DUPLICATE: (category: string) => ({ - category: 'suppressions/duplicate', - message: markup`Duplicate suppression category ${category}`, - }), - }, - SNAPSHOTS: { - MISSING_NEWLINE_AFTER_CODE_BLOCK: 'Newline required after code block', - MISSING_NEWLINE_BEFORE_CODE_BLOCK: 'Newline required before code block end', - UNCLOSED_CODE_BLOCK: 'Unclosed code block', - EXPECTED_CODE_BLOCK_AFTER_HEADING: 'Expected a code block after this heading', - REDUNDANT: { - category: 'tests/snapshots/redundant', - message: 'Snapshot should not exist', - }, - MISSING: { - category: 'tests/snapshots/missing', - message: 'Snapshot does not exist', - }, - INCORRECT: (expected: string, got: string) => ({ - category: 'tests/snapshots/incorrect', - message: 'Snapshots do not match', - advice: [ - { - type: 'diff', - diff: stringDiff(expected, got), - }, - ], - }), - INLINE_COLLISION: { - category: 'tests/snapshots/inlineCollision', - message: 'Trying to update this inline snapshot multiple times', - advice: [ - { - type: 'log', - category: 'info', - text: 't.inlineSnapshot can only be called once. Did you call it in a loop?', - }, - ], - }, - INLINE_MISSING_RECEIVED: { - category: 'tests/snapshots/inlineMissingReceived', - message: 'This inline snapshot call does not have a received argument', - }, - INLINE_FROZEN: { - category: 'tests/snapshots/frozen', - message: 'Inline snapshot cannot be updated as snapshots are frozen', - }, - FROZEN: { - category: 'tests/snapshots/frozen', - message: 'Snapshot cannot be updated as snapshots are frozen', - }, - INLINE_BAD_MATCH: { - category: 'tests/snapshots/incorrect', - message: 'Inline snapshots do not match', - }, - }, - BUNDLER: { - TOP_LEVEL_AWAIT_IN_LEGACY: { - category: 'bundler/topLevelAwait', - message: "This module contains a top level await which isn't supported in wrapper mode", - }, - DETECTED_CYCLE: ( - localName: string, - target: string, - culprit: string, - path: Array, - ) => { - function formatPart(part: string, index?: number): string { - const tagged = ``; - if (part === culprit) { - return `${tagged}`; - } else if (part === target) { - return `${tagged}`; - } else if (index === 0) { - return `${tagged} ENTRY`; - } else { - return tagged; - } - } + return { + message: 'Unused suppression. Did not hide any errors.', + category: 'suppressions/unused', + advice: [ + { + type: 'log', + category: 'info', + text: `This suppression should hide ${description}`, + }, + ], + }; + }, + MISSING_SPACE: { + category: 'suppressions/missingSpace', + message: 'Missing space between prefix and suppression categories', + }, + MISSING_TARGET: { + category: 'suppressions/missingTarget', + message: 'We could not find a target for this suppression', + }, + DUPLICATE: (category: string) => ({ + category: 'suppressions/duplicate', + message: markup`Duplicate suppression category ${category}`, + }), + }, + SNAPSHOTS: { + MISSING_NEWLINE_AFTER_CODE_BLOCK: 'Newline required after code block', + MISSING_NEWLINE_BEFORE_CODE_BLOCK: 'Newline required before code block end', + UNCLOSED_CODE_BLOCK: 'Unclosed code block', + EXPECTED_CODE_BLOCK_AFTER_HEADING: 'Expected a code block after this heading', + REDUNDANT: { + category: 'tests/snapshots/redundant', + message: 'Snapshot should not exist', + }, + MISSING: { + category: 'tests/snapshots/missing', + message: 'Snapshot does not exist', + }, + INCORRECT: (expected: string, got: string) => ({ + category: 'tests/snapshots/incorrect', + message: 'Snapshots do not match', + advice: [ + { + type: 'diff', + diff: stringDiff(expected, got), + }, + ], + }), + INLINE_COLLISION: { + category: 'tests/snapshots/inlineCollision', + message: 'Trying to update this inline snapshot multiple times', + advice: [ + { + type: 'log', + category: 'info', + text: 't.inlineSnapshot can only be called once. Did you call it in a loop?', + }, + ], + }, + INLINE_MISSING_RECEIVED: { + category: 'tests/snapshots/inlineMissingReceived', + message: 'This inline snapshot call does not have a received argument', + }, + INLINE_FROZEN: { + category: 'tests/snapshots/frozen', + message: 'Inline snapshot cannot be updated as snapshots are frozen', + }, + FROZEN: { + category: 'tests/snapshots/frozen', + message: 'Snapshot cannot be updated as snapshots are frozen', + }, + INLINE_BAD_MATCH: { + category: 'tests/snapshots/incorrect', + message: 'Inline snapshots do not match', + }, + }, + BUNDLER: { + TOP_LEVEL_AWAIT_IN_LEGACY: { + category: 'bundler/topLevelAwait', + message: "This module contains a top level await which isn't supported in wrapper mode", + }, + DETECTED_CYCLE: ( + localName: string, + target: string, + culprit: string, + path: Array, + ) => { + function formatPart(part: string, index?: number): string { + const tagged = ``; + if (part === culprit) { + return `${tagged}`; + } else if (part === target) { + return `${tagged}`; + } else if (index === 0) { + return `${tagged} ENTRY`; + } else { + return tagged; + } + } - return { - category: 'bundler/moduleCycle', - message: `The variable ${localName} won't be initialized yet`, - advice: [ - { - type: 'log', - category: 'info', - text: 'This is because the module it belongs to wont be executed yet. This is due to a circular dependency creating a module cycle.', - }, - { - type: 'log', - category: 'info', - text: `The likely cause is the file ${formatPart(culprit)} that was required by ${formatPart( - target, - )} which created a circular dependency:`, - }, - { - type: 'list', - reverse: true, - ordered: true, - list: path.map(formatPart), - }, - ], - }; - }, - }, - RESOLVER: { - NOT_FOUND: ( - responseType: ResolverQueryResponseNotFound['type'], - source: string, - location: DiagnosticLocation, - ) => { - let messagePrefix = ''; - let category: DiagnosticCategory = 'resolver/notFound'; + return { + category: 'bundler/moduleCycle', + message: `The variable ${localName} won't be initialized yet`, + advice: [ + { + type: 'log', + category: 'info', + text: 'This is because the module it belongs to wont be executed yet. This is due to a circular dependency creating a module cycle.', + }, + { + type: 'log', + category: 'info', + text: `The likely cause is the file ${formatPart(culprit)} that was required by ${formatPart( + target, + )} which created a circular dependency:`, + }, + { + type: 'list', + reverse: true, + ordered: true, + list: path.map(formatPart), + }, + ], + }; + }, + }, + RESOLVER: { + NOT_FOUND: ( + responseType: ResolverQueryResponseNotFound['type'], + source: string, + location: DiagnosticLocation, + ) => { + let messagePrefix = ''; + let category: DiagnosticCategory = 'resolver/notFound'; - switch (responseType) { - case 'UNSUPPORTED': { - messagePrefix = `Unsupported`; - category = 'resolver/unsupported'; - break; - } - case 'MISSING': { - messagePrefix = `Cannot find`; - break; - } - case 'FETCH_ERROR': { - messagePrefix = 'Failed to fetch'; - category = 'resolver/fetchFailed'; - break; - } - } + switch (responseType) { + case 'UNSUPPORTED': { + messagePrefix = `Unsupported`; + category = 'resolver/unsupported'; + break; + } + case 'MISSING': { + messagePrefix = `Cannot find`; + break; + } + case 'FETCH_ERROR': { + messagePrefix = 'Failed to fetch'; + category = 'resolver/fetchFailed'; + break; + } + } - return { - message: messagePrefix + - markup` ${source} from `, - category, - }; - }, - IMPORT_TYPE_MISMATCH: ( - exportName: string, - source: string, - importedAsKing: string, - actualKind: string, - exportLoc: undefined | SourceLocation, - ) => ({ - category: 'resolver/importTypeMismatch', - message: `The export ${exportName} in was incorrectly imported as a ${importedAsKing} when it's actually a ${actualKind}`, - advice: exportLoc && [ - { - type: 'log', - category: 'info', - text: `Export was defined here in `, - }, - { - type: 'frame', - location: exportLoc, - }, - ], - }), - UNKNOWN_EXPORT: ( - name: string, - source: string, - exportedNames: Array, - formatExportedName: ( - name: string, - ) => { - location: undefined | DiagnosticLocation; - source: undefined | string; - }, - ) => ({ - message: `Couldn't find export ${name} in `, - category: 'resolver/unknownExport', - advice: exportedNames.length === 0 - ? [ - { - type: 'log', - category: 'info', - text: "This file doesn't have any exports", - }, - ] - : buildSuggestionAdvice( - name, - exportedNames, - { - formatItem: (name) => { - const {location, source} = formatExportedName(name); + return { + message: messagePrefix + + markup` ${source} from `, + category, + }; + }, + IMPORT_TYPE_MISMATCH: ( + exportName: string, + source: string, + importedAsKing: string, + actualKind: string, + exportLoc: undefined | SourceLocation, + ) => ({ + category: 'resolver/importTypeMismatch', + message: `The export ${exportName} in was incorrectly imported as a ${importedAsKing} when it's actually a ${actualKind}`, + advice: exportLoc && [ + { + type: 'log', + category: 'info', + text: `Export was defined here in `, + }, + { + type: 'frame', + location: exportLoc, + }, + ], + }), + UNKNOWN_EXPORT: ( + name: string, + source: string, + exportedNames: Array, + formatExportedName: ( + name: string, + ) => { + location: undefined | DiagnosticLocation; + source: undefined | string; + }, + ) => ({ + message: `Couldn't find export ${name} in `, + category: 'resolver/unknownExport', + advice: exportedNames.length === 0 + ? [ + { + type: 'log', + category: 'info', + text: "This file doesn't have any exports", + }, + ] + : buildSuggestionAdvice( + name, + exportedNames, + { + formatItem: (name) => { + const {location, source} = formatExportedName(name); - if (location !== undefined) { - if (location.start === undefined) { - name = markup`${name}`; - } else { - name = markup`${name}`; - } - } + if (location !== undefined) { + if (location.start === undefined) { + name = markup`${name}`; + } else { + name = markup`${name}`; + } + } - if (source !== undefined) { - name += markup` (from )`; - } + if (source !== undefined) { + name += markup` (from )`; + } - return name; - }, - }, - ), - }), - UNKNOWN_EXPORT_POSSIBLE_UNEXPORTED_LOCAL: ( - name: string, - source: string, - location: SourceLocation, - ) => ({ - message: markup`Couldn't find export ${name} in `, - category: 'resolver/unknownExport', - advice: [ - { - type: 'log', - category: 'info', - text: markup`However we found a matching local variable in . Did you forget to export it?`, - }, - { - type: 'frame', - location, - }, - ], - }), - }, - SPDX: { - UNKNOWN_LICENSE: (id: string, knownLicenses: Array) => ({ - message: markup`Unknown SPDX license ${id}`, - advice: buildSuggestionAdvice(id, knownLicenses), - }), - VALID_LICENSE_WITH_MISSING_DASH: (possibleCorrectLicense: string) => ({ - message: `Missing dash between SPDX license name and version`, - advice: [ - { - type: 'log', - category: 'info', - text: `Did you mean ${possibleCorrectLicense}?`, - }, - ], - }), - WITH_RIGHT_LICENSE_ONLY: 'Only a license id can be on the right side of a WITH', - OPERATOR_NOT_BETWEEN_EXPRESSION: 'Can only use AND/OR in between an expression', - PLUS_NOT_AFTER_LICENSE: 'A plus can only come after a license id', - UNOPENED_PAREN: 'Nothing open to close', - }, - // @romejs/js-parser - JS_PARSER: { - UNTERMINATED_BLOCK_COMMENT: 'Unterminated comment', - UNTERMINATED_JSX_STRING: 'Unterminated string constant', - INVALID_UNICODE_ESCAPE: 'Invalid Unicode escape', - EXPECTED_UNICODE_ESCAPE: 'Expecting Unicode escape sequence \\uXXXX', - BAD_HEX_ESCAPE: 'Bad character escape sequence', - OCTAL_IN_STRICT_MODE: 'Octal literal in strict mode', - UNTERMINATED_TEMPLATE: 'Unterminated template', - UNTERMINATED_STRING: 'Unterminated string constant', - OUT_OF_BOUND_CODE_POINT: 'Code point out of bounds', - IDENTIFIER_AFTER_NUMBER: 'Identifier directly after number', - OCTAL_BIGINT: "A bigint can't be an octal", - DECIMAL_BIGINT: "A bigint can't have a decimal", - INVALID_NUMBER: 'Invalid number', - LEGACY_OCTAL_IN_STRICT_MODE: 'Legacy octal literals are not allowed in strict mode', - INVALID_INT_TOKEN: 'Invalid or unexpected int token', - UNICODE_ESCAPE_IN_REGEX_FLAGS: "Regular expression flags can't contain unicode escapes", - UNTERMINATED_REGEX: 'Unterminated regular expression', - DANGLING_BACKSLASH_IN_REGEX: 'Dangling backslash in a regular expression', - EXPECTED_RELATIONAL_OPERATOR: 'Expected relational operator', - UNEXPECTED_SPACE: 'Unexpected space', - EXPECTED_SEMI_OR_LINE_TERMINATOR: 'Expected a semicolon or a line terminator', - GET_SET_CLASS_CONSTRUCTOR: "Constructor can't have get/set modifier", - ASYNC_CLASS_CONSTRUCTOR: 'Constructor cannot be async', - GENERATOR_CLASS_CONSTRUCTOR: 'Constructor cannot be a generator', - DUPLICATE_CLASS_CONSTRUCTOR: 'Duplicate constructor in the same class', - UNKNOWN_CLASS_PROPERTY_START: 'Unknown class property start', - CLASS_STATIC_PROTOTYPE_PROPERTY: 'Classes may not have static property named prototype', - CLASS_PRIVATE_FIELD_NAMED_CONSTRUCTOR: "Classes may not have a private field named '#constructor'", - CLASS_PROPERTY_NAME_CONSTRUCTOR: "Classes may not have a non-static field named 'constructor'", - PROTO_PROP_REDEFINITION: 'Redefinition of __proto__ property', - MISSING_CONDITIONAL_SEPARATOR: 'Missing conditional expression consequent separator', - WRAP_EXPONENTIATION: 'Illegal expression. Wrap left hand side or entire exponentiation in parentheses.', - DELETE_LOCAL_VARIABLE_IN_STRICT: 'Deleting local variable in strict mode', - DELETE_PRIVATE_FIELD: 'Deleting a private field is not allowed', - TAGGED_TEMPLATE_IN_OPTIONAL_CHAIN: 'Tagged Template Literals are not allowed in optionalChain', - YIELD_NAME_IN_GENERATOR: "Can not use 'yield' as identifier inside a generator", - AWAIT_NAME_IN_ASYNC: "Can not use 'await' as identifier inside an async function", - EMPTY_PARENTHESIZED_EXPRESSION: 'Parenthesized expression didnt contain anything', - AWAIT_IN_ASYNC_PARAMS: 'await is not allowed in async function parameters', - YIELD_IN_GENERATOR_PARAMS: 'yield is not allowed in generator parameters', - FLOW_TYPE_CAST_IN_TS: "Flow type cast expressions aren't allowed in TypeScript", - PARENTHESIZED_FUNCTION_PARAMS: "Function parameters can't be parenthesized", - NEW_WITH_TYPESCRIPT_TYPE_ARGUMENTS_NO_PARENS: 'In TypeScript, a new expression with type arguments must have parens', - INVALID_TEMPLATE_ESCAPE: 'Invalid escape sequence in template', - EXPECTED_IDENTIFIER: 'Expected an identifier', - IMPORT_EXACT_ARGUMENTS: 'import() requires exactly one argument', - IMPORT_TRAILING_COMMA: 'Trailing comma is disallowed inside import(...) arguments', - IMPORT_SPREAD: 'Spread is not allowed in import()', - IMPORT_NEW_CALLEE: 'Cannot use new with import(...)', - SUPER_OUTSIDE_METHOD: 'super is only allowed in object methods and classes', - INVALID_SUPER_SUFFIX: 'Invalid super suffix operator', - AWAIT_OUTSIDE_ASYNC: "Can't use await outside of an async function", - AWAIT_STAR: 'await* has been removed from the async functions proposal. Use Promise.all() instead.', - NEW_TARGET_OUTSIDE_CLASS: 'new.target can only be used in functions or class properties', - MULTIPLE_DESTRUCTURING_RESTS: 'Cannot have multiple rest elements when destructuring', - TRAILING_COMMA_AFTER_REST: 'A trailing comma is not permitted after the rest element', - GETTER_WITH_PARAMS: 'getter should have no parameters', - SETTER_WITH_REST: 'setter function argument must not be a rest parameter', - SETTER_NOT_ONE_PARAM: 'setter should have exactly one param', - ASYNC_GETTER_SETTER: "An object setter/getter can't be async", - GENERATOR_GETTER_SETTER: "An object setter/getter can't be a generator", - ARGUMENTS_IN_CLASS_FIELD: "'arguments' is not allowed in class field initializer", - NON_SIMPLE_PARAM_IN_EXPLICIT_STRICT_FUNCTION: 'Non-simple parameter in strict mode', - STRICT_DIRECTIVE_IN_NON_SIMPLE_PARAMS: "Illegal 'use strict' directive in function with non-simple parameter list", - OBJECT_PROPERTY_WITH_TYPE_PARAMETERS: 'Object property cannot have type parameters', - ILLEGAL_VARIANCE: 'Variance is not allowed here', - OBJECT_METHOD_IN_PATTERN: "Object methods aren't allowed in object patterns", - IMPORT_META_OUTSIDE_MODULE: `import.meta may only appear in a module`, - EXPECTED_ARROW_AFTER_ASYNC_TYPE_PARAMS: 'Expected arrow because we are a possible async arrow and type annotated parameters were present', - INVALID_OBJECT_PATTERN_PROP: 'Invalid property node for object pattern', - ASYNC_OBJECT_METHOD_LINE_BREAK: "There shouldn't be any newlines between async and the rest of the function", - SPACE_BETWEEN_PRIVATE_HASH: 'Unexpected space between # and identifier', - CONFUSING_CALL_ARGUMENT: 'Function parameter type annotation? Possibly forgot curlies around an object. Possibly forgot async keyword.', - EXPECTED_ARROW_AFTER_TYPE_PARAMS: 'Expected an arrow function after this type parameter declaration', - REQUIRED_CLASS_NAME: 'Class name is required', - JSX_ELEM_TYPE_ARGUMENTS_OUTSIDE_TS: 'JSX element type arguments are only allowed in TS', - UNWRAPPED_ADJACENT_JHX: `Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...?`, - CONFUSED_OR: 'Unexpected ||, did you mean just |?', - INVALID_ASSIGNMENT_TARGET: 'Not a valid assignment target', - IMPORT_KIND_SPECIFIER_ON_IMPORT_DECLARATION_WITH_KIND: 'The `type` and `typeof` keywords on named imports can only be used on regular `import` statements. It cannot be used with `import type` or `import typeof` statements', - DESTRUCTURING_IN_IMPORT: 'ES2015 named imports do not destructure. Use another statement for destructuring after the import.', - IMPORT_TYPE_STAR: 'import * is not allowed', - IMPORT_MISSING_SOURCE: 'import missing a source', - EXPORT_TYPE_NAMESPACE: "Can't have a type export namespacer specifier", - EXPORT_MISSING_FROM: 'Expected `from` for an export node', - EXPORT_FROM_NOT_STRING: 'Export from only allows strings', - BINDING_MEMBER_EXPRESSION: 'Binding member expression', - INVALID_OBJECT_PATTERN_PROPERTY: 'Not a valid assignment object pattern property', - OBJECT_PATTERN_CANNOT_CONTAIN_METHODS: 'Object pattern cannot contains methods', - INVALID_ASSIGNMENT_PATTERN_OPERATOR: "Only '=' operator can be used for specifying default value.", - INVALID_OBJECT_REST_ARGUMENT: "Invalid rest operator's argument", - INVALID_EXPORT_DEFAULT: 'Only expressions, functions or classes are allowed as the `default` export.', - INVALID_EXPORT_DECLARATION: 'Invalid export declaration', - DESTRUCTURING_REST_ELEMENT_NOT_LAST: `The rest element has to be the last element when destructuring`, - REST_INVALID_ARGUMENT: "Invalid rest operator's argument", - EXPORT_ASYNC_NO_FUNCTION_KEYWORD: 'Started with `export async` so we expected to receive an async function but no function keyword was found', - TYPE_CAST_WITHOUT_ANNOTATION: 'Type cast expression has no type annotation. Did you mean for this to be a function parameter?', - TYPE_CAST_CANNOT_BE_OPTIONAL: 'Type cast expressions cannot be optional. Did you mean for this to be a function parameter?', - TYPE_CAST_EXPECTED_PARENS: 'The type cast expression is expected to be wrapped with parentheses', - INVALID_ASYNC_ARROW_WITH_TYPE_PARAMS: 'Invalid async arrow with type parameters', - TYPE_NUMERIC_LITERAL_PLUS: 'Numeric literal type annotations cannot stand with a +, omit it instead', - TYPE_NUMERIC_LITERAL_EXPECTED: `Unexpected token, expected "number"`, - JSX_INVALID_ATTRIBUTE_VALUE: 'JSX attribute value should be either an expression or a quoted JSX text', - JSX_UNCLOSED_SELF_CLOSING_TAG: 'Unclosed JSX element open', - JSX_UNCLOSED_CLOSING_TAG: 'Unclosed JSX element close', - JSX_EMPTY_ATTRIBUTE_VALUE: 'JSX attribute cannot be an empty expression', - JSX_UNKNOWN_IDENTIFIER_TOKEN: 'Unknown JSX identifier token', - TS_IMPORT_ARG_NOT_STRING: 'Argument in a type import must be a string literal', - TS_CONSTANT_NOT_LITERAL: 'Only literal values are allowed as a constant type', - TS_INVALID_SIGNATURE_BINDING_NODE: 'Invalid node in signature binding list', - TS_REQUIRED_FOLLOWS_OPTIONAL: 'A required element cannot follow an optional element.', - TS_TEMPLATE_LITERAL_WITH_SUBSTITUION: 'Template literal types cannot have any substitution', - TS_UNKNOWN_NON_ARRAY_START: 'Unknown TS non array type start', - TS_INVALID_READONLY_MODIFIER: "'readonly' type modifier is only permitted on array and tuple literal types.", - TS_EXTERNAL_MODULE_REFERENCE_ARG_NOT_STRING: 'TypeScript require() must have a single string argument', - TS_UNKNOWN_DECLARE_START: 'Unknown TypeScript declare start', - TS_UNEXPECTED_CAST_IN_PARAMETER_POSITION: 'Unexpected type cast in parameter position', - TS_DISABLED_BUT_ACCESSIBILITY_OR_READONLY: 'Accessibility and readonly syntax found but TS is not enabled', - TS_PARAMETER_PROPERTY_BINDING_PATTERN: 'A parameter property may not be declared using a binding pattern.', - TYPE_ANNOTATION_AFTER_ASSIGNMENT: 'Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`', - TYPE_BINDING_PARAMETER_OPTIONAL: 'A binding pattern parameter cannot be optional in an implementation signature.', - ILLEGAL_FUNCTION_IN_STRICT: 'In strict mode code, functions can only be declared at top level or inside a block', - ILLEGAL_FUNCTION_IN_NON_STRICT: 'In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement', - ILLEGAL_GENERATOR_DEFINITION: 'Generators can only be declared at the top level or inside a block', - ILLEGAL_ASYNC_DEFINITION: 'Async functions can only be declared at the top level or inside a block', - LEXICAL_DECLARATION_IN_SINGLE_STATEMENT_CONTEXT: 'Lexical declaration cannot appear in a single-statement context', - IMPORT_EXPORT_MUST_TOP_LEVEL: "'import' and 'export' may only appear at the top level", - REGULAR_FOR_AWAIT: "Can't have an await on a regular for loop", - RETURN_OUTSIDE_FUNCTION: "'return' outside of function", - MULTIPLE_DEFAULT_CASE: 'Multiple default clauses', - SWITCH_STATEMENT_OUTSIDE_CASE: 'Statement outside of a case or default block', - NEWLINE_AFTER_THROW: 'Illegal newline after throw', - TRY_MISSING_FINALLY_OR_CATCH: 'Missing catch or finally clause', - INVALID_LABEL_DECLARATION: 'Invalid labeled declaration', - WITH_IN_STRICT: "'with' in strict mode", - OCTAL_IN_STRICT: 'Octal literal in strict mode', - FOR_IN_OF_WITH_INITIALIZER: 'Loop variable declaration may not have an initializer', - CONST_WITHOUT_INITIALIZER: 'A constant must have an initializer', - COMPLEX_BINDING_WITHOUT_INITIALIZER: 'Complex binding patterns require an initialization value', - ACCESSOR_WITH_TYPE_PARAMS: 'An accessor cannot have type parameters', - UNEXPECTED_SPREAD: 'Unexpected spread', - DUPLICATE_LABEL: (label: string, loc: undefined | SourceLocation) => ({ - message: markup`Label ${label} is already declared`, - advice: buildDuplicateLocationAdvice([loc]), - }), - UNKNOWN_LABEL: (label: undefined | string) => ({ - message: label === undefined - ? 'No loop label found' - : markup`Unknown label ${label}`, - }), - IMPORT_EXPORT_IN_SCRIPT: (manifestPath: string) => ({ - message: `import and export can only appear in a module`, - advice: [ - // TODO this advice is pointless if you have syntax extensions enabled - { - type: 'log', - category: 'info', - text: 'Change the extension to .mjs to turn this file into a module', - }, - { - type: 'log', - category: 'info', - text: `Add "type": "module" to your `, - }, - ], - }), - SUPER_CALL_OUTSIDE_CONSTRUCTOR: { - message: 'super() is only valid inside a class constructor of a subclass', - advice: [ - { - type: 'log', - category: 'info', - text: "Maybe a typo in the method name ('constructor') or not extending another class?", - }, - ], - }, - JSX_DISABLED: { - message: "JSX syntax isn't enabled", - advice: [ - { - type: 'log', - category: 'info', - text: 'Are you using TypeScript? Change the file extension to .tsx', - }, - { - type: 'log', - category: 'info', - text: 'Are you using Flow? Add a @flow comment annotation to the top of the file', - }, - { - type: 'log', - category: 'info', - text: 'Not using either? Change the file extension to .jsx', - }, - // TODO you can also add `@jsx whatever` at the top of a file - ], - }, - JSX_IN_TS_EXTENSION: { - message: "JSX isn't allowed in regular TypeScript files", - advice: [ - { - type: 'log', - category: 'info', - text: 'Change the file extension to .tsx to enable JSX support', - }, - ], - }, - INVALID_PARENTEHSIZED_LVAL: (patternType: undefined | 'object' | 'array') => ({ - message: 'Invalid parenthesized binding', - advice: patternType === 'object' - ? [ - { - type: 'log', - category: 'info', - text: 'Did you use `({a}) = 0` instead of `({a} = 0)`?', - }, - ] - : patternType === 'array' - ? [ - { - type: 'log', - category: 'info', - text: 'Did you use `([a]) = 0` instead of `([a] = 0)`?', - }, - ] - : [], - }), - EXPECTED_COMMA_SEPARATOR: (context: string) => ({ - message: `Expected a comma to separate items in ${context}`, - }), - INVALID_LEFT_HAND_SIDE: (context: string) => ({ - message: `Invalid left-hand side in ${context}`, - }), - TS_EMPTY_LIST: (descriptor: string) => ({ - message: `${descriptor} list cannot be empty`, - }), - JSX_EXPECTED_CLOSING_TAG: (name: string, openingLoc: SourceLocation) => ({ - message: `Expected a corresponding JSX closing tag for ${name}`, - advice: buildJSXOpeningAdvice(name, openingLoc), - }), - JSX_EXPECTED_CLOSING_FRAGMENT_TAG: ( - name: string, - openingLoc: SourceLocation, - ) => ({ - message: 'Expected JSX closing fragment tag', - advice: buildJSXOpeningAdvice(name, openingLoc), - }), - JSX_UNKNOWN_CHILD_START: (name: string, openingLoc: SourceLocation) => ({ - message: 'Unknown JSX children start', - advice: buildJSXOpeningAdvice(name, openingLoc), - }), - JSX_UNCLOSED_ELEMENT: (name: string, openingLoc: SourceLocation) => ({ - message: 'Unclosed JSX element', - advice: buildJSXOpeningAdvice(name, openingLoc), - }), - TS_REQUIRED: (label: string) => ({ - message: `A ${label} is only valid inside of a TypeScript file`, - advice: [ - { - type: 'log', - category: 'info', - text: 'To enable TypeScript support, the file extension should end in .ts or .tsx', - }, - ], - }), - DUPLICATE_EXPORT: (name: string, existing: SourceLocation) => ({ - message: name === 'default' - ? 'Only one default export allowed per module.' - : `\`${name}\` has already been exported. Exported identifiers must be unique.`, - advice: buildDuplicateLocationAdvice([existing]), - }), - NEW_IN_OPTIONAL_CHAIN: (responsiblePointer?: DiagnosticLocation) => ({ - message: 'constructors in/after an Optional Chain are not allowed', - advice: responsiblePointer && [ - { - type: 'log', - category: 'info', - text: 'Optional chain member responsible', - }, - { - type: 'frame', - location: responsiblePointer, - }, - ], - }), - UNKNOWN_EXPRESSION_ATOM_START: (context: string) => ({ - message: `Unknown start to an ${context}`, - }), - INVALID_META_PROPERTY: (metaName: string, propertyName: string) => ({ - message: `The only valid meta property for ${metaName} is ${metaName}.${propertyName}`, - }), - ARGUMENT_CLASH_IN_STRICT: (name: string, loc: undefined | SourceLocation) => ({ - message: markup`Argument ${name} name clash in strict mode`, - advice: buildDuplicateLocationAdvice([loc]), - }), - RESERVED_WORD: (word: string) => ({ - message: `${word} is a reserved word`, - }), - UNEXPECTED_KEYWORD: (keyword: string) => ({ - message: `Unexpected keyword ${keyword}`, - }), - UNEXPECTED_TOKEN: ( - expected: undefined | string, - possibleShiftMistake: boolean, - ) => ({ - message: expected === undefined - ? 'Unexpected token' - : `Unexpected token, expected ${expected}`, - advice: possibleShiftMistake - ? [ - { - type: 'log', - category: 'info', - text: `Did you accidently hold shift?`, - }, - ] - : [], - }), - EXPECTED_CLOSING: (name: string, char: string, location: DiagnosticLocation) => ({ - message: `Unclosed ${name}`, - advice: [ - { - type: 'log', - category: 'info', - text: `We expected to find the closing character ${char} here`, - }, - { - type: 'frame', - location, - }, - ], - }), - EXPECTED_KEYWORD: (keyword: string) => ({ - message: markup`Expected keyword ${keyword}`, - }), - ESCAPE_SEQUENCE_IN_WORD: (word: string) => ({ - message: markup`${word} can't contain a unicode escape`, - }), - EXPECTED_ENABLE_SYNTAX: (syntaxName: string) => ({ - message: markup`Expected ${syntaxName} syntax to be enabled`, - }), - UNEXPECTED_HASH: (exclamationFollowed: boolean) => ({ - message: 'Unexpected character #', - advice: exclamationFollowed - ? [ - { - type: 'log', - category: 'info', - text: 'Did you want to write a hashbang? A hashbang can only be the first thing in a file.', - }, - ] - : [], - }), - UNEXPECTED_UNICODE_CHARACTER: ( - char: string, - unicodeName: string, - equivalentChar: string, - equivalentName: string, - ) => ({ - message: markup`Unexpected Unicode character '${char}' (${unicodeName})`, - advice: [ - { - type: 'log', - category: 'info', - text: markup`Did you mean '${equivalentChar}' (${equivalentName})? Both characters look the same, but are not.`, - }, - ], - }), - EXPECTED_NUMBER_IN_RADIX: (radix: number) => ({ - message: `Expected number in radix ${String(radix)}`, - }), - INVALID_IDENTIFIER_NAME: (name: string) => ({ - message: `Invalid identifier ${name}`, - }), - ESCAPE_SEQUENCE_IN_KEYWORD: (keyword: string) => ({ - message: `Escape sequence in keyword ${keyword}`, - }), - }, - // @romejs/js-analysis - TYPE_CHECK: { - NOT_CALLABLE: { - category: 'typeCheck/uncallable', - message: `This type isn't callable`, - }, - INCOMPATIBILITY: (upper: string, originLoc: undefined | SourceLocation) => ({ - category: 'typeCheck/incompatible', - message: 'Type incompatibility found', - advice: [ - { - type: 'log', - category: 'error', - text: `This type is incompatible with expected type of`, - }, - originLoc === undefined - ? { - type: 'log', - category: 'info', - text: upper, - } - : { - type: 'frame', - location: { - ...originLoc, - marker: upper, - }, - }, - ], - }), - UNKNOWN_IMPORT: ( - importedName: string, - source: string, - possibleNames: Array, - ) => ({ - category: 'typeCheck/unknownImport', - message: `Unknown import '${importedName}' in '${source}'`, - advice: buildSuggestionAdvice(importedName, possibleNames), - }), - UNKNOWN_PROP: (key: string, possibleNames: Array) => ({ - message: markup`Property ${key} not found in`, - category: 'typeCheck/unknownProperty', - advice: buildSuggestionAdvice(key, possibleNames), - }), - UNDECLARED_VARIABLE: (name: string, possibleNames: Array) => ({ - category: 'typeCheck/undeclaredVariable', - message: markup`Undeclared variable ${name}`, - advice: buildSuggestionAdvice(name, possibleNames), - }), - NOT_EXHAUSTIVE: (only: string, target: string) => ({ - category: 'typeCheck/notExhaustive', - //message += `but allows ${this.extraenous.map(type => this.utils.humanize(type)).join(' | ')}`; - message: `Expected only a ${only} but got ${target}`, - }), - MISSING_CONDITION: (missing: Array) => ({ - category: 'typeCheck/missingCondition', - message: `Missing the conditions ${missing.join(', ')}`, - }), - }, - // @romejs/consume - CONSUME: { - SET_PROPERTY_NON_OBJECT: 'Attempted to set a property on a non-object', - EXPECTED_JSON_VALUE: 'Expected a JSON value', - EXPECTED_OBJECT: 'Expected object', - EXPECTED_ARRAY: 'Expected array', - EXPECTED_DATE: 'Expected a date', - EXPECTED_BOOLEAN: 'Expected a boolean', - EXPECTED_STRING: 'Expected a string', - EXPECTED_BIGINT: 'Expected a bigint', - EXPECTED_NUMBER: 'Expected a number', - EXPECTED_URL: 'Expected a URL', - EXPECTED_VALID_NUMBER: 'Expected valid number', - EXPECTED_ABSOLUTE_PATH: 'Expected an absolute file path', - EXPECTED_RELATIVE_PATH: 'Expected a relative file path', - EXPECTED_EXPLICIT_RELATIVE_PATH: 'Expected an explicit relative file path. This is one that starts with ./ or ../', - INVALID: 'Invalid value', - EXPECTED_NUMBER_BETWEEN: (min: UnknownNumber, max: UnknownNumber) => ({ - message: `Expected number between ${min} and ${max}`, - }), - EXPECTED_NUMBER_HIGHER: (num: UnknownNumber) => ({ - message: `Expected number higher than ${num}`, - }), - EXPECTED_NUMBER_LOWER: (num: UnknownNumber) => ({ - message: `Expected number lower than ${num}`, - }), - INVALID_STRING_SET_VALUE: (value: string, validValues: Array) => ({ - message: markup`Invalid value ${value}`, - advice: [ - { - type: 'log', - category: 'info', - text: 'Possible values are', - }, - { - type: 'list', - list: validValues.map((str) => escapeMarkup(str)), - }, - ], - }), - UNUSED_PROPERTY: (key: string, type: string, knownProperties: Array) => ({ - message: markup`Unknown ${key} ${type}`, - advice: buildSuggestionAdvice( - key, - knownProperties, - { - ignoreCase: true, - }, - ), - }), - }, - // @romejs/codec-js-manifest - MANIFEST: { - TOO_MANY_HASH_PARTS: 'Too many hashes', - MISSING_HOSTED_GIT_USER: 'Missing user', - MISSING_HOSTED_GIT_REPO: 'Missing repo', - TOO_MANY_HOSTED_GIT_PARTS: 'Expected only 2 parts', - EMPTY_NPM_PATTERN: 'Missing rest of npm dependency pattern', - TOO_MANY_NPM_PARTS: 'Too many @ signs', - STRING_BIN_WITHOUT_NAME: 'A string bin is only allowed if the manifest has a name property', - MISSING_REPO_URL: 'Missing repo URL', - MIXED_EXPORTS_PATHS: 'Cannot mix a root conditional export with relative paths', - NAME_EXCEEDS: `cannot exceed 214 characters`, - INVALID_NAME_START: `cannot start with a dot or underscore`, - ORG_WITH_NO_PACKAGE_NAME: `contains an org but no package name`, - ORG_TOO_MANY_PARTS: `contains too many name separators`, - REDUNDANT_ORG_NAME_START: 'Redundant @ in org name', - INVALID_NAME_CHAR: (char: string) => ({ - message: markup`The character ${char} isn't allowed`, - }), - INCORRECT_CASING: (typoKey: string, correctKey: string) => ({ - message: `${typoKey} has incorrect casing, should be ${correctKey}`, - }), - INCORRECT_CAMEL_CASING: (typoKey: string, correctKey: string) => ({ - message: `${typoKey} isn't correctly camel cased when it should be ${correctKey}`, - }), - TYPO: (typoKey: string, correctKey: string) => ({ - message: `${typoKey} is a typo of ${correctKey}`, - }), - }, - // @romejs/project - PROJECT_CONFIG: { - BOOLEAN_CATEGORY: (enabled: boolean) => ({ - message: `Expected an object here but got a boolean`, - advice: [ - { - type: 'log', - category: 'info', - text: `You likely wanted \`{"enabled": ${String(enabled)}}\` instead`, - }, - ], - }), - RECURSIVE_CONFIG: 'Recursive config', - }, + return name; + }, + }, + ), + }), + UNKNOWN_EXPORT_POSSIBLE_UNEXPORTED_LOCAL: ( + name: string, + source: string, + location: SourceLocation, + ) => ({ + message: markup`Couldn't find export ${name} in `, + category: 'resolver/unknownExport', + advice: [ + { + type: 'log', + category: 'info', + text: markup`However we found a matching local variable in . Did you forget to export it?`, + }, + { + type: 'frame', + location, + }, + ], + }), + }, + SPDX: { + UNKNOWN_LICENSE: (id: string, knownLicenses: Array) => ({ + message: markup`Unknown SPDX license ${id}`, + advice: buildSuggestionAdvice(id, knownLicenses), + }), + VALID_LICENSE_WITH_MISSING_DASH: (possibleCorrectLicense: string) => ({ + message: `Missing dash between SPDX license name and version`, + advice: [ + { + type: 'log', + category: 'info', + text: `Did you mean ${possibleCorrectLicense}?`, + }, + ], + }), + WITH_RIGHT_LICENSE_ONLY: 'Only a license id can be on the right side of a WITH', + OPERATOR_NOT_BETWEEN_EXPRESSION: 'Can only use AND/OR in between an expression', + PLUS_NOT_AFTER_LICENSE: 'A plus can only come after a license id', + UNOPENED_PAREN: 'Nothing open to close', + }, + // @romejs/js-parser + JS_PARSER: { + UNTERMINATED_BLOCK_COMMENT: 'Unterminated comment', + UNTERMINATED_JSX_STRING: 'Unterminated string constant', + INVALID_UNICODE_ESCAPE: 'Invalid Unicode escape', + EXPECTED_UNICODE_ESCAPE: 'Expecting Unicode escape sequence \\uXXXX', + BAD_HEX_ESCAPE: 'Bad character escape sequence', + OCTAL_IN_STRICT_MODE: 'Octal literal in strict mode', + UNTERMINATED_TEMPLATE: 'Unterminated template', + UNTERMINATED_STRING: 'Unterminated string constant', + OUT_OF_BOUND_CODE_POINT: 'Code point out of bounds', + IDENTIFIER_AFTER_NUMBER: 'Identifier directly after number', + OCTAL_BIGINT: "A bigint can't be an octal", + DECIMAL_BIGINT: "A bigint can't have a decimal", + INVALID_NUMBER: 'Invalid number', + LEGACY_OCTAL_IN_STRICT_MODE: 'Legacy octal literals are not allowed in strict mode', + INVALID_INT_TOKEN: 'Invalid or unexpected int token', + UNICODE_ESCAPE_IN_REGEX_FLAGS: "Regular expression flags can't contain unicode escapes", + UNTERMINATED_REGEX: 'Unterminated regular expression', + DANGLING_BACKSLASH_IN_REGEX: 'Dangling backslash in a regular expression', + EXPECTED_RELATIONAL_OPERATOR: 'Expected relational operator', + UNEXPECTED_SPACE: 'Unexpected space', + EXPECTED_SEMI_OR_LINE_TERMINATOR: 'Expected a semicolon or a line terminator', + GET_SET_CLASS_CONSTRUCTOR: "Constructor can't have get/set modifier", + ASYNC_CLASS_CONSTRUCTOR: 'Constructor cannot be async', + GENERATOR_CLASS_CONSTRUCTOR: 'Constructor cannot be a generator', + DUPLICATE_CLASS_CONSTRUCTOR: 'Duplicate constructor in the same class', + UNKNOWN_CLASS_PROPERTY_START: 'Unknown class property start', + CLASS_STATIC_PROTOTYPE_PROPERTY: 'Classes may not have static property named prototype', + CLASS_PRIVATE_FIELD_NAMED_CONSTRUCTOR: "Classes may not have a private field named '#constructor'", + CLASS_PROPERTY_NAME_CONSTRUCTOR: "Classes may not have a non-static field named 'constructor'", + PROTO_PROP_REDEFINITION: 'Redefinition of __proto__ property', + MISSING_CONDITIONAL_SEPARATOR: 'Missing conditional expression consequent separator', + WRAP_EXPONENTIATION: 'Illegal expression. Wrap left hand side or entire exponentiation in parentheses.', + DELETE_LOCAL_VARIABLE_IN_STRICT: 'Deleting local variable in strict mode', + DELETE_PRIVATE_FIELD: 'Deleting a private field is not allowed', + TAGGED_TEMPLATE_IN_OPTIONAL_CHAIN: 'Tagged Template Literals are not allowed in optionalChain', + YIELD_NAME_IN_GENERATOR: "Can not use 'yield' as identifier inside a generator", + AWAIT_NAME_IN_ASYNC: "Can not use 'await' as identifier inside an async function", + EMPTY_PARENTHESIZED_EXPRESSION: 'Parenthesized expression didnt contain anything', + AWAIT_IN_ASYNC_PARAMS: 'await is not allowed in async function parameters', + YIELD_IN_GENERATOR_PARAMS: 'yield is not allowed in generator parameters', + FLOW_TYPE_CAST_IN_TS: "Flow type cast expressions aren't allowed in TypeScript", + PARENTHESIZED_FUNCTION_PARAMS: "Function parameters can't be parenthesized", + NEW_WITH_TYPESCRIPT_TYPE_ARGUMENTS_NO_PARENS: 'In TypeScript, a new expression with type arguments must have parens', + INVALID_TEMPLATE_ESCAPE: 'Invalid escape sequence in template', + EXPECTED_IDENTIFIER: 'Expected an identifier', + IMPORT_EXACT_ARGUMENTS: 'import() requires exactly one argument', + IMPORT_TRAILING_COMMA: 'Trailing comma is disallowed inside import(...) arguments', + IMPORT_SPREAD: 'Spread is not allowed in import()', + IMPORT_NEW_CALLEE: 'Cannot use new with import(...)', + SUPER_OUTSIDE_METHOD: 'super is only allowed in object methods and classes', + INVALID_SUPER_SUFFIX: 'Invalid super suffix operator', + AWAIT_OUTSIDE_ASYNC: "Can't use await outside of an async function", + AWAIT_STAR: 'await* has been removed from the async functions proposal. Use Promise.all() instead.', + NEW_TARGET_OUTSIDE_CLASS: 'new.target can only be used in functions or class properties', + MULTIPLE_DESTRUCTURING_RESTS: 'Cannot have multiple rest elements when destructuring', + TRAILING_COMMA_AFTER_REST: 'A trailing comma is not permitted after the rest element', + GETTER_WITH_PARAMS: 'getter should have no parameters', + SETTER_WITH_REST: 'setter function argument must not be a rest parameter', + SETTER_NOT_ONE_PARAM: 'setter should have exactly one param', + ASYNC_GETTER_SETTER: "An object setter/getter can't be async", + GENERATOR_GETTER_SETTER: "An object setter/getter can't be a generator", + ARGUMENTS_IN_CLASS_FIELD: "'arguments' is not allowed in class field initializer", + NON_SIMPLE_PARAM_IN_EXPLICIT_STRICT_FUNCTION: 'Non-simple parameter in strict mode', + STRICT_DIRECTIVE_IN_NON_SIMPLE_PARAMS: "Illegal 'use strict' directive in function with non-simple parameter list", + OBJECT_PROPERTY_WITH_TYPE_PARAMETERS: 'Object property cannot have type parameters', + ILLEGAL_VARIANCE: 'Variance is not allowed here', + OBJECT_METHOD_IN_PATTERN: "Object methods aren't allowed in object patterns", + IMPORT_META_OUTSIDE_MODULE: `import.meta may only appear in a module`, + EXPECTED_ARROW_AFTER_ASYNC_TYPE_PARAMS: 'Expected arrow because we are a possible async arrow and type annotated parameters were present', + INVALID_OBJECT_PATTERN_PROP: 'Invalid property node for object pattern', + ASYNC_OBJECT_METHOD_LINE_BREAK: "There shouldn't be any newlines between async and the rest of the function", + SPACE_BETWEEN_PRIVATE_HASH: 'Unexpected space between # and identifier', + CONFUSING_CALL_ARGUMENT: 'Function parameter type annotation? Possibly forgot curlies around an object. Possibly forgot async keyword.', + EXPECTED_ARROW_AFTER_TYPE_PARAMS: 'Expected an arrow function after this type parameter declaration', + REQUIRED_CLASS_NAME: 'Class name is required', + JSX_ELEM_TYPE_ARGUMENTS_OUTSIDE_TS: 'JSX element type arguments are only allowed in TS', + UNWRAPPED_ADJACENT_JHX: `Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...?`, + CONFUSED_OR: 'Unexpected ||, did you mean just |?', + INVALID_ASSIGNMENT_TARGET: 'Not a valid assignment target', + IMPORT_KIND_SPECIFIER_ON_IMPORT_DECLARATION_WITH_KIND: 'The `type` and `typeof` keywords on named imports can only be used on regular `import` statements. It cannot be used with `import type` or `import typeof` statements', + DESTRUCTURING_IN_IMPORT: 'ES2015 named imports do not destructure. Use another statement for destructuring after the import.', + IMPORT_TYPE_STAR: 'import * is not allowed', + IMPORT_MISSING_SOURCE: 'import missing a source', + EXPORT_TYPE_NAMESPACE: "Can't have a type export namespacer specifier", + EXPORT_MISSING_FROM: 'Expected `from` for an export node', + EXPORT_FROM_NOT_STRING: 'Export from only allows strings', + BINDING_MEMBER_EXPRESSION: 'Binding member expression', + INVALID_OBJECT_PATTERN_PROPERTY: 'Not a valid assignment object pattern property', + OBJECT_PATTERN_CANNOT_CONTAIN_METHODS: 'Object pattern cannot contains methods', + INVALID_ASSIGNMENT_PATTERN_OPERATOR: "Only '=' operator can be used for specifying default value.", + INVALID_OBJECT_REST_ARGUMENT: "Invalid rest operator's argument", + INVALID_EXPORT_DEFAULT: 'Only expressions, functions or classes are allowed as the `default` export.', + INVALID_EXPORT_DECLARATION: 'Invalid export declaration', + DESTRUCTURING_REST_ELEMENT_NOT_LAST: `The rest element has to be the last element when destructuring`, + REST_INVALID_ARGUMENT: "Invalid rest operator's argument", + EXPORT_ASYNC_NO_FUNCTION_KEYWORD: 'Started with `export async` so we expected to receive an async function but no function keyword was found', + TYPE_CAST_WITHOUT_ANNOTATION: 'Type cast expression has no type annotation. Did you mean for this to be a function parameter?', + TYPE_CAST_CANNOT_BE_OPTIONAL: 'Type cast expressions cannot be optional. Did you mean for this to be a function parameter?', + TYPE_CAST_EXPECTED_PARENS: 'The type cast expression is expected to be wrapped with parentheses', + INVALID_ASYNC_ARROW_WITH_TYPE_PARAMS: 'Invalid async arrow with type parameters', + TYPE_NUMERIC_LITERAL_PLUS: 'Numeric literal type annotations cannot stand with a +, omit it instead', + TYPE_NUMERIC_LITERAL_EXPECTED: `Unexpected token, expected "number"`, + JSX_INVALID_ATTRIBUTE_VALUE: 'JSX attribute value should be either an expression or a quoted JSX text', + JSX_UNCLOSED_SELF_CLOSING_TAG: 'Unclosed JSX element open', + JSX_UNCLOSED_CLOSING_TAG: 'Unclosed JSX element close', + JSX_EMPTY_ATTRIBUTE_VALUE: 'JSX attribute cannot be an empty expression', + JSX_UNKNOWN_IDENTIFIER_TOKEN: 'Unknown JSX identifier token', + TS_IMPORT_ARG_NOT_STRING: 'Argument in a type import must be a string literal', + TS_CONSTANT_NOT_LITERAL: 'Only literal values are allowed as a constant type', + TS_INVALID_SIGNATURE_BINDING_NODE: 'Invalid node in signature binding list', + TS_REQUIRED_FOLLOWS_OPTIONAL: 'A required element cannot follow an optional element.', + TS_TEMPLATE_LITERAL_WITH_SUBSTITUION: 'Template literal types cannot have any substitution', + TS_UNKNOWN_NON_ARRAY_START: 'Unknown TS non array type start', + TS_INVALID_READONLY_MODIFIER: "'readonly' type modifier is only permitted on array and tuple literal types.", + TS_EXTERNAL_MODULE_REFERENCE_ARG_NOT_STRING: 'TypeScript require() must have a single string argument', + TS_UNKNOWN_DECLARE_START: 'Unknown TypeScript declare start', + TS_UNEXPECTED_CAST_IN_PARAMETER_POSITION: 'Unexpected type cast in parameter position', + TS_DISABLED_BUT_ACCESSIBILITY_OR_READONLY: 'Accessibility and readonly syntax found but TS is not enabled', + TS_PARAMETER_PROPERTY_BINDING_PATTERN: 'A parameter property may not be declared using a binding pattern.', + TYPE_ANNOTATION_AFTER_ASSIGNMENT: 'Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`', + TYPE_BINDING_PARAMETER_OPTIONAL: 'A binding pattern parameter cannot be optional in an implementation signature.', + ILLEGAL_FUNCTION_IN_STRICT: 'In strict mode code, functions can only be declared at top level or inside a block', + ILLEGAL_FUNCTION_IN_NON_STRICT: 'In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement', + ILLEGAL_GENERATOR_DEFINITION: 'Generators can only be declared at the top level or inside a block', + ILLEGAL_ASYNC_DEFINITION: 'Async functions can only be declared at the top level or inside a block', + LEXICAL_DECLARATION_IN_SINGLE_STATEMENT_CONTEXT: 'Lexical declaration cannot appear in a single-statement context', + IMPORT_EXPORT_MUST_TOP_LEVEL: "'import' and 'export' may only appear at the top level", + REGULAR_FOR_AWAIT: "Can't have an await on a regular for loop", + RETURN_OUTSIDE_FUNCTION: "'return' outside of function", + MULTIPLE_DEFAULT_CASE: 'Multiple default clauses', + SWITCH_STATEMENT_OUTSIDE_CASE: 'Statement outside of a case or default block', + NEWLINE_AFTER_THROW: 'Illegal newline after throw', + TRY_MISSING_FINALLY_OR_CATCH: 'Missing catch or finally clause', + INVALID_LABEL_DECLARATION: 'Invalid labeled declaration', + WITH_IN_STRICT: "'with' in strict mode", + OCTAL_IN_STRICT: 'Octal literal in strict mode', + FOR_IN_OF_WITH_INITIALIZER: 'Loop variable declaration may not have an initializer', + CONST_WITHOUT_INITIALIZER: 'A constant must have an initializer', + COMPLEX_BINDING_WITHOUT_INITIALIZER: 'Complex binding patterns require an initialization value', + ACCESSOR_WITH_TYPE_PARAMS: 'An accessor cannot have type parameters', + UNEXPECTED_SPREAD: 'Unexpected spread', + DUPLICATE_LABEL: (label: string, loc: undefined | SourceLocation) => ({ + message: markup`Label ${label} is already declared`, + advice: buildDuplicateLocationAdvice([loc]), + }), + UNKNOWN_LABEL: (label: undefined | string) => ({ + message: label === undefined + ? 'No loop label found' + : markup`Unknown label ${label}`, + }), + IMPORT_EXPORT_IN_SCRIPT: (manifestPath: string) => ({ + message: `import and export can only appear in a module`, + advice: [ + // TODO this advice is pointless if you have syntax extensions enabled + { + type: 'log', + category: 'info', + text: 'Change the extension to .mjs to turn this file into a module', + }, + { + type: 'log', + category: 'info', + text: `Add "type": "module" to your `, + }, + ], + }), + SUPER_CALL_OUTSIDE_CONSTRUCTOR: { + message: 'super() is only valid inside a class constructor of a subclass', + advice: [ + { + type: 'log', + category: 'info', + text: "Maybe a typo in the method name ('constructor') or not extending another class?", + }, + ], + }, + JSX_DISABLED: { + message: "JSX syntax isn't enabled", + advice: [ + { + type: 'log', + category: 'info', + text: 'Are you using TypeScript? Change the file extension to .tsx', + }, + { + type: 'log', + category: 'info', + text: 'Are you using Flow? Add a @flow comment annotation to the top of the file', + }, + { + type: 'log', + category: 'info', + text: 'Not using either? Change the file extension to .jsx', + }, + // TODO you can also add `@jsx whatever` at the top of a file + ], + }, + JSX_IN_TS_EXTENSION: { + message: "JSX isn't allowed in regular TypeScript files", + advice: [ + { + type: 'log', + category: 'info', + text: 'Change the file extension to .tsx to enable JSX support', + }, + ], + }, + INVALID_PARENTEHSIZED_LVAL: (patternType: undefined | 'object' | 'array') => ({ + message: 'Invalid parenthesized binding', + advice: patternType === 'object' + ? [ + { + type: 'log', + category: 'info', + text: 'Did you use `({a}) = 0` instead of `({a} = 0)`?', + }, + ] + : patternType === 'array' + ? [ + { + type: 'log', + category: 'info', + text: 'Did you use `([a]) = 0` instead of `([a] = 0)`?', + }, + ] + : [], + }), + EXPECTED_COMMA_SEPARATOR: (context: string) => ({ + message: `Expected a comma to separate items in ${context}`, + }), + INVALID_LEFT_HAND_SIDE: (context: string) => ({ + message: `Invalid left-hand side in ${context}`, + }), + TS_EMPTY_LIST: (descriptor: string) => ({ + message: `${descriptor} list cannot be empty`, + }), + JSX_EXPECTED_CLOSING_TAG: (name: string, openingLoc: SourceLocation) => ({ + message: `Expected a corresponding JSX closing tag for ${name}`, + advice: buildJSXOpeningAdvice(name, openingLoc), + }), + JSX_EXPECTED_CLOSING_FRAGMENT_TAG: (name: string, openingLoc: SourceLocation) => ({ + message: 'Expected JSX closing fragment tag', + advice: buildJSXOpeningAdvice(name, openingLoc), + }), + JSX_UNKNOWN_CHILD_START: (name: string, openingLoc: SourceLocation) => ({ + message: 'Unknown JSX children start', + advice: buildJSXOpeningAdvice(name, openingLoc), + }), + JSX_UNCLOSED_ELEMENT: (name: string, openingLoc: SourceLocation) => ({ + message: 'Unclosed JSX element', + advice: buildJSXOpeningAdvice(name, openingLoc), + }), + TS_REQUIRED: (label: string) => ({ + message: `A ${label} is only valid inside of a TypeScript file`, + advice: [ + { + type: 'log', + category: 'info', + text: 'To enable TypeScript support, the file extension should end in .ts or .tsx', + }, + ], + }), + DUPLICATE_EXPORT: (name: string, existing: SourceLocation) => ({ + message: name === 'default' + ? 'Only one default export allowed per module.' + : `\`${name}\` has already been exported. Exported identifiers must be unique.`, + advice: buildDuplicateLocationAdvice([existing]), + }), + NEW_IN_OPTIONAL_CHAIN: (responsiblePointer?: DiagnosticLocation) => ({ + message: 'constructors in/after an Optional Chain are not allowed', + advice: responsiblePointer && [ + { + type: 'log', + category: 'info', + text: 'Optional chain member responsible', + }, + { + type: 'frame', + location: responsiblePointer, + }, + ], + }), + UNKNOWN_EXPRESSION_ATOM_START: (context: string) => ({ + message: `Unknown start to an ${context}`, + }), + INVALID_META_PROPERTY: (metaName: string, propertyName: string) => ({ + message: `The only valid meta property for ${metaName} is ${metaName}.${propertyName}`, + }), + ARGUMENT_CLASH_IN_STRICT: (name: string, loc: undefined | SourceLocation) => ({ + message: markup`Argument ${name} name clash in strict mode`, + advice: buildDuplicateLocationAdvice([loc]), + }), + RESERVED_WORD: (word: string) => ({ + message: `${word} is a reserved word`, + }), + UNEXPECTED_KEYWORD: (keyword: string) => ({ + message: `Unexpected keyword ${keyword}`, + }), + UNEXPECTED_TOKEN: ( + expected: undefined | string, + possibleShiftMistake: boolean, + ) => ({ + message: expected === undefined + ? 'Unexpected token' + : `Unexpected token, expected ${expected}`, + advice: possibleShiftMistake + ? [ + { + type: 'log', + category: 'info', + text: `Did you accidently hold shift?`, + }, + ] + : [], + }), + EXPECTED_CLOSING: (name: string, char: string, location: DiagnosticLocation) => ({ + message: `Unclosed ${name}`, + advice: [ + { + type: 'log', + category: 'info', + text: `We expected to find the closing character ${char} here`, + }, + { + type: 'frame', + location, + }, + ], + }), + EXPECTED_KEYWORD: (keyword: string) => ({ + message: markup`Expected keyword ${keyword}`, + }), + ESCAPE_SEQUENCE_IN_WORD: (word: string) => ({ + message: markup`${word} can't contain a unicode escape`, + }), + EXPECTED_ENABLE_SYNTAX: (syntaxName: string) => ({ + message: markup`Expected ${syntaxName} syntax to be enabled`, + }), + UNEXPECTED_HASH: (exclamationFollowed: boolean) => ({ + message: 'Unexpected character #', + advice: exclamationFollowed + ? [ + { + type: 'log', + category: 'info', + text: 'Did you want to write a hashbang? A hashbang can only be the first thing in a file.', + }, + ] + : [], + }), + UNEXPECTED_UNICODE_CHARACTER: ( + char: string, + unicodeName: string, + equivalentChar: string, + equivalentName: string, + ) => ({ + message: markup`Unexpected Unicode character '${char}' (${unicodeName})`, + advice: [ + { + type: 'log', + category: 'info', + text: markup`Did you mean '${equivalentChar}' (${equivalentName})? Both characters look the same, but are not.`, + }, + ], + }), + EXPECTED_NUMBER_IN_RADIX: (radix: number) => ({ + message: `Expected number in radix ${String(radix)}`, + }), + INVALID_IDENTIFIER_NAME: (name: string) => ({ + message: `Invalid identifier ${name}`, + }), + ESCAPE_SEQUENCE_IN_KEYWORD: (keyword: string) => ({ + message: `Escape sequence in keyword ${keyword}`, + }), + }, + // @romejs/js-analysis + TYPE_CHECK: { + NOT_CALLABLE: { + category: 'typeCheck/uncallable', + message: `This type isn't callable`, + }, + INCOMPATIBILITY: (upper: string, originLoc: undefined | SourceLocation) => ({ + category: 'typeCheck/incompatible', + message: 'Type incompatibility found', + advice: [ + { + type: 'log', + category: 'error', + text: `This type is incompatible with expected type of`, + }, + originLoc === undefined + ? { + type: 'log', + category: 'info', + text: upper, + } + : { + type: 'frame', + location: { + ...originLoc, + marker: upper, + }, + }, + ], + }), + UNKNOWN_IMPORT: ( + importedName: string, + source: string, + possibleNames: Array, + ) => ({ + category: 'typeCheck/unknownImport', + message: `Unknown import '${importedName}' in '${source}'`, + advice: buildSuggestionAdvice(importedName, possibleNames), + }), + UNKNOWN_PROP: (key: string, possibleNames: Array) => ({ + message: markup`Property ${key} not found in`, + category: 'typeCheck/unknownProperty', + advice: buildSuggestionAdvice(key, possibleNames), + }), + UNDECLARED_VARIABLE: (name: string, possibleNames: Array) => ({ + category: 'typeCheck/undeclaredVariable', + message: markup`Undeclared variable ${name}`, + advice: buildSuggestionAdvice(name, possibleNames), + }), + NOT_EXHAUSTIVE: (only: string, target: string) => ({ + category: 'typeCheck/notExhaustive', + //message += `but allows ${this.extraenous.map(type => this.utils.humanize(type)).join(' | ')}`; + message: `Expected only a ${only} but got ${target}`, + }), + MISSING_CONDITION: (missing: Array) => ({ + category: 'typeCheck/missingCondition', + message: `Missing the conditions ${missing.join(', ')}`, + }), + }, + // @romejs/consume + CONSUME: { + SET_PROPERTY_NON_OBJECT: 'Attempted to set a property on a non-object', + EXPECTED_JSON_VALUE: 'Expected a JSON value', + EXPECTED_OBJECT: 'Expected object', + EXPECTED_ARRAY: 'Expected array', + EXPECTED_DATE: 'Expected a date', + EXPECTED_BOOLEAN: 'Expected a boolean', + EXPECTED_STRING: 'Expected a string', + EXPECTED_BIGINT: 'Expected a bigint', + EXPECTED_NUMBER: 'Expected a number', + EXPECTED_URL: 'Expected a URL', + EXPECTED_VALID_NUMBER: 'Expected valid number', + EXPECTED_ABSOLUTE_PATH: 'Expected an absolute file path', + EXPECTED_RELATIVE_PATH: 'Expected a relative file path', + EXPECTED_EXPLICIT_RELATIVE_PATH: 'Expected an explicit relative file path. This is one that starts with ./ or ../', + INVALID: 'Invalid value', + EXPECTED_NUMBER_BETWEEN: (min: UnknownNumber, max: UnknownNumber) => ({ + message: `Expected number between ${min} and ${max}`, + }), + EXPECTED_NUMBER_HIGHER: (num: UnknownNumber) => ({ + message: `Expected number higher than ${num}`, + }), + EXPECTED_NUMBER_LOWER: (num: UnknownNumber) => ({ + message: `Expected number lower than ${num}`, + }), + INVALID_STRING_SET_VALUE: (value: string, validValues: Array) => ({ + message: markup`Invalid value ${value}`, + advice: [ + { + type: 'log', + category: 'info', + text: 'Possible values are', + }, + { + type: 'list', + list: validValues.map((str) => escapeMarkup(str)), + }, + ], + }), + UNUSED_PROPERTY: (key: string, type: string, knownProperties: Array) => ({ + message: markup`Unknown ${key} ${type}`, + advice: buildSuggestionAdvice( + key, + knownProperties, + { + ignoreCase: true, + }, + ), + }), + }, + // @romejs/codec-js-manifest + MANIFEST: { + TOO_MANY_HASH_PARTS: 'Too many hashes', + MISSING_HOSTED_GIT_USER: 'Missing user', + MISSING_HOSTED_GIT_REPO: 'Missing repo', + TOO_MANY_HOSTED_GIT_PARTS: 'Expected only 2 parts', + EMPTY_NPM_PATTERN: 'Missing rest of npm dependency pattern', + TOO_MANY_NPM_PARTS: 'Too many @ signs', + STRING_BIN_WITHOUT_NAME: 'A string bin is only allowed if the manifest has a name property', + MISSING_REPO_URL: 'Missing repo URL', + MIXED_EXPORTS_PATHS: 'Cannot mix a root conditional export with relative paths', + NAME_EXCEEDS: `cannot exceed 214 characters`, + INVALID_NAME_START: `cannot start with a dot or underscore`, + ORG_WITH_NO_PACKAGE_NAME: `contains an org but no package name`, + ORG_TOO_MANY_PARTS: `contains too many name separators`, + REDUNDANT_ORG_NAME_START: 'Redundant @ in org name', + INVALID_NAME_CHAR: (char: string) => ({ + message: markup`The character ${char} isn't allowed`, + }), + INCORRECT_CASING: (typoKey: string, correctKey: string) => ({ + message: `${typoKey} has incorrect casing, should be ${correctKey}`, + }), + INCORRECT_CAMEL_CASING: (typoKey: string, correctKey: string) => ({ + message: `${typoKey} isn't correctly camel cased when it should be ${correctKey}`, + }), + TYPO: (typoKey: string, correctKey: string) => ({ + message: `${typoKey} is a typo of ${correctKey}`, + }), + }, + // @romejs/project + PROJECT_CONFIG: { + BOOLEAN_CATEGORY: (enabled: boolean) => ({ + message: `Expected an object here but got a boolean`, + advice: [ + { + type: 'log', + category: 'info', + text: `You likely wanted \`{"enabled": ${String(enabled)}}\` instead`, + }, + ], + }), + RECURSIVE_CONFIG: 'Recursive config', + }, }); diff --git a/packages/@romejs/diagnostics/errors.ts b/packages/@romejs/diagnostics/errors.ts index dcc76c04428..521deeae28d 100644 --- a/packages/@romejs/diagnostics/errors.ts +++ b/packages/@romejs/diagnostics/errors.ts @@ -10,46 +10,46 @@ import {printDiagnosticsToString} from '@romejs/cli-diagnostics'; import {Diagnostic, DiagnosticSuppressions} from './types'; export class DiagnosticsError extends Error { - constructor( - message: string, - diagnostics: Diagnostics, - suppressions: DiagnosticSuppressions = [], - ) { - if (diagnostics.length === 0) { - throw new Error('No diagnostics'); - } - - message += '\n'; - message += printDiagnosticsToString({diagnostics, suppressions}); - - super(message); - this.diagnostics = diagnostics; - this.suppressions = suppressions; - this.name = 'DiagnosticsError'; - } - - diagnostics: Diagnostics; - suppressions: DiagnosticSuppressions; + constructor( + message: string, + diagnostics: Diagnostics, + suppressions: DiagnosticSuppressions = [], + ) { + if (diagnostics.length === 0) { + throw new Error('No diagnostics'); + } + + message += '\n'; + message += printDiagnosticsToString({diagnostics, suppressions}); + + super(message); + this.diagnostics = diagnostics; + this.suppressions = suppressions; + this.name = 'DiagnosticsError'; + } + + diagnostics: Diagnostics; + suppressions: DiagnosticSuppressions; } export function createSingleDiagnosticError( - diag: Diagnostic, - suppressions?: DiagnosticSuppressions, + diag: Diagnostic, + suppressions?: DiagnosticSuppressions, ): DiagnosticsError { - return new DiagnosticsError( - diag.description.message.value, - [diag], - suppressions, - ); + return new DiagnosticsError( + diag.description.message.value, + [diag], + suppressions, + ); } export function getDiagnosticsFromError(err: Error): undefined | Diagnostics { - if (err instanceof DiagnosticsError) { - const processor = new DiagnosticsProcessor({}); - processor.addSuppressions(err.suppressions); - processor.addDiagnostics(err.diagnostics); - return processor.getDiagnostics(); - } - - return undefined; + if (err instanceof DiagnosticsError) { + const processor = new DiagnosticsProcessor({}); + processor.addSuppressions(err.suppressions); + processor.addDiagnostics(err.diagnostics); + return processor.getDiagnostics(); + } + + return undefined; } diff --git a/packages/@romejs/diagnostics/helpers.ts b/packages/@romejs/diagnostics/helpers.ts index 482a5070702..bde199ee6d7 100644 --- a/packages/@romejs/diagnostics/helpers.ts +++ b/packages/@romejs/diagnostics/helpers.ts @@ -14,154 +14,154 @@ import {NEWLINE} from '@romejs/js-parser-utils'; import {escapeMarkup, markup} from '@romejs/string-markup'; type BuildSuggestionAdviceOptions = { - minRating?: number; - ignoreCase?: boolean; - formatItem?: (item: string) => string; + minRating?: number; + ignoreCase?: boolean; + formatItem?: (item: string) => string; }; export function buildSuggestionAdvice( - value: string, - items: Array, - {minRating = 0.5, ignoreCase, formatItem}: BuildSuggestionAdviceOptions = {}, + value: string, + items: Array, + {minRating = 0.5, ignoreCase, formatItem}: BuildSuggestionAdviceOptions = {}, ): DiagnosticAdvice { - const advice: DiagnosticAdvice = []; - - const ratings = orderBySimilarity( - value, - items, - { - minRating, - formatItem, - ignoreCase, - }, - ); - - const strings = ratings.map((item) => { - const {target} = item; - if (formatItem === undefined) { - return target; - } else { - return formatItem(target); - } - }); - - const topRatingFormatted = strings.shift(); - if (topRatingFormatted === undefined) { - return advice; - } - - // Raw rating that hasn't been formatted - const topRatingRaw = ratings[0].target; - - if (topRatingRaw === value) { - // TODO produce a better example - } - - // If there's only 2 suggestions then just say "Did you mean A or B?" rather than printing the list - if (strings.length === 1) { - advice.push({ - type: 'log', - category: 'info', - text: markup`Did you mean ${topRatingFormatted} or ${strings[0]}?`, - }); - } else { - advice.push({ - type: 'log', - category: 'info', - text: markup`Did you mean ${topRatingFormatted}?`, - }); - - advice.push({ - type: 'diff', - diff: stringDiff(value, topRatingRaw), - }); - - if (strings.length > 0) { - advice.push({ - type: 'log', - category: 'info', - text: 'Or one of these?', - }); - - advice.push({ - type: 'list', - list: strings.map((str) => escapeMarkup(str)), - truncate: true, - }); - } - } - - // TODO check if ANY of the suggestions match - if ( - topRatingRaw !== value && - topRatingRaw.toLowerCase() === value.toLowerCase() - ) { - advice.push({ - type: 'log', - category: 'warn', - text: 'This operation is case sensitive', - }); - } - - return advice; + const advice: DiagnosticAdvice = []; + + const ratings = orderBySimilarity( + value, + items, + { + minRating, + formatItem, + ignoreCase, + }, + ); + + const strings = ratings.map((item) => { + const {target} = item; + if (formatItem === undefined) { + return target; + } else { + return formatItem(target); + } + }); + + const topRatingFormatted = strings.shift(); + if (topRatingFormatted === undefined) { + return advice; + } + + // Raw rating that hasn't been formatted + const topRatingRaw = ratings[0].target; + + if (topRatingRaw === value) { + // TODO produce a better example + } + + // If there's only 2 suggestions then just say "Did you mean A or B?" rather than printing the list + if (strings.length === 1) { + advice.push({ + type: 'log', + category: 'info', + text: markup`Did you mean ${topRatingFormatted} or ${strings[0]}?`, + }); + } else { + advice.push({ + type: 'log', + category: 'info', + text: markup`Did you mean ${topRatingFormatted}?`, + }); + + advice.push({ + type: 'diff', + diff: stringDiff(value, topRatingRaw), + }); + + if (strings.length > 0) { + advice.push({ + type: 'log', + category: 'info', + text: 'Or one of these?', + }); + + advice.push({ + type: 'list', + list: strings.map((str) => escapeMarkup(str)), + truncate: true, + }); + } + } + + // TODO check if ANY of the suggestions match + if ( + topRatingRaw !== value && + topRatingRaw.toLowerCase() === value.toLowerCase() + ) { + advice.push({ + type: 'log', + category: 'warn', + text: 'This operation is case sensitive', + }); + } + + return advice; } // Sometimes we'll have big blobs of JS in a diagnostic when we'll only show a snippet. This method pads it out then truncates the rest for efficient transmission. We will have crappy ANSI formatting in the console and elsewhere but for places where we need to truncate we probably don't care (generated code). export function truncateSourceText( - code: string, - start: Position, - end: Position, + code: string, + start: Position, + end: Position, ): string { - const lines = code.split(NEWLINE); + const lines = code.split(NEWLINE); - // Pad the starting and ending lines by 10 - const fromLine = Math.max(ob1Get1(start.line) - 10, 0); - const toLine = Math.max(ob1Get1(end.line) + 10, lines.length); + // Pad the starting and ending lines by 10 + const fromLine = Math.max(ob1Get1(start.line) - 10, 0); + const toLine = Math.max(ob1Get1(end.line) + 10, lines.length); - const capturedLines = lines.slice(fromLine, toLine); - return '\n'.repeat(fromLine) + capturedLines.join('\n'); + const capturedLines = lines.slice(fromLine, toLine); + return '\n'.repeat(fromLine) + capturedLines.join('\n'); } export function buildDuplicateLocationAdvice( - locations: Array, + locations: Array, ): DiagnosticAdvice { - const locationAdvice: DiagnosticAdvice = locations.map((location) => { - if (location === undefined) { - return { - type: 'log', - category: 'warn', - text: 'Unable to find location', - }; - } else { - return { - type: 'frame', - location, - }; - } - }); - - return [ - { - type: 'log', - category: 'info', - text: 'Defined already here', - }, - ...locationAdvice, - ]; + const locationAdvice: DiagnosticAdvice = locations.map((location) => { + if (location === undefined) { + return { + type: 'log', + category: 'warn', + text: 'Unable to find location', + }; + } else { + return { + type: 'frame', + location, + }; + } + }); + + return [ + { + type: 'log', + category: 'info', + text: 'Defined already here', + }, + ...locationAdvice, + ]; } export function diagnosticLocationToMarkupFilelink( - loc: DiagnosticLocation, + loc: DiagnosticLocation, ): string { - const {start, filename} = loc; + const {start, filename} = loc; - if (filename === undefined) { - return 'unknown'; - } + if (filename === undefined) { + return 'unknown'; + } - if (start === undefined) { - return markup``; - } + if (start === undefined) { + return markup``; + } - return markup``; + return markup``; } diff --git a/packages/@romejs/diagnostics/types.ts b/packages/@romejs/diagnostics/types.ts index bf0b39f0402..6fc29fdf5b7 100644 --- a/packages/@romejs/diagnostics/types.ts +++ b/packages/@romejs/diagnostics/types.ts @@ -15,43 +15,43 @@ import {Dict} from '@romejs/typescript-helpers'; import {ClientRequestFlags} from '@romejs/core'; export type DiagnosticFilter = { - category?: DiagnosticCategory; - message?: string; - filename?: string; - start?: Position; - line?: Number1; + category?: DiagnosticCategory; + message?: string; + filename?: string; + start?: Position; + line?: Number1; }; export type DiagnosticFilters = Array; export type DiagnosticSuppression = { - filename: string; - category: string; - startLine: Number1; - endLine: Number1; - commentLocation: SourceLocation; + filename: string; + category: string; + startLine: Number1; + endLine: Number1; + commentLocation: SourceLocation; }; export type DiagnosticSuppressions = Array; export type DiagnosticFilterWithTest = DiagnosticFilter & { - test?: (diagnostic: Diagnostic) => boolean; + test?: (diagnostic: Diagnostic) => boolean; }; export type DiagnosticLocation = { - sourceText?: string; - mtime?: number; - marker?: string; - language?: DiagnosticLanguage; - sourceType?: DiagnosticSourceType; - filename?: string; - start?: Position; - end?: Position; + sourceText?: string; + mtime?: number; + marker?: string; + language?: DiagnosticLanguage; + sourceType?: DiagnosticSourceType; + filename?: string; + start?: Position; + end?: Position; }; export type DiagnosticOrigin = { - category: string; - message?: string; + category: string; + message?: string; }; export type DiagnosticLogCategory = 'none' | 'info' | 'warn' | 'error'; @@ -61,130 +61,130 @@ export type DiagnosticLanguage = 'json' | 'js' | 'url' | 'shell' | 'unknown'; export type DiagnosticSourceType = 'unknown' | ConstSourceType; export type DiagnosticsMeta = { - identifierName?: string; + identifierName?: string; }; export type Diagnostic = { - description: DiagnosticDescription; - location: DiagnosticLocation; - unique?: boolean; - fixable?: boolean; - label?: string; - origins?: Array; - dependencies?: Array<{ - filename: string; - mtime: number; - }>; - meta?: DiagnosticsMeta; + description: DiagnosticDescription; + location: DiagnosticLocation; + unique?: boolean; + fixable?: boolean; + label?: string; + origins?: Array; + dependencies?: Array<{ + filename: string; + mtime: number; + }>; + meta?: DiagnosticsMeta; }; export type Diagnostics = Array; export type DiagnosticDescription = { - category: DiagnosticCategory; - message: DiagnosticBlessedMessage; - advice: DiagnosticAdvice; + category: DiagnosticCategory; + message: DiagnosticBlessedMessage; + advice: DiagnosticAdvice; }; export type DiagnosticDescriptionOptionalCategory = { - category?: DiagnosticCategory; - message: DiagnosticBlessedMessage; - advice?: DiagnosticAdvice; + category?: DiagnosticCategory; + message: DiagnosticBlessedMessage; + advice?: DiagnosticAdvice; }; // TS doesn't have opaque types so we need to use an intermediate object export type DiagnosticBlessedMessage = { - type: 'PARTIAL_BLESSED_DIAGNOSTIC_MESSAGE'; - value: string; + type: 'PARTIAL_BLESSED_DIAGNOSTIC_MESSAGE'; + value: string; }; export type DiagnosticAdviceItem = - | DiagnosticAdviceLog - | DiagnosticAdviceList - | DiagnosticAdviceInspect - | DiagnosticAdviceCode - | DiagnosticAdviceFrame - | DiagnosticAdviceDiff - | DiagnosticAdviceStacktrace - | DiagnosticAdviceCommand - | DiagnosticAdviceAction; + | DiagnosticAdviceLog + | DiagnosticAdviceList + | DiagnosticAdviceInspect + | DiagnosticAdviceCode + | DiagnosticAdviceFrame + | DiagnosticAdviceDiff + | DiagnosticAdviceStacktrace + | DiagnosticAdviceCommand + | DiagnosticAdviceAction; export type DiagnosticAdviceCommand = { - type: 'command'; - command: string; + type: 'command'; + command: string; }; export type DiagnosticAdviceLog = { - type: 'log'; - category: DiagnosticLogCategory; - text: string; - compact?: boolean; + type: 'log'; + category: DiagnosticLogCategory; + text: string; + compact?: boolean; }; export type DiagnosticAdviceList = { - type: 'list'; - list: Array; - truncate?: boolean; - reverse?: boolean; - ordered?: boolean; + type: 'list'; + list: Array; + truncate?: boolean; + reverse?: boolean; + ordered?: boolean; }; export type DiagnosticAdviceInspect = { - type: 'inspect'; - data: JSONPropertyValue; + type: 'inspect'; + data: JSONPropertyValue; }; export type DiagnosticAdviceAction = { - type: 'action'; - hidden?: boolean; - extra?: boolean; - shortcut?: string; - instruction: string; - noun: string; - command: string; - commandFlags?: Dict>; - requestFlags?: ClientRequestFlags; - args?: Array; + type: 'action'; + hidden?: boolean; + extra?: boolean; + shortcut?: string; + instruction: string; + noun: string; + command: string; + commandFlags?: Dict>; + requestFlags?: ClientRequestFlags; + args?: Array; }; export type DiagnosticAdviceCode = { - type: 'code'; - code: string; - sourceType?: ConstSourceType; - language?: DiagnosticLanguage; + type: 'code'; + code: string; + sourceType?: ConstSourceType; + language?: DiagnosticLanguage; }; export type DiagnosticAdviceFrame = { - type: 'frame'; - location: DiagnosticLocation; + type: 'frame'; + location: DiagnosticLocation; }; export type DiagnosticAdviceDiff = { - type: 'diff'; - diff: Diffs; - legend?: { - add: string; - delete: string; - }; + type: 'diff'; + diff: Diffs; + legend?: { + add: string; + delete: string; + }; }; export type DiagnosticAdviceStacktrace = { - type: 'stacktrace'; - title?: string; - truncate?: boolean; - frames: Array; + type: 'stacktrace'; + title?: string; + truncate?: boolean; + frames: Array; }; export type DiagnosticAdvice = Array; export type DiagnosticAdviceStackFrame = { - prefix?: string; - suffix?: string; - object?: string; - property?: string; - filename?: string; - line?: Number1; - column?: Number0; - language?: DiagnosticLanguage; - sourceText?: string; + prefix?: string; + suffix?: string; + object?: string; + property?: string; + filename?: string; + line?: Number1; + column?: Number0; + language?: DiagnosticLanguage; + sourceText?: string; }; diff --git a/packages/@romejs/diagnostics/wrap.ts b/packages/@romejs/diagnostics/wrap.ts index 60c05c755b8..3ad2e6b87a1 100644 --- a/packages/@romejs/diagnostics/wrap.ts +++ b/packages/@romejs/diagnostics/wrap.ts @@ -10,59 +10,59 @@ import {addOriginsToDiagnostics} from './derive'; import {getDiagnosticsFromError} from './errors'; type WrapResult = - | { - readonly value: T; - readonly diagnostics: undefined; - } - | { - readonly value: undefined; - readonly diagnostics: Diagnostics; - }; + | { + readonly value: T; + readonly diagnostics: undefined; + } + | { + readonly value: undefined; + readonly diagnostics: Diagnostics; + }; export async function catchDiagnostics( - promise: () => Promise, - origin?: DiagnosticOrigin, + promise: () => Promise, + origin?: DiagnosticOrigin, ): Promise> { - try { - const value = await promise(); + try { + const value = await promise(); - return {value, diagnostics: undefined}; - } catch (err) { - const diagnostics = getDiagnosticsFromError(err); + return {value, diagnostics: undefined}; + } catch (err) { + const diagnostics = getDiagnosticsFromError(err); - if (diagnostics) { - return { - value: undefined, - diagnostics: origin === undefined - ? diagnostics - : addOriginsToDiagnostics([origin], diagnostics), - }; - } else { - throw err; - } - } + if (diagnostics) { + return { + value: undefined, + diagnostics: origin === undefined + ? diagnostics + : addOriginsToDiagnostics([origin], diagnostics), + }; + } else { + throw err; + } + } } export function catchDiagnosticsSync( - callback: () => T, - origin?: DiagnosticOrigin, + callback: () => T, + origin?: DiagnosticOrigin, ): WrapResult { - try { - const value = callback(); + try { + const value = callback(); - return {value, diagnostics: undefined}; - } catch (err) { - const diagnostics = getDiagnosticsFromError(err); + return {value, diagnostics: undefined}; + } catch (err) { + const diagnostics = getDiagnosticsFromError(err); - if (diagnostics) { - return { - value: undefined, - diagnostics: origin === undefined - ? diagnostics - : addOriginsToDiagnostics([origin], diagnostics), - }; - } else { - throw err; - } - } + if (diagnostics) { + return { + value: undefined, + diagnostics: origin === undefined + ? diagnostics + : addOriginsToDiagnostics([origin], diagnostics), + }; + } else { + throw err; + } + } } diff --git a/packages/@romejs/events/Bridge.ts b/packages/@romejs/events/Bridge.ts index ff4482cb3b5..6107d6807a9 100644 --- a/packages/@romejs/events/Bridge.ts +++ b/packages/@romejs/events/Bridge.ts @@ -6,445 +6,445 @@ */ import { - BridgeErrorResponseMessage, - BridgeMessage, - BridgeOptions, - BridgeRequestMessage, - BridgeResponseMessage, - BridgeSuccessResponseMessage, - BridgeType, - EventSubscription, + BridgeErrorResponseMessage, + BridgeMessage, + BridgeOptions, + BridgeRequestMessage, + BridgeResponseMessage, + BridgeSuccessResponseMessage, + BridgeType, + EventSubscription, } from './types'; import {JSONObject, JSONPropertyValue} from '@romejs/codec-json'; import BridgeError from './BridgeError'; import BridgeEvent, {BridgeEventOptions} from './BridgeEvent'; import Event from './Event'; import { - ERROR_FRAMES_PROP, - StructuredError, - getErrorStructure, + ERROR_FRAMES_PROP, + StructuredError, + getErrorStructure, } from '@romejs/v8'; type ErrorJSON = { - serialize: (err: Error) => JSONObject; - hydrate: (err: StructuredError, obj: JSONObject) => Error; + serialize: (err: Error) => JSONObject; + hydrate: (err: StructuredError, obj: JSONObject) => Error; }; export default class Bridge { - constructor(opts: BridgeOptions) { - this.errorTransports = new Map(); - - this.alive = true; - this.type = opts.type; - this.opts = opts; - - this.messageIdCounter = 0; - this.events = new Map(); - - this.hasHandshook = false; - this.handshakeEvent = new Event({name: 'Bridge.handshake'}); - this.endEvent = new Event({name: 'Bridge.end', serial: true}); - this.updatedListenersEvent = new Event({ - name: 'Bridge.updatedListenersEvent', - }); - - // A Set of event names that are being listened to on the other end - - // We track this to avoid sending over subscriptions that aren't needed - this.listeners = new Set(); - - this.prioritizedResponses = new Set(); - this.deprioritizedResponseQueue = []; - - this.postHandshakeQueue = []; - - this.heartbeatEvent = this.createEvent({ - name: 'Bridge.heartbeat', - direction: 'server<->client', - }); - - if (this.type !== 'server&client') { - this.heartbeatEvent.subscribe(() => { - return undefined; - }); - } - - this.clear(); - this.init(); - } - - heartbeatEvent: BridgeEvent; - heartbeatTimeout: undefined | NodeJS.Timeout; - - prioritizedResponses: Set; - deprioritizedResponseQueue: Array; - postHandshakeQueue: Array; - - handshakeEvent: Event< - { - first: boolean; - subscriptions: Array; - }, - void - >; - hasHandshook: boolean; - endEvent: Event; - - alive: boolean; - type: BridgeType; - - messageIdCounter: number; - // rome-ignore lint/noExplicitAny - events: Map>; - - listeners: Set; - updatedListenersEvent: Event, void>; - - opts: BridgeOptions; - errorTransports: Map; - - attachEndSubscriptionRemoval(subscription: EventSubscription) { - this.endEvent.subscribe(() => { - subscription.unsubscribe(); - }); - } - - monitorHeartbeat(timeout: number, onExceeded: () => undefined | Promise) { - if (this.type === 'server&client') { - // No point in monitoring this since we're the same process - return; - } - - this.heartbeatTimeout = setTimeout( - async () => { - try { - await this.heartbeatEvent.call(undefined, {timeout}); - this.monitorHeartbeat(timeout, onExceeded); - } catch (err) { - if (err instanceof BridgeError) { - if (this.alive) { - onExceeded(); - } - } else { - throw err; - } - } - }, - 1_000, - ); - } - - clearPrioritization(id: number) { - this.prioritizedResponses.delete(id); - - if (this.prioritizedResponses.size === 0) { - for (const msg of this.deprioritizedResponseQueue) { - this.sendMessage(msg); - } - this.deprioritizedResponseQueue = []; - } - } - - async handshake( - opts: { - timeout?: number; - second?: boolean; - } = {}, - ): Promise { - if (this.hasHandshook) { - throw new Error('Already performed handshake'); - } - - const {timeout, second = false} = opts; - - // Send a handshake in case we were the first - if (!second) { - this.sendMessage({ - type: 'handshake', - first: true, - subscriptions: this.getSubscriptions(), - }); - } - - // Wait for a handshake from the other end - const res = await this.handshakeEvent.wait(undefined, timeout); - - if (res.first) { - // Send the handshake again, as it wouldn't have received the first - this.sendMessage({ - type: 'handshake', - first: false, - subscriptions: this.getSubscriptions(), - }); - } - - this.receivedSubscriptions(res.subscriptions); - - this.hasHandshook = true; - - for (const msg of this.postHandshakeQueue) { - this.sendMessage(msg); - } - this.postHandshakeQueue = []; - } - - getSubscriptions(): Array { - const names = []; - for (const event of this.events.values()) { - if (event.hasSubscriptions()) { - names.push(event.name); - } - } - return names; - } - - sendSubscriptions(): void { - if (!this.hasHandshook) { - // If we haven't had the handshake then no point sending them. They'll be sent all at once after - return; - } - - // Notify the other side of what we're currently subscribed to - // We send over a list of all of our subscriptions every time - // This is fine since we don't change subscriptions often and they aren't very large - // If we have a lot of subscriptions, or are changing them a lot in the future then this could be optimized - this.sendMessage({ - type: 'subscriptions', - names: this.getSubscriptions(), - }); - } - - receivedSubscriptions(names: Array): void { - this.listeners = new Set(names); - this.updatedListenersEvent.send(this.listeners); - } - - init(): void { - // This method can be overridden by subclasses, it allows you to add logic such as error serializers - } - - clear(): void { - for (const [, event] of this.events) { - event.clear(); - } - } - - getNextMessageId(): number { - return ++this.messageIdCounter; - } - - createEvent( - opts: BridgeEventOptions, - ): BridgeEvent { - if (this.events.has(opts.name)) { - throw new Error('Duplicate event'); - } - - const event = new BridgeEvent(opts, this); - this.events.set(opts.name, event); - return event; - } - - //# Connection death - assertAlive(): void { - if (this.alive === false) { - throw new Error('Bridge is dead'); - } - } - - endWithError(err: Error): void { - if (this.alive === false) { - return; - } - - this.alive = false; - - // Reject any pending requests - for (const [, event] of this.events) { - event.end(err); - } - this.clear(); - - // Clear any currently processing heartbeat - if (this.heartbeatTimeout !== undefined) { - clearTimeout(this.heartbeatTimeout); - } - - // Notify listeners - this.endEvent.callSync(err); - } - - end(message: string = 'Connection died') { - this.endWithError(new BridgeError(message, this)); - } - - //# Error serialization - buildError(struct: StructuredError, data: JSONObject) { - const transport = this.errorTransports.get(struct.name); - if (transport === undefined) { - const err: Error & { - [ERROR_FRAMES_PROP]?: unknown; - } = new Error(struct.message); - err.name = struct.name || 'Error'; - err.stack = struct.stack; - err[ERROR_FRAMES_PROP] = struct.frames; - return err; - } else { - return transport.hydrate(struct, data); - } - } - - buildErrorResponse( - id: number, - event: string, - errRaw: unknown, - ): BridgeErrorResponseMessage { - // Just in case something that wasn't an Error was thrown - const err = errRaw instanceof Error ? errRaw : new Error(String(errRaw)); - - // Fetch some metadata for hydration - const tranport = this.errorTransports.get(err.name); - const metadata: JSONObject = - tranport === undefined ? {} : tranport.serialize(err); - - return { - id, - event, - type: 'response', - responseStatus: 'error', - value: getErrorStructure(err), - metadata, - }; - } - - addErrorTransport(name: string, transport: ErrorJSON) { - this.errorTransports.set(name, transport); - } - - //# Message transmission - sendMessage(msg: BridgeMessage) { - // There's no try-catch gated around sendMessage because the call stack here will include some other error handler - // We need to be specific for handleMessage because it could come from anywhere - if (msg.type !== 'handshake' && !this.hasHandshook) { - this.postHandshakeQueue.push(msg); - return; - } - - this.assertAlive(); - - if (msg.type === 'response') { - if ( - this.prioritizedResponses.size > 0 && - !this.prioritizedResponses.has(msg.id) - ) { - this.deprioritizedResponseQueue.push(msg); - return; - } - - if (this.prioritizedResponses.has(msg.id)) { - this.clearPrioritization(msg.id); - } - } - - const {opts} = this; - opts.sendMessage(msg); - if (opts.onSendMessage !== undefined) { - opts.onSendMessage(msg); - } - } - - handleJSONMessage(str: string) { - try { - const data = JSON.parse(str); - this.handleMessage(data); - } catch (err) { - if (err instanceof SyntaxError) { - this.endWithError( - new BridgeError(`Error parsing message JSON: ${err.message}`, this), - ); - } else { - this.endWithError(err); - } - } - } - - handleMessage(msg: BridgeMessage) { - try { - this.assertAlive(); - - if (msg.type === 'handshake') { - this.handshakeEvent.send({ - subscriptions: msg.subscriptions, - first: msg.first, - }); - } - - if (msg.type === 'subscriptions') { - this.receivedSubscriptions(msg.names); - } - - if (msg.type === 'request') { - this.handleMessageRequest(msg); - } - - if (msg.type === 'response') { - this.handleMessageResponse(msg); - } - } catch (err) { - this.endWithError(err); - } - } - - handleMessageResponse( - data: BridgeSuccessResponseMessage | BridgeErrorResponseMessage, - ) { - const {id, event} = data; - if (id === undefined) { - throw new Error('Expected id'); - } - if (event === undefined) { - throw new Error('Expected event'); - } - - const eventHandler = this.events.get(event); - if (eventHandler === undefined) { - throw new Error('Unknown event'); - } - - eventHandler.dispatchResponse(id, data); - } - - handleMessageRequest(data: BridgeRequestMessage) { - const {id, event, param, priority} = data; - if (event === undefined) { - throw new Error('Expected event in message request but received none'); - } - - const eventHandler = this.events.get(event); - if (eventHandler === undefined) { - throw new Error(`Unknown event ${event}`); - } - - if (id === undefined) { - // We don't need to do anything with the return value of this since - // there's nothing on the other end to catch it - eventHandler.dispatchRequest(param); - } else { - if (priority) { - this.prioritizedResponses.add(id); - } - - eventHandler.dispatchRequest(param).then((value) => { - this.sendMessage({ - event, - id, - type: 'response', - responseStatus: 'success', - value, - }); - }).catch((err) => { - this.sendMessage(this.buildErrorResponse(id, event, err)); - }).catch((err) => this.endWithError(err)); - } - } + constructor(opts: BridgeOptions) { + this.errorTransports = new Map(); + + this.alive = true; + this.type = opts.type; + this.opts = opts; + + this.messageIdCounter = 0; + this.events = new Map(); + + this.hasHandshook = false; + this.handshakeEvent = new Event({name: 'Bridge.handshake'}); + this.endEvent = new Event({name: 'Bridge.end', serial: true}); + this.updatedListenersEvent = new Event({ + name: 'Bridge.updatedListenersEvent', + }); + + // A Set of event names that are being listened to on the other end + + // We track this to avoid sending over subscriptions that aren't needed + this.listeners = new Set(); + + this.prioritizedResponses = new Set(); + this.deprioritizedResponseQueue = []; + + this.postHandshakeQueue = []; + + this.heartbeatEvent = this.createEvent({ + name: 'Bridge.heartbeat', + direction: 'server<->client', + }); + + if (this.type !== 'server&client') { + this.heartbeatEvent.subscribe(() => { + return undefined; + }); + } + + this.clear(); + this.init(); + } + + heartbeatEvent: BridgeEvent; + heartbeatTimeout: undefined | NodeJS.Timeout; + + prioritizedResponses: Set; + deprioritizedResponseQueue: Array; + postHandshakeQueue: Array; + + handshakeEvent: Event< + { + first: boolean; + subscriptions: Array; + }, + void + >; + hasHandshook: boolean; + endEvent: Event; + + alive: boolean; + type: BridgeType; + + messageIdCounter: number; + // rome-ignore lint/noExplicitAny + events: Map>; + + listeners: Set; + updatedListenersEvent: Event, void>; + + opts: BridgeOptions; + errorTransports: Map; + + attachEndSubscriptionRemoval(subscription: EventSubscription) { + this.endEvent.subscribe(() => { + subscription.unsubscribe(); + }); + } + + monitorHeartbeat(timeout: number, onExceeded: () => undefined | Promise) { + if (this.type === 'server&client') { + // No point in monitoring this since we're the same process + return; + } + + this.heartbeatTimeout = setTimeout( + async () => { + try { + await this.heartbeatEvent.call(undefined, {timeout}); + this.monitorHeartbeat(timeout, onExceeded); + } catch (err) { + if (err instanceof BridgeError) { + if (this.alive) { + onExceeded(); + } + } else { + throw err; + } + } + }, + 1_000, + ); + } + + clearPrioritization(id: number) { + this.prioritizedResponses.delete(id); + + if (this.prioritizedResponses.size === 0) { + for (const msg of this.deprioritizedResponseQueue) { + this.sendMessage(msg); + } + this.deprioritizedResponseQueue = []; + } + } + + async handshake( + opts: { + timeout?: number; + second?: boolean; + } = {}, + ): Promise { + if (this.hasHandshook) { + throw new Error('Already performed handshake'); + } + + const {timeout, second = false} = opts; + + // Send a handshake in case we were the first + if (!second) { + this.sendMessage({ + type: 'handshake', + first: true, + subscriptions: this.getSubscriptions(), + }); + } + + // Wait for a handshake from the other end + const res = await this.handshakeEvent.wait(undefined, timeout); + + if (res.first) { + // Send the handshake again, as it wouldn't have received the first + this.sendMessage({ + type: 'handshake', + first: false, + subscriptions: this.getSubscriptions(), + }); + } + + this.receivedSubscriptions(res.subscriptions); + + this.hasHandshook = true; + + for (const msg of this.postHandshakeQueue) { + this.sendMessage(msg); + } + this.postHandshakeQueue = []; + } + + getSubscriptions(): Array { + const names = []; + for (const event of this.events.values()) { + if (event.hasSubscriptions()) { + names.push(event.name); + } + } + return names; + } + + sendSubscriptions(): void { + if (!this.hasHandshook) { + // If we haven't had the handshake then no point sending them. They'll be sent all at once after + return; + } + + // Notify the other side of what we're currently subscribed to + // We send over a list of all of our subscriptions every time + // This is fine since we don't change subscriptions often and they aren't very large + // If we have a lot of subscriptions, or are changing them a lot in the future then this could be optimized + this.sendMessage({ + type: 'subscriptions', + names: this.getSubscriptions(), + }); + } + + receivedSubscriptions(names: Array): void { + this.listeners = new Set(names); + this.updatedListenersEvent.send(this.listeners); + } + + init(): void { + // This method can be overridden by subclasses, it allows you to add logic such as error serializers + } + + clear(): void { + for (const [, event] of this.events) { + event.clear(); + } + } + + getNextMessageId(): number { + return ++this.messageIdCounter; + } + + createEvent( + opts: BridgeEventOptions, + ): BridgeEvent { + if (this.events.has(opts.name)) { + throw new Error('Duplicate event'); + } + + const event = new BridgeEvent(opts, this); + this.events.set(opts.name, event); + return event; + } + + //# Connection death + assertAlive(): void { + if (this.alive === false) { + throw new Error('Bridge is dead'); + } + } + + endWithError(err: Error): void { + if (this.alive === false) { + return; + } + + this.alive = false; + + // Reject any pending requests + for (const [, event] of this.events) { + event.end(err); + } + this.clear(); + + // Clear any currently processing heartbeat + if (this.heartbeatTimeout !== undefined) { + clearTimeout(this.heartbeatTimeout); + } + + // Notify listeners + this.endEvent.callSync(err); + } + + end(message: string = 'Connection died') { + this.endWithError(new BridgeError(message, this)); + } + + //# Error serialization + buildError(struct: StructuredError, data: JSONObject) { + const transport = this.errorTransports.get(struct.name); + if (transport === undefined) { + const err: Error & { + [ERROR_FRAMES_PROP]?: unknown; + } = new Error(struct.message); + err.name = struct.name || 'Error'; + err.stack = struct.stack; + err[ERROR_FRAMES_PROP] = struct.frames; + return err; + } else { + return transport.hydrate(struct, data); + } + } + + buildErrorResponse( + id: number, + event: string, + errRaw: unknown, + ): BridgeErrorResponseMessage { + // Just in case something that wasn't an Error was thrown + const err = errRaw instanceof Error ? errRaw : new Error(String(errRaw)); + + // Fetch some metadata for hydration + const tranport = this.errorTransports.get(err.name); + const metadata: JSONObject = + tranport === undefined ? {} : tranport.serialize(err); + + return { + id, + event, + type: 'response', + responseStatus: 'error', + value: getErrorStructure(err), + metadata, + }; + } + + addErrorTransport(name: string, transport: ErrorJSON) { + this.errorTransports.set(name, transport); + } + + //# Message transmission + sendMessage(msg: BridgeMessage) { + // There's no try-catch gated around sendMessage because the call stack here will include some other error handler + // We need to be specific for handleMessage because it could come from anywhere + if (msg.type !== 'handshake' && !this.hasHandshook) { + this.postHandshakeQueue.push(msg); + return; + } + + this.assertAlive(); + + if (msg.type === 'response') { + if ( + this.prioritizedResponses.size > 0 && + !this.prioritizedResponses.has(msg.id) + ) { + this.deprioritizedResponseQueue.push(msg); + return; + } + + if (this.prioritizedResponses.has(msg.id)) { + this.clearPrioritization(msg.id); + } + } + + const {opts} = this; + opts.sendMessage(msg); + if (opts.onSendMessage !== undefined) { + opts.onSendMessage(msg); + } + } + + handleJSONMessage(str: string) { + try { + const data = JSON.parse(str); + this.handleMessage(data); + } catch (err) { + if (err instanceof SyntaxError) { + this.endWithError( + new BridgeError(`Error parsing message JSON: ${err.message}`, this), + ); + } else { + this.endWithError(err); + } + } + } + + handleMessage(msg: BridgeMessage) { + try { + this.assertAlive(); + + if (msg.type === 'handshake') { + this.handshakeEvent.send({ + subscriptions: msg.subscriptions, + first: msg.first, + }); + } + + if (msg.type === 'subscriptions') { + this.receivedSubscriptions(msg.names); + } + + if (msg.type === 'request') { + this.handleMessageRequest(msg); + } + + if (msg.type === 'response') { + this.handleMessageResponse(msg); + } + } catch (err) { + this.endWithError(err); + } + } + + handleMessageResponse( + data: BridgeSuccessResponseMessage | BridgeErrorResponseMessage, + ) { + const {id, event} = data; + if (id === undefined) { + throw new Error('Expected id'); + } + if (event === undefined) { + throw new Error('Expected event'); + } + + const eventHandler = this.events.get(event); + if (eventHandler === undefined) { + throw new Error('Unknown event'); + } + + eventHandler.dispatchResponse(id, data); + } + + handleMessageRequest(data: BridgeRequestMessage) { + const {id, event, param, priority} = data; + if (event === undefined) { + throw new Error('Expected event in message request but received none'); + } + + const eventHandler = this.events.get(event); + if (eventHandler === undefined) { + throw new Error(`Unknown event ${event}`); + } + + if (id === undefined) { + // We don't need to do anything with the return value of this since + // there's nothing on the other end to catch it + eventHandler.dispatchRequest(param); + } else { + if (priority) { + this.prioritizedResponses.add(id); + } + + eventHandler.dispatchRequest(param).then((value) => { + this.sendMessage({ + event, + id, + type: 'response', + responseStatus: 'success', + value, + }); + }).catch((err) => { + this.sendMessage(this.buildErrorResponse(id, event, err)); + }).catch((err) => this.endWithError(err)); + } + } } diff --git a/packages/@romejs/events/BridgeError.ts b/packages/@romejs/events/BridgeError.ts index dd5db38c3b8..ce60ec3e728 100644 --- a/packages/@romejs/events/BridgeError.ts +++ b/packages/@romejs/events/BridgeError.ts @@ -7,10 +7,10 @@ import Bridge from './Bridge'; * LICENSE file in the root directory of this source tree. */ export default class BridgeError extends Error { - constructor(message: string, bridge: Bridge) { - super(message); - this.bridge = bridge; - } + constructor(message: string, bridge: Bridge) { + super(message); + this.bridge = bridge; + } - bridge: Bridge; + bridge: Bridge; } diff --git a/packages/@romejs/events/BridgeEvent.ts b/packages/@romejs/events/BridgeEvent.ts index 298990710a2..e69f9043e47 100644 --- a/packages/@romejs/events/BridgeEvent.ts +++ b/packages/@romejs/events/BridgeEvent.ts @@ -7,207 +7,207 @@ import {JSONPropertyValue} from '@romejs/codec-json'; import { - BridgeErrorResponseMessage, - BridgeSuccessResponseMessage, - BridgeType, - EventOptions, + BridgeErrorResponseMessage, + BridgeSuccessResponseMessage, + BridgeType, + EventOptions, } from './types'; import Bridge from './Bridge'; import BridgeError from './BridgeError'; import Event from './Event'; type CallOptions = { - timeout?: number; - priority?: boolean; + timeout?: number; + priority?: boolean; }; export type BridgeEventDirection = - | 'server->client' - | 'server<-client' - | 'server<->client'; + | 'server->client' + | 'server<-client' + | 'server<->client'; export type BridgeEventOptions = EventOptions & { - direction: BridgeEventDirection; + direction: BridgeEventDirection; }; function validateDirection( - // rome-ignore lint/noExplicitAny - event: BridgeEvent, - invalidDirections: Array<[BridgeEventDirection, BridgeType]>, - verb: string, + // rome-ignore lint/noExplicitAny + event: BridgeEvent, + invalidDirections: Array<[BridgeEventDirection, BridgeType]>, + verb: string, ) { - invalidDirections.push(['server<->client', 'server&client']); - - for (const [eventDirection, bridgeType] of invalidDirections) { - if (event.direction === eventDirection && event.bridge.type === bridgeType) { - throw new Error( - `The ${eventDirection} event "${event.name}" cannot be ${verb} by a ${bridgeType} bridge`, - ); - } - } + invalidDirections.push(['server<->client', 'server&client']); + + for (const [eventDirection, bridgeType] of invalidDirections) { + if (event.direction === eventDirection && event.bridge.type === bridgeType) { + throw new Error( + `The ${eventDirection} event "${event.name}" cannot be ${verb} by a ${bridgeType} bridge`, + ); + } + } } export default class BridgeEvent< - Param extends JSONPropertyValue, - Ret extends JSONPropertyValue + Param extends JSONPropertyValue, + Ret extends JSONPropertyValue > extends Event { - constructor(opts: BridgeEventOptions, bridge: Bridge) { - super(opts); - - this.bridge = bridge; - this.requestCallbacks = new Map(); - this.direction = opts.direction; - } - - bridge: Bridge; - direction: BridgeEventDirection; - requestCallbacks: Map< - number, - { - completed: undefined | (() => void); - resolve: (data: Ret) => void; - reject: (err: Error) => void; - } - >; - - clear() { - super.clear(); - this.requestCallbacks.clear(); - } - - end(err: Error) { - for (const {reject} of this.requestCallbacks.values()) { - reject(err); - } - } - - onSubscriptionChange() { - validateDirection( - this, - [['server->client', 'client'], ['server<-client', 'server']], - 'subscribed', - ); - this.bridge.sendSubscriptions(); - } - - dispatchRequest(param: Param): Promise { - return super.call(param); - } - - dispatchResponse( - id: number, - data: BridgeSuccessResponseMessage | BridgeErrorResponseMessage, - ): void { - const callbacks = this.requestCallbacks.get(id); - if (!callbacks) { - // ??? - return; - } - - this.requestCallbacks.delete(id); - - if (data.responseStatus === 'success') { - // @ts-ignore - callbacks.resolve(data.value); - } else if (data.responseStatus === 'error') { - try { - callbacks.reject(this.bridge.buildError(data.value, data.metadata)); - } catch (err) { - callbacks.reject(err); - } - } else { - // ??? - } - - if (callbacks.completed !== undefined) { - callbacks.completed(); - } - } - - hasSubscribers(): boolean { - return this.bridge.listeners.has(this.name); - } - - validateCanSend(): void { - validateDirection( - this, - [['server<-client', 'client'], ['server->client', 'server']], - 'called', - ); - } - - send(param: Param): void { - if (!this.hasSubscribers()) { - // No point in sending over a subscription that doesn't have a listener - return; - } - - this.validateCanSend(); - this.bridge.assertAlive(); - this.bridge.sendMessage({ - type: 'request', - event: this.name, - param, - priority: false, - }); - } - - async call(param: Param, opts: CallOptions = {}): Promise { - const {priority = false, timeout} = opts; - this.validateCanSend(); - - try { - return await new Promise((resolve, reject) => { - this.bridge.assertAlive(); - - const id = this.bridge.getNextMessageId(); - - let completed; - if (timeout !== undefined) { - const timeoutId = setTimeout( - () => { - // Remove the request callback - this.requestCallbacks.delete(id); - - // Reject the promise - reject( - new BridgeError( - `Timeout of ${String(timeout)}ms for ${this.name}(${String( - JSON.stringify(param), - )}) event exceeded`, - this.bridge, - ), - ); - }, - timeout, - ); - - // Cancel the timeout if the response returns before the timer - completed = () => { - clearTimeout(timeoutId); - }; - } - - this.requestCallbacks.set( - id, - { - completed, - reject, - resolve, - }, - ); - - this.bridge.sendMessage({ - id, - event: this.name, - param, - type: 'request', - priority, - }); - }); - } catch (err) { - this.onError(err); - throw err; - } - } + constructor(opts: BridgeEventOptions, bridge: Bridge) { + super(opts); + + this.bridge = bridge; + this.requestCallbacks = new Map(); + this.direction = opts.direction; + } + + bridge: Bridge; + direction: BridgeEventDirection; + requestCallbacks: Map< + number, + { + completed: undefined | (() => void); + resolve: (data: Ret) => void; + reject: (err: Error) => void; + } + >; + + clear() { + super.clear(); + this.requestCallbacks.clear(); + } + + end(err: Error) { + for (const {reject} of this.requestCallbacks.values()) { + reject(err); + } + } + + onSubscriptionChange() { + validateDirection( + this, + [['server->client', 'client'], ['server<-client', 'server']], + 'subscribed', + ); + this.bridge.sendSubscriptions(); + } + + dispatchRequest(param: Param): Promise { + return super.call(param); + } + + dispatchResponse( + id: number, + data: BridgeSuccessResponseMessage | BridgeErrorResponseMessage, + ): void { + const callbacks = this.requestCallbacks.get(id); + if (!callbacks) { + // ??? + return; + } + + this.requestCallbacks.delete(id); + + if (data.responseStatus === 'success') { + // @ts-ignore + callbacks.resolve(data.value); + } else if (data.responseStatus === 'error') { + try { + callbacks.reject(this.bridge.buildError(data.value, data.metadata)); + } catch (err) { + callbacks.reject(err); + } + } else { + // ??? + } + + if (callbacks.completed !== undefined) { + callbacks.completed(); + } + } + + hasSubscribers(): boolean { + return this.bridge.listeners.has(this.name); + } + + validateCanSend(): void { + validateDirection( + this, + [['server<-client', 'client'], ['server->client', 'server']], + 'called', + ); + } + + send(param: Param): void { + if (!this.hasSubscribers()) { + // No point in sending over a subscription that doesn't have a listener + return; + } + + this.validateCanSend(); + this.bridge.assertAlive(); + this.bridge.sendMessage({ + type: 'request', + event: this.name, + param, + priority: false, + }); + } + + async call(param: Param, opts: CallOptions = {}): Promise { + const {priority = false, timeout} = opts; + this.validateCanSend(); + + try { + return await new Promise((resolve, reject) => { + this.bridge.assertAlive(); + + const id = this.bridge.getNextMessageId(); + + let completed; + if (timeout !== undefined) { + const timeoutId = setTimeout( + () => { + // Remove the request callback + this.requestCallbacks.delete(id); + + // Reject the promise + reject( + new BridgeError( + `Timeout of ${String(timeout)}ms for ${this.name}(${String( + JSON.stringify(param), + )}) event exceeded`, + this.bridge, + ), + ); + }, + timeout, + ); + + // Cancel the timeout if the response returns before the timer + completed = () => { + clearTimeout(timeoutId); + }; + } + + this.requestCallbacks.set( + id, + { + completed, + reject, + resolve, + }, + ); + + this.bridge.sendMessage({ + id, + event: this.name, + param, + type: 'request', + priority, + }); + }); + } catch (err) { + this.onError(err); + throw err; + } + } } diff --git a/packages/@romejs/events/Event.ts b/packages/@romejs/events/Event.ts index 9264ec553ff..6885db214f3 100644 --- a/packages/@romejs/events/Event.ts +++ b/packages/@romejs/events/Event.ts @@ -10,194 +10,192 @@ import {EventOptions, EventSubscription} from './types'; type Callback = (param: Param) => Ret | Promise; function noPromise(ret: Ret | Promise): Ret { - if (ret instanceof Promise) { - throw new Error('Subscription returned promise for a callSync'); - } else { - return ret; - } + if (ret instanceof Promise) { + throw new Error('Subscription returned promise for a callSync'); + } else { + return ret; + } } export default class Event { - constructor(opts: EventOptions) { - this.subscriptions = new Set(); - this.rootSubscription = undefined; - this.name = opts.name; - this.options = opts; - } - - name: string; - options: EventOptions; - - rootSubscription: undefined | Callback; - subscriptions: Set>; - - onSubscriptionChange() { - // Hook for BridgeEvent - } - - onError(err: Error) { - const {onError} = this.options; - if (onError !== undefined) { - onError(err); - } - } - - clear() { - this.subscriptions.clear(); - } - - hasSubscribers(): boolean { - return this.hasSubscriptions(); - } - - hasSubscriptions(): boolean { - return this.rootSubscription !== undefined; - } - - // Dispatch the event without caring about the return values - send(param: Param) { - const {rootSubscription} = this; - if (rootSubscription === undefined) { - return; - } - - rootSubscription(param); - - for (const callback of this.subscriptions) { - callback(param); - } - } - - callSync(param: Param): Ret { - try { - const {rootSubscription, subscriptions} = this; - if (rootSubscription === undefined) { - throw new Error(`No subscription for event ${this.name}`); - } - - const ret = noPromise(rootSubscription(param)); - for (const callback of subscriptions) { - noPromise(callback(param)); - } - return ret; - } catch (err) { - this.onError(err); - throw err; - } - } - - async call(param: Param): Promise { - const {rootSubscription, subscriptions} = this; - if (rootSubscription === undefined) { - throw new Error(`No subscription for event ${this.name}`); - } - - try { - if (this.options.serial === true) { - const ret = await rootSubscription(param); - for (const callback of subscriptions) { - await callback(param); - } - return ret; - } else { - const res = await Promise.all([ - rootSubscription(param), - ...Array.from(subscriptions, (callback) => callback(param)), - ]); - - // Return the root subscription value - return res[0]; - } - } catch (err) { - this.onError(err); - throw err; - } - } - - wait(val: Ret, timeout?: number): Promise { - return new Promise((resolve, reject) => { - let timeoutId: undefined | NodeJS.Timeout; - let timedOut = false; - - if (timeout !== undefined) { - timeoutId = setTimeout( - () => { - timedOut = true; - listener.unsubscribe(); - reject( - new Error(`Timed out after waiting ${timeout}ms for ${this.name}`), - ); - }, - timeout, - ); - } - - const listener = this.subscribe((param) => { - if (timedOut) { - return val; - } - - if (timeoutId !== undefined) { - clearTimeout(timeoutId); - } - - listener.unsubscribe(); - resolve(param); - return val; - }); - }); - } - - async callOptional(param: Param): Promise { - if (this.rootSubscription === undefined) { - return undefined; - } else { - return this.call(param); - } - } - - subscribe( - callback: Callback, - makeRoot?: boolean, - ): EventSubscription { - if (this.options.unique === true && this.subscriptions.size !== 0) { - throw new Error(`Event ${this.name} only allows a single subscription`); - } - - if (this.rootSubscription === callback || this.subscriptions.has(callback)) { - throw new Error('Cannot double subscribe a callback'); - } - - if (this.rootSubscription === undefined) { - this.rootSubscription = callback; - } else if (makeRoot === true) { - this.subscriptions.add(this.rootSubscription); - this.rootSubscription = callback; - } else { - this.subscriptions.add(callback); - } - - this.onSubscriptionChange(); - - return { - unsubscribe: () => { - this.unsubscribe(callback); - }, - }; - } - - unsubscribe(callback: Callback) { - if (this.subscriptions.has(callback)) { - this.subscriptions.delete(callback); - this.onSubscriptionChange(); - return; - } - - // If this callback was the root subscription, then set it to the next one - if (callback === this.rootSubscription) { - this.rootSubscription = Array.from(this.subscriptions)[0]; - this.onSubscriptionChange(); - return; - } - } + constructor(opts: EventOptions) { + this.subscriptions = new Set(); + this.rootSubscription = undefined; + this.name = opts.name; + this.options = opts; + } + + name: string; + options: EventOptions; + + rootSubscription: undefined | Callback; + subscriptions: Set>; + + onSubscriptionChange() { + // Hook for BridgeEvent + } + + onError(err: Error) { + const {onError} = this.options; + if (onError !== undefined) { + onError(err); + } + } + + clear() { + this.subscriptions.clear(); + } + + hasSubscribers(): boolean { + return this.hasSubscriptions(); + } + + hasSubscriptions(): boolean { + return this.rootSubscription !== undefined; + } + + // Dispatch the event without caring about the return values + send(param: Param) { + const {rootSubscription} = this; + if (rootSubscription === undefined) { + return; + } + + rootSubscription(param); + + for (const callback of this.subscriptions) { + callback(param); + } + } + + callSync(param: Param): Ret { + try { + const {rootSubscription, subscriptions} = this; + if (rootSubscription === undefined) { + throw new Error(`No subscription for event ${this.name}`); + } + + const ret = noPromise(rootSubscription(param)); + for (const callback of subscriptions) { + noPromise(callback(param)); + } + return ret; + } catch (err) { + this.onError(err); + throw err; + } + } + + async call(param: Param): Promise { + const {rootSubscription, subscriptions} = this; + if (rootSubscription === undefined) { + throw new Error(`No subscription for event ${this.name}`); + } + + try { + if (this.options.serial === true) { + const ret = await rootSubscription(param); + for (const callback of subscriptions) { + await callback(param); + } + return ret; + } else { + const res = await Promise.all([ + rootSubscription(param), + ...Array.from(subscriptions, (callback) => callback(param)), + ]); + + // Return the root subscription value + return res[0]; + } + } catch (err) { + this.onError(err); + throw err; + } + } + + wait(val: Ret, timeout?: number): Promise { + return new Promise((resolve, reject) => { + let timeoutId: undefined | NodeJS.Timeout; + let timedOut = false; + + if (timeout !== undefined) { + timeoutId = setTimeout( + () => { + timedOut = true; + listener.unsubscribe(); + reject(new Error(`Timed out after waiting ${timeout}ms for ${this.name}`)); + }, + timeout, + ); + } + + const listener = this.subscribe((param) => { + if (timedOut) { + return val; + } + + if (timeoutId !== undefined) { + clearTimeout(timeoutId); + } + + listener.unsubscribe(); + resolve(param); + return val; + }); + }); + } + + async callOptional(param: Param): Promise { + if (this.rootSubscription === undefined) { + return undefined; + } else { + return this.call(param); + } + } + + subscribe( + callback: Callback, + makeRoot?: boolean, + ): EventSubscription { + if (this.options.unique === true && this.subscriptions.size !== 0) { + throw new Error(`Event ${this.name} only allows a single subscription`); + } + + if (this.rootSubscription === callback || this.subscriptions.has(callback)) { + throw new Error('Cannot double subscribe a callback'); + } + + if (this.rootSubscription === undefined) { + this.rootSubscription = callback; + } else if (makeRoot === true) { + this.subscriptions.add(this.rootSubscription); + this.rootSubscription = callback; + } else { + this.subscriptions.add(callback); + } + + this.onSubscriptionChange(); + + return { + unsubscribe: () => { + this.unsubscribe(callback); + }, + }; + } + + unsubscribe(callback: Callback) { + if (this.subscriptions.has(callback)) { + this.subscriptions.delete(callback); + this.onSubscriptionChange(); + return; + } + + // If this callback was the root subscription, then set it to the next one + if (callback === this.rootSubscription) { + this.rootSubscription = Array.from(this.subscriptions)[0]; + this.onSubscriptionChange(); + return; + } + } } diff --git a/packages/@romejs/events/bridgeCreators.ts b/packages/@romejs/events/bridgeCreators.ts index 557852ef994..3d666b21f84 100644 --- a/packages/@romejs/events/bridgeCreators.ts +++ b/packages/@romejs/events/bridgeCreators.ts @@ -18,263 +18,263 @@ const SOCKET_LENGTH = /^(\d+):/; // JSON.stringify but throw on bad data types // Most likely slower... But safer and our data structures are usually fairly shallow function stringify(obj: unknown): string { - return JSON.stringify( - obj, - (key, value) => { - const type = typeof value; - - if (value === undefined || value === null) { - return value; - } - - // Primitives - if (type === 'string' || type === 'number' || type === 'boolean') { - return value; - } - - // Arrays and plain objects - if (Array.isArray(value) || value.constructor === Object) { - return value; - } - - throw new Error( - `Illegal data type not allowed in JSON: ${prettyFormat(value)} in ${prettyFormat( - obj, - )}`, - ); - }, - ); + return JSON.stringify( + obj, + (key, value) => { + const type = typeof value; + + if (value === undefined || value === null) { + return value; + } + + // Primitives + if (type === 'string' || type === 'number' || type === 'boolean') { + return value; + } + + // Arrays and plain objects + if (Array.isArray(value) || value.constructor === Object) { + return value; + } + + throw new Error( + `Illegal data type not allowed in JSON: ${prettyFormat(value)} in ${prettyFormat( + obj, + )}`, + ); + }, + ); } export function createBridgeFromWebSocketInterface( - CustomBridge: Class, - inf: WebSocketInterface, - opts: BridgeCreatorOptions, + CustomBridge: Class, + inf: WebSocketInterface, + opts: BridgeCreatorOptions, ): B { - const bridge = new CustomBridge({ - ...opts, - sendMessage: (data: BridgeMessage) => { - inf.sendJSON(data); - }, - }); - - const {socket} = inf; - - bridge.endEvent.subscribe(() => { - socket.end(); - }); - - inf.completeFrameEvent.subscribe((frame) => { - const json = frame.payload.toString(); - bridge.handleJSONMessage(json); - }); - - socket.on( - 'error', - (err) => { - bridge.endWithError(err); - }, - ); - - socket.on( - 'end', - () => { - bridge.end('RPC WebSocket died'); - }, - ); - - return bridge; + const bridge = new CustomBridge({ + ...opts, + sendMessage: (data: BridgeMessage) => { + inf.sendJSON(data); + }, + }); + + const {socket} = inf; + + bridge.endEvent.subscribe(() => { + socket.end(); + }); + + inf.completeFrameEvent.subscribe((frame) => { + const json = frame.payload.toString(); + bridge.handleJSONMessage(json); + }); + + socket.on( + 'error', + (err) => { + bridge.endWithError(err); + }, + ); + + socket.on( + 'end', + () => { + bridge.end('RPC WebSocket died'); + }, + ); + + return bridge; } export function createBridgeFromBrowserWebSocket( - CustomBridge: Class, - socket: WebSocket, - opts: BridgeCreatorOptions, + CustomBridge: Class, + socket: WebSocket, + opts: BridgeCreatorOptions, ): B { - const bridge = new CustomBridge({ - ...opts, - sendMessage: (data: BridgeMessage) => { - socket.send(stringify(data)); - }, - }); - - bridge.endEvent.subscribe(() => { - socket.close(); - }); - - socket.onmessage = function(event) { - bridge.handleJSONMessage(String(event.data)); - }; - - socket.onclose = () => { - bridge.end('RPC WebSocket disconnected'); - }; - - return bridge; + const bridge = new CustomBridge({ + ...opts, + sendMessage: (data: BridgeMessage) => { + socket.send(stringify(data)); + }, + }); + + bridge.endEvent.subscribe(() => { + socket.close(); + }); + + socket.onmessage = function(event) { + bridge.handleJSONMessage(String(event.data)); + }; + + socket.onclose = () => { + bridge.end('RPC WebSocket disconnected'); + }; + + return bridge; } export function createBridgeFromSocket( - CustomBridge: Class, - socket: Socket, - opts: BridgeCreatorOptions, + CustomBridge: Class, + socket: Socket, + opts: BridgeCreatorOptions, ): B { - const bridge = new CustomBridge({ - ...opts, - sendMessage: (data: BridgeMessage) => { - const serialized = stringify(data); - socket.write(`${serialized.length}:${serialized}`); - }, - }); - - bridge.endEvent.subscribe(() => { - socket.end(); - }); - - // buffer data and parse message on newline - let buff = ''; - let messageLength = 0; - socket.setEncoding('utf8'); - function checkForPossibleMessage() { - // we're awaiting a message and have received it - if (messageLength > 0 && buff.length >= messageLength) { - // retrieve the message from the buffer - const msg = buff.slice(0, messageLength); - - // clear the next message length and remove the current message from the buffer - buff = buff.slice(messageLength); - messageLength = 0; - - // parse it - bridge.handleJSONMessage(msg); - } - - // if we aren't waiting for a message and we have a buffer then check for an incoming message - if (messageLength === 0 && buff !== '') { - // check if we've received the starting info of a message - const possibleLength = buff.match(SOCKET_LENGTH); - if (possibleLength != null) { - // get the message length - messageLength = Number(possibleLength[1]); - - // remove the length designator - buff = buff.slice(possibleLength[0].length); - - // check if we have a full message - checkForPossibleMessage(); - } - } - } - - socket.on( - 'data', - (chunk) => { - buff += chunk; - checkForPossibleMessage(); - }, - ); - - socket.on( - 'error', - (err) => { - bridge.endWithError(err); - }, - ); - - socket.on( - 'end', - () => { - bridge.end('Socket disconnected'); - }, - ); - - return bridge; + const bridge = new CustomBridge({ + ...opts, + sendMessage: (data: BridgeMessage) => { + const serialized = stringify(data); + socket.write(`${serialized.length}:${serialized}`); + }, + }); + + bridge.endEvent.subscribe(() => { + socket.end(); + }); + + // buffer data and parse message on newline + let buff = ''; + let messageLength = 0; + socket.setEncoding('utf8'); + function checkForPossibleMessage() { + // we're awaiting a message and have received it + if (messageLength > 0 && buff.length >= messageLength) { + // retrieve the message from the buffer + const msg = buff.slice(0, messageLength); + + // clear the next message length and remove the current message from the buffer + buff = buff.slice(messageLength); + messageLength = 0; + + // parse it + bridge.handleJSONMessage(msg); + } + + // if we aren't waiting for a message and we have a buffer then check for an incoming message + if (messageLength === 0 && buff !== '') { + // check if we've received the starting info of a message + const possibleLength = buff.match(SOCKET_LENGTH); + if (possibleLength != null) { + // get the message length + messageLength = Number(possibleLength[1]); + + // remove the length designator + buff = buff.slice(possibleLength[0].length); + + // check if we have a full message + checkForPossibleMessage(); + } + } + } + + socket.on( + 'data', + (chunk) => { + buff += chunk; + checkForPossibleMessage(); + }, + ); + + socket.on( + 'error', + (err) => { + bridge.endWithError(err); + }, + ); + + socket.on( + 'end', + () => { + bridge.end('Socket disconnected'); + }, + ); + + return bridge; } export function createBridgeFromLocal( - CustomBridge: Class, - opts: Omit, + CustomBridge: Class, + opts: Omit, ): B { - const bridge = new CustomBridge({ - ...opts, - type: 'server&client', - sendMessage: (msg: BridgeMessage) => { - bridge.handleMessage(msg); - }, - }); - - return bridge; + const bridge = new CustomBridge({ + ...opts, + type: 'server&client', + sendMessage: (msg: BridgeMessage) => { + bridge.handleMessage(msg); + }, + }); + + return bridge; } export function createBridgeFromChildProcess( - CustomBridge: Class, - proc: ChildProcess, - opts: BridgeCreatorOptions, + CustomBridge: Class, + proc: ChildProcess, + opts: BridgeCreatorOptions, ): B { - const bridge = new CustomBridge({ - ...opts, - sendMessage: (data: BridgeMessage) => { - proc.send(data); - }, - }); - - bridge.endEvent.subscribe(() => { - proc.kill(); - }); - - proc.on( - 'error', - (err) => { - bridge.endWithError(err); - }, - ); - - proc.on( - 'message', - (msg) => { - bridge.handleMessage((msg as BridgeMessage)); - }, - ); - - // Catch process dying and reject any requests in flight - proc.on( - 'close', - () => { - bridge.end('RPC child process died'); - }, - ); - - return bridge; + const bridge = new CustomBridge({ + ...opts, + sendMessage: (data: BridgeMessage) => { + proc.send(data); + }, + }); + + bridge.endEvent.subscribe(() => { + proc.kill(); + }); + + proc.on( + 'error', + (err) => { + bridge.endWithError(err); + }, + ); + + proc.on( + 'message', + (msg) => { + bridge.handleMessage((msg as BridgeMessage)); + }, + ); + + // Catch process dying and reject any requests in flight + proc.on( + 'close', + () => { + bridge.end('RPC child process died'); + }, + ); + + return bridge; } export function createBridgeFromParentProcess( - CustomBridge: Class, - opts: BridgeCreatorOptions, + CustomBridge: Class, + opts: BridgeCreatorOptions, ): B { - const bridge = new CustomBridge({ - ...opts, - sendMessage: (data: BridgeMessage) => { - if (typeof process.send === 'function') { - process.send(data); - } else { - throw new Error('No process.send found'); - } - }, - }); - - process.on( - 'message', - (data) => { - bridge.handleMessage(data); - }, - ); - - // I doubt any of these will have time to dispatch but for consistency sake... - process.on( - 'exit', - () => { - bridge.end('RPC self process died'); - }, - ); - - return bridge; + const bridge = new CustomBridge({ + ...opts, + sendMessage: (data: BridgeMessage) => { + if (typeof process.send === 'function') { + process.send(data); + } else { + throw new Error('No process.send found'); + } + }, + }); + + process.on( + 'message', + (data) => { + bridge.handleMessage(data); + }, + ); + + // I doubt any of these will have time to dispatch but for consistency sake... + process.on( + 'exit', + () => { + bridge.end('RPC self process died'); + }, + ); + + return bridge; } diff --git a/packages/@romejs/events/types.ts b/packages/@romejs/events/types.ts index eb70068b1de..ab1723d9af1 100644 --- a/packages/@romejs/events/types.ts +++ b/packages/@romejs/events/types.ts @@ -9,69 +9,69 @@ import {StructuredError} from '@romejs/v8'; import {JSONObject} from '@romejs/codec-json'; export type BridgeCreatorOptions = { - type: BridgeType; - onSendMessage?: (msg: BridgeMessage) => void; + type: BridgeType; + onSendMessage?: (msg: BridgeMessage) => void; }; export type BridgeType = 'server' | 'client' | 'server&client'; export type BridgeOptions = BridgeCreatorOptions & { - sendMessage: (msg: BridgeMessage) => void; + sendMessage: (msg: BridgeMessage) => void; }; export type EventOptions = { - name: string; - onError?: (err: Error) => void; - unique?: boolean; - serial?: boolean; + name: string; + onError?: (err: Error) => void; + unique?: boolean; + serial?: boolean; }; export type EventSubscription = { - unsubscribe: () => void; + unsubscribe: () => void; }; export type BridgeHandshakeMessage = { - type: 'handshake'; - first: boolean; - subscriptions: Array; + type: 'handshake'; + first: boolean; + subscriptions: Array; }; export type BridgeSubscriptionsMessage = { - type: 'subscriptions'; - names: Array; + type: 'subscriptions'; + names: Array; }; export type BridgeRequestMessage = { - id?: number; - event: string; - param: unknown; - type: 'request'; - priority: boolean; + id?: number; + event: string; + param: unknown; + type: 'request'; + priority: boolean; }; export type BridgeSuccessResponseMessage = { - id: number; - event: string; - value: unknown; - type: 'response'; - responseStatus: 'success'; + id: number; + event: string; + value: unknown; + type: 'response'; + responseStatus: 'success'; }; export type BridgeErrorResponseMessage = { - id: number; - event: string; - type: 'response'; - responseStatus: 'error'; - value: StructuredError; - metadata: JSONObject; + id: number; + event: string; + type: 'response'; + responseStatus: 'error'; + value: StructuredError; + metadata: JSONObject; }; export type BridgeResponseMessage = - | BridgeSuccessResponseMessage - | BridgeErrorResponseMessage; + | BridgeSuccessResponseMessage + | BridgeErrorResponseMessage; export type BridgeMessage = - | BridgeHandshakeMessage - | BridgeSubscriptionsMessage - | BridgeRequestMessage - | BridgeResponseMessage; + | BridgeHandshakeMessage + | BridgeSubscriptionsMessage + | BridgeRequestMessage + | BridgeResponseMessage; diff --git a/packages/@romejs/events/utils.ts b/packages/@romejs/events/utils.ts index 0fa36b0f284..56a5cdf8ac1 100644 --- a/packages/@romejs/events/utils.ts +++ b/packages/@romejs/events/utils.ts @@ -8,13 +8,13 @@ import {EventSubscription} from './types'; export function mergeEventSubscriptions( - subs: Array, + subs: Array, ): EventSubscription { - return { - unsubscribe() { - for (const sub of subs) { - sub.unsubscribe(); - } - }, - }; + return { + unsubscribe() { + for (const sub of subs) { + sub.unsubscribe(); + } + }, + }; } diff --git a/packages/@romejs/fs/index.ts b/packages/@romejs/fs/index.ts index c7eb5678751..fe617542fac 100644 --- a/packages/@romejs/fs/index.ts +++ b/packages/@romejs/fs/index.ts @@ -16,211 +16,211 @@ import fs = require('fs'); type DataCallback = (err: null | Error, data: Data) => void; function promisifyData( - path: AbsoluteFilePath, - factory: (path: string, callback: DataCallback) => void, + path: AbsoluteFilePath, + factory: (path: string, callback: DataCallback) => void, ): Promise { - return new Promise((resolve, reject) => { - factory( - path.join(), - (err, data) => { - if (err === null) { - resolve(data); - } else { - reject(err); - } - }, - ); - }); + return new Promise((resolve, reject) => { + factory( + path.join(), + (err, data) => { + if (err === null) { + resolve(data); + } else { + reject(err); + } + }, + ); + }); } type VoidCallback = (err: null | Error) => void; function promisifyVoid( - path: AbsoluteFilePath, - factory: (path: string, callback: VoidCallback) => void, + path: AbsoluteFilePath, + factory: (path: string, callback: VoidCallback) => void, ): Promise { - return new Promise((resolve, reject) => { - factory( - path.join(), - (err) => { - if (err === null) { - resolve(); - } else { - reject(err); - } - }, - ); - }); + return new Promise((resolve, reject) => { + factory( + path.join(), + (err) => { + if (err === null) { + resolve(); + } else { + reject(err); + } + }, + ); + }); } // watch export function watch( - path: AbsoluteFilePath, - options: - | { - encoding?: BufferEncoding | null; - persistent?: boolean; - recursive?: boolean; - } - | undefined, - listener?: (event: string, filename: null | string) => void, + path: AbsoluteFilePath, + options: + | { + encoding?: BufferEncoding | null; + persistent?: boolean; + recursive?: boolean; + } + | undefined, + listener?: (event: string, filename: null | string) => void, ) { - return fs.watch(path.join(), options, listener); + return fs.watch(path.join(), options, listener); } // readFile export function readFile(path: AbsoluteFilePath): Promise { - return promisifyData( - path, - (filename, callback) => fs.readFile(filename, callback), - ); + return promisifyData( + path, + (filename, callback) => fs.readFile(filename, callback), + ); } export function readFileSync(path: AbsoluteFilePath): Buffer { - return fs.readFileSync(path.join()); + return fs.readFileSync(path.join()); } // readFileText export async function readFileText(path: AbsoluteFilePath): Promise { - return (await readFile(path)).toString(); + return (await readFile(path)).toString(); } export function readFileTextSync(path: AbsoluteFilePath): string { - return fs.readFileSync(path.join(), 'utf8'); + return fs.readFileSync(path.join(), 'utf8'); } // writeFile export function writeFile( - path: AbsoluteFilePath, - content: string | Buffer, + path: AbsoluteFilePath, + content: string | Buffer, ): Promise { - return promisifyVoid( - path, - (filename, callback) => fs.writeFile(filename, content, callback), - ); + return promisifyVoid( + path, + (filename, callback) => fs.writeFile(filename, content, callback), + ); } export function writeFileSync( - path: AbsoluteFilePath, - content: Buffer | string, + path: AbsoluteFilePath, + content: Buffer | string, ): void { - return fs.writeFileSync(path.join(), content); + return fs.writeFileSync(path.join(), content); } // readdir function createReaddirReturn( - folder: AbsoluteFilePath, - files: Array, + folder: AbsoluteFilePath, + files: Array, ): AbsoluteFilePathSet { - return new AbsoluteFilePathSet( - files.map((basename) => { - return folder.append(basename); - }), - ); + return new AbsoluteFilePathSet( + files.map((basename) => { + return folder.append(basename); + }), + ); } export function readdir(path: AbsoluteFilePath): Promise { - return new Promise((resolve, reject) => { - fs.readdir( - path.join(), - (err, files) => { - if (err === null) { - resolve(createReaddirReturn(path, files)); - } else { - reject(err); - } - }, - ); - }); + return new Promise((resolve, reject) => { + fs.readdir( + path.join(), + (err, files) => { + if (err === null) { + resolve(createReaddirReturn(path, files)); + } else { + reject(err); + } + }, + ); + }); } export function readdirSync(path: AbsoluteFilePath): AbsoluteFilePathSet { - return createReaddirReturn(path, fs.readdirSync(path.join())); + return createReaddirReturn(path, fs.readdirSync(path.join())); } // lstat export function lstat(path: AbsoluteFilePath): Promise { - return promisifyData( - path, - (filename, callback) => fs.lstat(filename, callback), - ); + return promisifyData( + path, + (filename, callback) => fs.lstat(filename, callback), + ); } export function lstatSync(path: AbsoluteFilePath): fs.Stats { - return fs.lstatSync(path.join()); + return fs.lstatSync(path.join()); } // exists export function exists(path: AbsoluteFilePath): Promise { - return new Promise((resolve) => { - fs.exists( - path.join(), - (exists) => { - resolve(exists); - }, - ); - }); + return new Promise((resolve) => { + fs.exists( + path.join(), + (exists) => { + resolve(exists); + }, + ); + }); } export function existsSync(path: AbsoluteFilePath): boolean { - return fs.existsSync(path.join()); + return fs.existsSync(path.join()); } // unlink export function unlink(path: AbsoluteFilePath): Promise { - return promisifyVoid( - path, - (filename, callback) => - fs.unlink( - filename, - (err) => { - if (err != null && err.code !== 'ENOENT') { - callback(err); - } else { - callback(null); - } - }, - ) - , - ); + return promisifyVoid( + path, + (filename, callback) => + fs.unlink( + filename, + (err) => { + if (err != null && err.code !== 'ENOENT') { + callback(err); + } else { + callback(null); + } + }, + ) + , + ); } export function unlinkSync(path: AbsoluteFilePath): void { - try { - fs.unlinkSync(path.join()); - } catch (err) { - if (err.code !== 'ENOENT') { - throw err; - } - } + try { + fs.unlinkSync(path.join()); + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + } } // createDirectory export function createDirectory( - path: AbsoluteFilePath, - opts: CreateDirectoryOptions = {}, + path: AbsoluteFilePath, + opts: CreateDirectoryOptions = {}, ): Promise { - return promisifyVoid( - path, - (filename, callback) => - fs.mkdir( - filename, - { - recursive: opts.recursive, - }, - callback, - ) - , - ); + return promisifyVoid( + path, + (filename, callback) => + fs.mkdir( + filename, + { + recursive: opts.recursive, + }, + callback, + ) + , + ); } export function createDirectorySync( - path: AbsoluteFilePath, - opts: CreateDirectoryOptions = {}, + path: AbsoluteFilePath, + opts: CreateDirectoryOptions = {}, ): void { - fs.mkdirSync(path.join(), {recursive: opts.recursive}); + fs.mkdirSync(path.join(), {recursive: opts.recursive}); } type CreateDirectoryOptions = { - recursive?: boolean; + recursive?: boolean; }; diff --git a/packages/@romejs/js-analysis/Evaluator.ts b/packages/@romejs/js-analysis/Evaluator.ts index 9e7485a4d62..da63b1c365a 100644 --- a/packages/@romejs/js-analysis/Evaluator.ts +++ b/packages/@romejs/js-analysis/Evaluator.ts @@ -26,287 +26,284 @@ export type HydrateTypeFactory = (id: unknown) => T; export type HydrateData = JSONObject; export type GetModuleSignature = ( - source: string, - relative: string, + source: string, + relative: string, ) => Promise; export class ModuleSignatureManager { - constructor( - graph: ModuleSignature, - getModuleSignature: GetModuleSignature, - topScope: Scope, - ) { - this.topScope = topScope; - this.getModuleSignature = getModuleSignature; - this.graph = graph; - this.openTypes = new Map(); - this.filename = graph.filename; - - this.exportNamesToTypeId = new Map(); - } - - filename: string; - getModuleSignature: GetModuleSignature; - topScope: Scope; - exportNamesToTypeId: Map; - openTypes: Map; - graph: ModuleSignature; - - addAll(manager: ModuleSignatureManager) { - for (const [name, id] of manager.exportNamesToTypeId) { - if (name === 'default') { - // ignore `default` - continue; - } - - this.exportNamesToTypeId.set(name, id); - - const openType = manager.openTypes.get(id); - if (openType === undefined) { - throw new Error('Expected an open type'); - } - this.openTypes.set(id, openType); - } - } - - async init() { - const {graph, openTypes} = this; - - // Create initial open types for all the nodes in this graph - for (const id in graph.types) { - const open = new OpenT(this.topScope, undefined); - openTypes.set(id, open); - } - - let currGetType: undefined | ModuleSignatureType; - - // Create a factory to fetch the open ids - const getType: HydrateTypeFactory = (id: unknown): T => { - if (id === undefined) { - throw new Error('expected id'); - } - - if (typeof id !== 'string') { - throw new Error('expected string id'); - } - - const type = openTypes.get(id); - - if (type === undefined) { - throw new Error( - `${graph.filename}: Expected type of id ${id} but it doesn't exist, serialized data: ${String( - JSON.stringify(currGetType), - )}`, - ); - } - - return type; - }; - - // Fetch the graphs of `export *` dependencies, future calls to `this.getModuleSignature` will fetch from 'cache - await Promise.all( - graph.exports.map((def) => { - if (def.type === 'all') { - return this.getModuleSignature(def.source, graph.filename); - } else { - return undefined; - } - }), - ); - - // Resolve all exports - for (const def of graph.exports) { - if (def.type === 'all') { - const manager = await this.getModuleSignature( - def.source, - graph.filename, - ); - if (manager !== undefined) { - this.addAll(manager); - } - } else { - this.exportNamesToTypeId.set(def.name, def.value); - } - } - - // Hydrate all types in the graph and link them to their open types - for (const id in graph.types) { - const node = graph.types[id]; - const {origin, type, data, human} = node; - currGetType = node; - - // Retrieve the open type - const openT = openTypes.get(id); - if (openT === undefined) { - throw new Error('Expected an open type'); - } - - // Get the type constructor - const TConstructor = types.get(type); - if (TConstructor === undefined) { - throw new Error('Expected a valid internal type constructor name'); - } - - // Create the type - - // @ts-ignore - const realT = TConstructor.hydrate( - this.topScope, - {loc: origin}, - data, - getType, - ); - - // - realT.setHuman(human); - - // Link it to the open type - openT.shouldMatch(realT); - } - } - - link(importedName: string, type: ImportT): void { - const graph = this.graph; - - // Get type id for this export - const maybeExportId = this.exportNamesToTypeId.get(importedName); - if (maybeExportId === undefined) { - // Export not found in the module so let's link it to an error - const error = new UnknownImportE( - this.topScope, - type.originNode, - { - possibleNames: Array.from(this.exportNamesToTypeId.keys()), - importedName, - source: graph.filename, - }, - ); - error.shouldMatch(type); - return; - } - - // Retrieve the open type - const openT = this.openTypes.get(maybeExportId); - if (openT === undefined) { - throw new Error('Expected an open type'); - } - - // Link it to this type - type.setResolvedType(openT); - } + constructor( + graph: ModuleSignature, + getModuleSignature: GetModuleSignature, + topScope: Scope, + ) { + this.topScope = topScope; + this.getModuleSignature = getModuleSignature; + this.graph = graph; + this.openTypes = new Map(); + this.filename = graph.filename; + + this.exportNamesToTypeId = new Map(); + } + + filename: string; + getModuleSignature: GetModuleSignature; + topScope: Scope; + exportNamesToTypeId: Map; + openTypes: Map; + graph: ModuleSignature; + + addAll(manager: ModuleSignatureManager) { + for (const [name, id] of manager.exportNamesToTypeId) { + if (name === 'default') { + // ignore `default` + continue; + } + + this.exportNamesToTypeId.set(name, id); + + const openType = manager.openTypes.get(id); + if (openType === undefined) { + throw new Error('Expected an open type'); + } + this.openTypes.set(id, openType); + } + } + + async init() { + const {graph, openTypes} = this; + + // Create initial open types for all the nodes in this graph + for (const id in graph.types) { + const open = new OpenT(this.topScope, undefined); + openTypes.set(id, open); + } + + let currGetType: undefined | ModuleSignatureType; + + // Create a factory to fetch the open ids + const getType: HydrateTypeFactory = (id: unknown): T => { + if (id === undefined) { + throw new Error('expected id'); + } + + if (typeof id !== 'string') { + throw new Error('expected string id'); + } + + const type = openTypes.get(id); + + if (type === undefined) { + throw new Error( + `${graph.filename}: Expected type of id ${id} but it doesn't exist, serialized data: ${String( + JSON.stringify(currGetType), + )}`, + ); + } + + return type; + }; + + // Fetch the graphs of `export *` dependencies, future calls to `this.getModuleSignature` will fetch from 'cache + await Promise.all( + graph.exports.map((def) => { + if (def.type === 'all') { + return this.getModuleSignature(def.source, graph.filename); + } else { + return undefined; + } + }), + ); + + // Resolve all exports + for (const def of graph.exports) { + if (def.type === 'all') { + const manager = await this.getModuleSignature(def.source, graph.filename); + if (manager !== undefined) { + this.addAll(manager); + } + } else { + this.exportNamesToTypeId.set(def.name, def.value); + } + } + + // Hydrate all types in the graph and link them to their open types + for (const id in graph.types) { + const node = graph.types[id]; + const {origin, type, data, human} = node; + currGetType = node; + + // Retrieve the open type + const openT = openTypes.get(id); + if (openT === undefined) { + throw new Error('Expected an open type'); + } + + // Get the type constructor + const TConstructor = types.get(type); + if (TConstructor === undefined) { + throw new Error('Expected a valid internal type constructor name'); + } + + // Create the type + + // @ts-ignore + const realT = TConstructor.hydrate( + this.topScope, + {loc: origin}, + data, + getType, + ); + + // + realT.setHuman(human); + + // Link it to the open type + openT.shouldMatch(realT); + } + } + + link(importedName: string, type: ImportT): void { + const graph = this.graph; + + // Get type id for this export + const maybeExportId = this.exportNamesToTypeId.get(importedName); + if (maybeExportId === undefined) { + // Export not found in the module so let's link it to an error + const error = new UnknownImportE( + this.topScope, + type.originNode, + { + possibleNames: Array.from(this.exportNamesToTypeId.keys()), + importedName, + source: graph.filename, + }, + ); + error.shouldMatch(type); + return; + } + + // Retrieve the open type + const openT = this.openTypes.get(maybeExportId); + if (openT === undefined) { + throw new Error('Expected an open type'); + } + + // Link it to this type + type.setResolvedType(openT); + } } type Export = - | { - type: 'local'; - name: string; - value: T; - } - | { - type: 'all'; - source: string; - }; + | { + type: 'local'; + name: string; + value: T; + } + | { + type: 'all'; + source: string; + }; export default class Evaluator { - constructor(hub: Hub, filename: string) { - this.filename = filename; - this.nodeToType = new Map(); - this.exports = []; - this.imports = []; - this.hub = hub; - this.graph = hub.graph; - // TODO we should use `ThisScope` and set it correctly to `window` or `undefined` depending on strict mode - this.topScope = new Scope({evaluator: this}); - this.intrinsics = this.topScope.intrinsics = new Intrinsics(this.topScope); - this.evaluatingType = undefined; - } - - evaluatingType: undefined | string; - filename: string; - nodeToType: Map; - hub: Hub; - intrinsics: Intrinsics; - exports: Array; - imports: Array<{ - relative: string; - importedName: undefined | string; - source: string; - type: ImportT; - }>; - topScope: Scope; - graph: Graph; - - initModuleSignature( - graph: ModuleSignature, - getModuleSignature: GetModuleSignature, - ): ModuleSignatureManager { - return new ModuleSignatureManager(graph, getModuleSignature, this.topScope); - } - - seed(ast: AnyNode) { - return this.evaluate(ast, this.topScope); - } - - evaluate(node: undefined | AnyNode, scope: Scope): T { - if (node === undefined) { - throw new Error('Expected node but received undefined'); - } - - const evaluator = evaluators.get(node.type); - if (evaluator === undefined) { - throw new Error(`what is this? ${node.type}`); - } else { - const oldEvaluatingType = this.evaluatingType; - this.evaluatingType = node.type; - let type = evaluator(node, scope, this.hub); - if (type === undefined) { - type = new EmptyT(scope, node); - } - this.evaluatingType = oldEvaluatingType; - this.nodeToType.set(node, type); - return type; - } - } - - getTypeFromEvaluatedNode(node: AnyNode): T { - const type = this.nodeToType.get(node); - if (type === undefined) { - throw new Error( - 'getTypeFromEvaluatedNode() called on a node that has not been validated yet', - ); - } else { - return type; - } - } - - addExport(name: string, type: T) { - this.exports.push({ - type: 'local', - name, - value: type, - }); - } - - addExportAll(source: string) { - this.exports.push({ - type: 'all', - source, - }); - } - - addImport( - t: ImportT, - opts: { - importedName: undefined | string; - source: string; - relative: string; - }, - ) { - this.imports.push({ - relative: opts.relative, - importedName: opts.importedName, - source: opts.source, - type: t, - }); - } + constructor(hub: Hub, filename: string) { + this.filename = filename; + this.nodeToType = new Map(); + this.exports = []; + this.imports = []; + this.hub = hub; + this.graph = hub.graph; + // TODO we should use `ThisScope` and set it correctly to `window` or `undefined` depending on strict mode + this.topScope = new Scope({evaluator: this}); + this.intrinsics = this.topScope.intrinsics = new Intrinsics(this.topScope); + this.evaluatingType = undefined; + } + + evaluatingType: undefined | string; + filename: string; + nodeToType: Map; + hub: Hub; + intrinsics: Intrinsics; + exports: Array; + imports: Array<{ + relative: string; + importedName: undefined | string; + source: string; + type: ImportT; + }>; + topScope: Scope; + graph: Graph; + + initModuleSignature( + graph: ModuleSignature, + getModuleSignature: GetModuleSignature, + ): ModuleSignatureManager { + return new ModuleSignatureManager(graph, getModuleSignature, this.topScope); + } + + seed(ast: AnyNode) { + return this.evaluate(ast, this.topScope); + } + + evaluate(node: undefined | AnyNode, scope: Scope): T { + if (node === undefined) { + throw new Error('Expected node but received undefined'); + } + + const evaluator = evaluators.get(node.type); + if (evaluator === undefined) { + throw new Error(`what is this? ${node.type}`); + } else { + const oldEvaluatingType = this.evaluatingType; + this.evaluatingType = node.type; + let type = evaluator(node, scope, this.hub); + if (type === undefined) { + type = new EmptyT(scope, node); + } + this.evaluatingType = oldEvaluatingType; + this.nodeToType.set(node, type); + return type; + } + } + + getTypeFromEvaluatedNode(node: AnyNode): T { + const type = this.nodeToType.get(node); + if (type === undefined) { + throw new Error( + 'getTypeFromEvaluatedNode() called on a node that has not been validated yet', + ); + } else { + return type; + } + } + + addExport(name: string, type: T) { + this.exports.push({ + type: 'local', + name, + value: type, + }); + } + + addExportAll(source: string) { + this.exports.push({ + type: 'all', + source, + }); + } + + addImport( + t: ImportT, + opts: { + importedName: undefined | string; + source: string; + relative: string; + }, + ) { + this.imports.push({ + relative: opts.relative, + importedName: opts.importedName, + source: opts.source, + type: t, + }); + } } diff --git a/packages/@romejs/js-analysis/Graph.ts b/packages/@romejs/js-analysis/Graph.ts index e8879bfbd08..4159eb1455d 100644 --- a/packages/@romejs/js-analysis/Graph.ts +++ b/packages/@romejs/js-analysis/Graph.ts @@ -6,46 +6,46 @@ */ export type Node = { - value: Value; - lines: Array>; + value: Value; + lines: Array>; }; export default class Graph { - constructor() { - this.nodes = []; - this.nodesByValue = new Map(); - } - - nodes: Array>; - nodesByValue: Map>; - - addNode(value: Value): void { - if (this.find(value)) { - return; - } - - const node: Node = {lines: [], value}; - this.nodesByValue.set(value, node); - this.nodes.push(node); - } - - find(value: Value): undefined | Node { - return this.nodesByValue.get(value); - } - - hasConnections(value: Value) { - const node = this.nodesByValue.get(value); - return node !== undefined && (node?.lines).length > 0; - } - - addLine(startValue: Value, endValue: Value) { - const startNode = this.find(startValue); - const endNode = this.find(endValue); - - if (!startNode || !endNode) { - throw new Error('Both nodes need to exist'); - } - - startNode.lines.push(endNode); - } + constructor() { + this.nodes = []; + this.nodesByValue = new Map(); + } + + nodes: Array>; + nodesByValue: Map>; + + addNode(value: Value): void { + if (this.find(value)) { + return; + } + + const node: Node = {lines: [], value}; + this.nodesByValue.set(value, node); + this.nodes.push(node); + } + + find(value: Value): undefined | Node { + return this.nodesByValue.get(value); + } + + hasConnections(value: Value) { + const node = this.nodesByValue.get(value); + return node !== undefined && (node?.lines).length > 0; + } + + addLine(startValue: Value, endValue: Value) { + const startNode = this.find(startValue); + const endNode = this.find(endValue); + + if (!startNode || !endNode) { + throw new Error('Both nodes need to exist'); + } + + startNode.lines.push(endNode); + } } diff --git a/packages/@romejs/js-analysis/Hub.ts b/packages/@romejs/js-analysis/Hub.ts index 70a8cb1f7c4..6b29f5b1b0d 100644 --- a/packages/@romejs/js-analysis/Hub.ts +++ b/packages/@romejs/js-analysis/Hub.ts @@ -13,71 +13,71 @@ import Evaluator from './Evaluator'; import Utils from './Utils'; const statuses = { - OPEN: 0, - CLOSING: 1, - CLOSED: 2, + OPEN: 0, + CLOSING: 1, + CLOSED: 2, }; type HubStatus = number; export default class Hub { - constructor(ast: Program, project: TransformProjectDefinition) { - this.context = new CompilerContext({ - // TODO - sourceText: '', - ast, - project, - origin: { - category: 'typeChecking', - }, - }); - this.utils = new Utils(this); - this.graph = new Graph(); - this.evaluator = new Evaluator(this, ast.filename); - this.status = statuses.OPEN; - } + constructor(ast: Program, project: TransformProjectDefinition) { + this.context = new CompilerContext({ + // TODO + sourceText: '', + ast, + project, + origin: { + category: 'typeChecking', + }, + }); + this.utils = new Utils(this); + this.graph = new Graph(); + this.evaluator = new Evaluator(this, ast.filename); + this.status = statuses.OPEN; + } - status: HubStatus; - evaluator: Evaluator; - graph: Graph; - context: CompilerContext; - utils: Utils; + status: HubStatus; + evaluator: Evaluator; + graph: Graph; + context: CompilerContext; + utils: Utils; - close() { - this.status = statuses.CLOSING; + close() { + this.status = statuses.CLOSING; - for (const [node] of this.graph.nodesByValue) { - this.utils.reduce(node); - } + for (const [node] of this.graph.nodesByValue) { + this.utils.reduce(node); + } - this.status = statuses.CLOSED; - } + this.status = statuses.CLOSED; + } - isClosing(): boolean { - return this.status === statuses.CLOSING; - } + isClosing(): boolean { + return this.status === statuses.CLOSING; + } - isOpen(): boolean { - return this.isClosing() || this.status === statuses.OPEN; - } + isOpen(): boolean { + return this.isClosing() || this.status === statuses.OPEN; + } - isClosed(): boolean { - return this.isClosing() || this.status === statuses.CLOSED; - } + isClosed(): boolean { + return this.isClosing() || this.status === statuses.CLOSED; + } - assertOpen() { - if (this.isClosed() && this.isClosing() === false) { - throw new Error( - 'This method can only be called when the graph has been open', - ); - } - } + assertOpen() { + if (this.isClosed() && this.isClosing() === false) { + throw new Error( + 'This method can only be called when the graph has been open', + ); + } + } - assertClosed() { - if (this.isOpen() && this.isClosing() === false) { - throw new Error( - 'This method can only be called when the graph has been closed', - ); - } - } + assertClosed() { + if (this.isOpen() && this.isClosing() === false) { + throw new Error( + 'This method can only be called when the graph has been closed', + ); + } + } } diff --git a/packages/@romejs/js-analysis/Intrinsics.ts b/packages/@romejs/js-analysis/Intrinsics.ts index 7052896424b..fc528c60e65 100644 --- a/packages/@romejs/js-analysis/Intrinsics.ts +++ b/packages/@romejs/js-analysis/Intrinsics.ts @@ -10,73 +10,73 @@ import T from './types/T'; import OpenIntrinsicT from './types/OpenIntrinsicT'; export default class Intrinsics { - constructor(scope: Scope) { - this.scope = scope; + constructor(scope: Scope) { + this.scope = scope; - this.intrinsicByName = new Map(); + this.intrinsicByName = new Map(); - this.NumberPrototype = this.createOpenT('NumberPrototype'); - this.Number = this.createOpenT('Number'); + this.NumberPrototype = this.createOpenT('NumberPrototype'); + this.Number = this.createOpenT('Number'); - this.StringPrototype = this.createOpenT('StringPrototype'); - this.String = this.createOpenT('String'); + this.StringPrototype = this.createOpenT('StringPrototype'); + this.String = this.createOpenT('String'); - this.ObjectPrototype = this.createOpenT('ObjectPrototype'); - this.Object = this.createOpenT('Object'); + this.ObjectPrototype = this.createOpenT('ObjectPrototype'); + this.Object = this.createOpenT('Object'); - this.ArrayPrototype = this.createOpenT('ArrayPrototype'); - this.Array = this.createOpenT('Array'); + this.ArrayPrototype = this.createOpenT('ArrayPrototype'); + this.Array = this.createOpenT('Array'); - this.RegExpPrototype = this.createOpenT('RegExpPrototype'); - this.RegExp = this.createOpenT('RegExp'); - } + this.RegExpPrototype = this.createOpenT('RegExpPrototype'); + this.RegExp = this.createOpenT('RegExp'); + } - scope: Scope; - intrinsicByName: Map; + scope: Scope; + intrinsicByName: Map; - String: T; - StringPrototype: T; + String: T; + StringPrototype: T; - Object: T; - ObjectPrototype: T; + Object: T; + ObjectPrototype: T; - Array: T; - ArrayPrototype: T; + Array: T; + ArrayPrototype: T; - RegExp: T; - RegExpPrototype: T; + RegExp: T; + RegExpPrototype: T; - Number: T; - NumberPrototype: T; + Number: T; + NumberPrototype: T; - get(name: string): T { - const t = this.intrinsicByName.get(name); - if (t === undefined) { - throw new Error(`No intrinsic found for ${name}`); - } - return t; - } + get(name: string): T { + const t = this.intrinsicByName.get(name); + if (t === undefined) { + throw new Error(`No intrinsic found for ${name}`); + } + return t; + } - createOpenT(name: string) { - const t = new OpenIntrinsicT(this.scope, undefined, name); - this.intrinsicByName.set(name, t); - return t; - } + createOpenT(name: string) { + const t = new OpenIntrinsicT(this.scope, undefined, name); + this.intrinsicByName.set(name, t); + return t; + } - link() { - this.String.shouldMatch(this.scope.query(['String'])); - this.StringPrototype.shouldMatch(this.scope.query(['String', 'prototype'])); + link() { + this.String.shouldMatch(this.scope.query(['String'])); + this.StringPrototype.shouldMatch(this.scope.query(['String', 'prototype'])); - this.Object.shouldMatch(this.scope.query(['Object'])); - this.ObjectPrototype.shouldMatch(this.scope.query(['Object', 'prototype'])); + this.Object.shouldMatch(this.scope.query(['Object'])); + this.ObjectPrototype.shouldMatch(this.scope.query(['Object', 'prototype'])); - this.Array.shouldMatch(this.scope.query(['Array'])); - this.ArrayPrototype.shouldMatch(this.scope.query(['Array', 'prototype'])); + this.Array.shouldMatch(this.scope.query(['Array'])); + this.ArrayPrototype.shouldMatch(this.scope.query(['Array', 'prototype'])); - this.RegExp.shouldMatch(this.scope.query(['RegExp'])); - this.RegExpPrototype.shouldMatch(this.scope.query(['RegExp', 'prototype'])); + this.RegExp.shouldMatch(this.scope.query(['RegExp'])); + this.RegExpPrototype.shouldMatch(this.scope.query(['RegExp', 'prototype'])); - this.Number.shouldMatch(this.scope.query(['Number'])); - this.NumberPrototype.shouldMatch(this.scope.query(['Number', 'prototype'])); - } + this.Number.shouldMatch(this.scope.query(['Number'])); + this.NumberPrototype.shouldMatch(this.scope.query(['Number', 'prototype'])); + } } diff --git a/packages/@romejs/js-analysis/Utils.ts b/packages/@romejs/js-analysis/Utils.ts index e8b916a6c4b..347cb3f72a9 100644 --- a/packages/@romejs/js-analysis/Utils.ts +++ b/packages/@romejs/js-analysis/Utils.ts @@ -18,277 +18,277 @@ const TYPE_COMPATIBLE: TypeCompatibilityReturn = {type: 'compatible'}; const MAX_DEPTH = 100; export class HumanBuilder { - constructor() { - this.stack = new Set(); - this.usedAliases = new Set(); - this.aliases = new Map(); - } - - stack: Set; - aliases: Map; - usedAliases: Set; - - isRecursive(t: T): boolean { - if (t.human !== undefined) { - return false; - } - - if (this.aliases.has(t)) { - return true; - } - - if (this.stack.has(t)) { - return true; - } - - return false; - } - - humanize(type: T): string { - // Check if we already have a human form for this type - if (type.human !== undefined) { - return type.human; - } - - // Check if we have an already created alias - if (this.aliases.has(type)) { - const alias = this.aliases.get(type); - if (alias === undefined) { - throw new Error('Expected alias'); - } - return alias; - } - - // Generate an alias if we've determined this as recursive - if (this.isRecursive(type)) { - const alias = `Alias${type.id}`; - this.aliases.set(type, alias); - return alias; - } - - // Setup the stack and call - this.stack.add(type); - try { - let humanized = type.humanize(this); - - // Check if an alias was created - const alias = this.aliases.get(type); - if (alias !== undefined) { - humanized = `${alias} = ${humanized}`; - } - return humanized; - } finally { - this.stack.delete(type); - } - } + constructor() { + this.stack = new Set(); + this.usedAliases = new Set(); + this.aliases = new Map(); + } + + stack: Set; + aliases: Map; + usedAliases: Set; + + isRecursive(t: T): boolean { + if (t.human !== undefined) { + return false; + } + + if (this.aliases.has(t)) { + return true; + } + + if (this.stack.has(t)) { + return true; + } + + return false; + } + + humanize(type: T): string { + // Check if we already have a human form for this type + if (type.human !== undefined) { + return type.human; + } + + // Check if we have an already created alias + if (this.aliases.has(type)) { + const alias = this.aliases.get(type); + if (alias === undefined) { + throw new Error('Expected alias'); + } + return alias; + } + + // Generate an alias if we've determined this as recursive + if (this.isRecursive(type)) { + const alias = `Alias${type.id}`; + this.aliases.set(type, alias); + return alias; + } + + // Setup the stack and call + this.stack.add(type); + try { + let humanized = type.humanize(this); + + // Check if an alias was created + const alias = this.aliases.get(type); + if (alias !== undefined) { + humanized = `${alias} = ${humanized}`; + } + return humanized; + } finally { + this.stack.delete(type); + } + } } export default class Utils { - constructor(hub: Hub) { - this.reduceCatchers = new Set(); - this.reduceCache = new Map(); - this.reduceStack = new Set(); - this.compatibilityDepth = 0; - this.hub = hub; - this.debug = false; - } - - compatibilityDepth: number; - reduceCatchers: Set>; - reduceCache: Map; - reduceStack: Set; - debug: boolean; - hub: Hub; - - inspect(t: T, safe: boolean = false): string { - const prevDebug = this.debug; - this.debug = true; - - const data: Map = new Map(); - data.set('id', String(t.id)); - - const {originLoc, originEvaluator} = t; - if (originLoc === undefined) { - data.set('origin', 'unknown'); - } else { - data.set( - 'origin', - `${String(originLoc.filename)}:${String(originLoc.start.line)}:${String( - originLoc.start.column, - )}`, - ); - } - if (originEvaluator !== undefined) { - data.set('evaluator', originEvaluator); - } - - const dataStr = Array.from(data.keys()).map((key) => - `${key}: ${String(data.get(key))}` - ).join(', '); - - let info = `${t.getConstructor().type}<`; - if (safe === false) { - info += `${this.humanize(t)}, `; - } - info += `${dataStr}>`; - - this.debug = prevDebug; - return info; - } - - assertClosed() { - if (this.debug === false) { - this.hub.assertClosed(); - } - } - - explodeUnion(type: T): Array { - return Array.from(new Set(this.reduce(type).explodeUnion())); - } - - isCompatibleWith(a: T, b: T): boolean { - return this.checkCompability(a, b).type === 'compatible'; - } - - checkCompability(a: T, b: T): TypeCompatibilityReturn { - this.assertClosed(); - - const lower = this.reduce(a); - const upper = this.reduce(b); - - // Exact same type - if (lower === upper) { - return TYPE_COMPATIBLE; - } - - // Any types shouldn't cause errors - if (lower instanceof AnyT || upper instanceof AnyT) { - return TYPE_COMPATIBLE; - } - - // Simple check for call stack limits - if (this.compatibilityDepth > MAX_DEPTH) { - throw new Error( - `Max depth exceeded when checking compatibility of ${lower.inspect()} to ${upper.inspect()}`, - ); - } - - const cached = lower.compatibilityCache.get(upper); - if (cached === undefined) { - lower.compatibilityCache.set( - upper, - { - type: 'incompatible', - lower, - upper, - }, - ); - } else { - return cached; - } - - // Check this relationship for compatibility - this.compatibilityDepth++; - let ret; - try { - ret = lower.compatibleWith(upper); - } catch (err) { - if (err instanceof ReduceRecursionError) { - ret = TYPE_COMPATIBLE; - } else { - throw err; - } - } finally { - this.compatibilityDepth--; - } - let res: undefined | TypeCompatibilityReturn; - if (ret === true) { - res = TYPE_COMPATIBLE; - } else if (ret === false) { - res = {type: 'incompatible', lower: a, upper: b}; - } else if (ret instanceof E) { - res = {type: 'incompatible', lower: a, upper: ret}; - } else { - res = ret; - } - - lower.compatibilityCache.set(upper, res); - - return res; - } - - humanize(type: T): string { - this.assertClosed(); - - return new HumanBuilder().humanize(type); - } - - reduce(type: T): T { - // - this.assertClosed(); - - // - const cached = this.reduceCache.get(type); - if (cached !== undefined) { - return cached; - } - - // Check if we're already trying to reduce this node, in that case this is a recursion error - if (this.reduceStack.has(type)) { - //throw new ReduceRecursionError(`Reduce recursion error for ${this.inspect(type, true)}`); - return new UnknownT(type.scope, type.originNode); - } - - // - if (this.reduceStack.size > MAX_DEPTH) { - throw new Error('Max depth exceeded when reducing'); - } - - this.reduceStack.add(type); - - if (this.reduceCatchers.size) { - for (const set of this.reduceCatchers) { - set.add(type); - } - } - - try { - const reduced = type.reduce(); - - if (reduced === undefined) { - throw new Error( - `The reduce() method for ${this.inspect(type, true)} returned null`, - ); - } - - if (reduced.getConstructor().type === 'OpenT') { - throw new Error( - `The reduce() method for ${this.inspect(type, true)} returned an OpenT. This should never be possible. It likely forgot to return utils.reduce() on it.`, - ); - } - - if (this.debug === false) { - this.reduceCache.set(type, reduced); - } - - return reduced; - } finally { - this.reduceStack.delete(type); - } - } - - reduceCatch( - type: T, - ): { - final: T; - involved: Set; - } { - const involved: Set = new Set(); - this.reduceCatchers.add(involved); - - const final: T = this.reduce(type); - this.reduceCatchers.delete(involved); - - return {final, involved}; - } + constructor(hub: Hub) { + this.reduceCatchers = new Set(); + this.reduceCache = new Map(); + this.reduceStack = new Set(); + this.compatibilityDepth = 0; + this.hub = hub; + this.debug = false; + } + + compatibilityDepth: number; + reduceCatchers: Set>; + reduceCache: Map; + reduceStack: Set; + debug: boolean; + hub: Hub; + + inspect(t: T, safe: boolean = false): string { + const prevDebug = this.debug; + this.debug = true; + + const data: Map = new Map(); + data.set('id', String(t.id)); + + const {originLoc, originEvaluator} = t; + if (originLoc === undefined) { + data.set('origin', 'unknown'); + } else { + data.set( + 'origin', + `${String(originLoc.filename)}:${String(originLoc.start.line)}:${String( + originLoc.start.column, + )}`, + ); + } + if (originEvaluator !== undefined) { + data.set('evaluator', originEvaluator); + } + + const dataStr = Array.from(data.keys()).map((key) => + `${key}: ${String(data.get(key))}` + ).join(', '); + + let info = `${t.getConstructor().type}<`; + if (safe === false) { + info += `${this.humanize(t)}, `; + } + info += `${dataStr}>`; + + this.debug = prevDebug; + return info; + } + + assertClosed() { + if (this.debug === false) { + this.hub.assertClosed(); + } + } + + explodeUnion(type: T): Array { + return Array.from(new Set(this.reduce(type).explodeUnion())); + } + + isCompatibleWith(a: T, b: T): boolean { + return this.checkCompability(a, b).type === 'compatible'; + } + + checkCompability(a: T, b: T): TypeCompatibilityReturn { + this.assertClosed(); + + const lower = this.reduce(a); + const upper = this.reduce(b); + + // Exact same type + if (lower === upper) { + return TYPE_COMPATIBLE; + } + + // Any types shouldn't cause errors + if (lower instanceof AnyT || upper instanceof AnyT) { + return TYPE_COMPATIBLE; + } + + // Simple check for call stack limits + if (this.compatibilityDepth > MAX_DEPTH) { + throw new Error( + `Max depth exceeded when checking compatibility of ${lower.inspect()} to ${upper.inspect()}`, + ); + } + + const cached = lower.compatibilityCache.get(upper); + if (cached === undefined) { + lower.compatibilityCache.set( + upper, + { + type: 'incompatible', + lower, + upper, + }, + ); + } else { + return cached; + } + + // Check this relationship for compatibility + this.compatibilityDepth++; + let ret; + try { + ret = lower.compatibleWith(upper); + } catch (err) { + if (err instanceof ReduceRecursionError) { + ret = TYPE_COMPATIBLE; + } else { + throw err; + } + } finally { + this.compatibilityDepth--; + } + let res: undefined | TypeCompatibilityReturn; + if (ret === true) { + res = TYPE_COMPATIBLE; + } else if (ret === false) { + res = {type: 'incompatible', lower: a, upper: b}; + } else if (ret instanceof E) { + res = {type: 'incompatible', lower: a, upper: ret}; + } else { + res = ret; + } + + lower.compatibilityCache.set(upper, res); + + return res; + } + + humanize(type: T): string { + this.assertClosed(); + + return new HumanBuilder().humanize(type); + } + + reduce(type: T): T { + // + this.assertClosed(); + + // + const cached = this.reduceCache.get(type); + if (cached !== undefined) { + return cached; + } + + // Check if we're already trying to reduce this node, in that case this is a recursion error + if (this.reduceStack.has(type)) { + //throw new ReduceRecursionError(`Reduce recursion error for ${this.inspect(type, true)}`); + return new UnknownT(type.scope, type.originNode); + } + + // + if (this.reduceStack.size > MAX_DEPTH) { + throw new Error('Max depth exceeded when reducing'); + } + + this.reduceStack.add(type); + + if (this.reduceCatchers.size) { + for (const set of this.reduceCatchers) { + set.add(type); + } + } + + try { + const reduced = type.reduce(); + + if (reduced === undefined) { + throw new Error( + `The reduce() method for ${this.inspect(type, true)} returned null`, + ); + } + + if (reduced.getConstructor().type === 'OpenT') { + throw new Error( + `The reduce() method for ${this.inspect(type, true)} returned an OpenT. This should never be possible. It likely forgot to return utils.reduce() on it.`, + ); + } + + if (this.debug === false) { + this.reduceCache.set(type, reduced); + } + + return reduced; + } finally { + this.reduceStack.delete(type); + } + } + + reduceCatch( + type: T, + ): { + final: T; + involved: Set; + } { + const involved: Set = new Set(); + this.reduceCatchers.add(involved); + + const final: T = this.reduce(type); + this.reduceCatchers.delete(involved); + + return {final, involved}; + } } diff --git a/packages/@romejs/js-analysis/api/buildGraph.ts b/packages/@romejs/js-analysis/api/buildGraph.ts index 6128320102a..143c937cf73 100644 --- a/packages/@romejs/js-analysis/api/buildGraph.ts +++ b/packages/@romejs/js-analysis/api/buildGraph.ts @@ -12,103 +12,103 @@ import Hub from '../Hub'; import {TransformProjectDefinition} from '@romejs/js-compiler'; export default async function buildGraph( - opts: { - ast: Program; - project: TransformProjectDefinition; - connected: boolean; - provider: CheckProvider; - }, + opts: { + ast: Program; + project: TransformProjectDefinition; + connected: boolean; + provider: CheckProvider; + }, ): Promise { - const {ast, connected, project, provider} = opts; + const {ast, connected, project, provider} = opts; - const hub = new Hub(ast, project); - const {evaluator} = hub; - if (provider.libs !== undefined) { - let body: Array = []; - for (const ast of provider.libs) { - body = [...body, ...ast.body]; - } - evaluator.seed({ - ...ast, - body, - }); - } - evaluator.seed(ast); + const hub = new Hub(ast, project); + const {evaluator} = hub; + if (provider.libs !== undefined) { + let body: Array = []; + for (const ast of provider.libs) { + body = [...body, ...ast.body]; + } + evaluator.seed({ + ...ast, + body, + }); + } + evaluator.seed(ast); - // fetch imports - if (connected) { - // create graphs - const graphs: Map = new Map(); - async function getModuleSignature( - source: string, - relative: string, - ): Promise { - const graphKey = `${relative}:${source}`; - if (graphs.has(graphKey)) { - // already prepared graph - return graphs.get(graphKey); - } + // fetch imports + if (connected) { + // create graphs + const graphs: Map = new Map(); + async function getModuleSignature( + source: string, + relative: string, + ): Promise { + const graphKey = `${relative}:${source}`; + if (graphs.has(graphKey)) { + // already prepared graph + return graphs.get(graphKey); + } - // query the provider for the export types - const graph = await provider.getExportTypes(relative, source); + // query the provider for the export types + const graph = await provider.getExportTypes(relative, source); - // check if the resolved graph even exists - if (graph === undefined) { - // TODO unknown module, create an error - graphs.set(graphKey, undefined); - return undefined; - } + // check if the resolved graph even exists + if (graph === undefined) { + // TODO unknown module, create an error + graphs.set(graphKey, undefined); + return undefined; + } - // check if we've already initialised this graph before, in the case of different relative URLs - if (graphs.has(graph.filename)) { - // TODO this is pretty inefficient, we shouldn't even receive it - const manager = graphs.get(graph.filename); - graphs.set(graphKey, manager); - return manager; - } + // check if we've already initialised this graph before, in the case of different relative URLs + if (graphs.has(graph.filename)) { + // TODO this is pretty inefficient, we shouldn't even receive it + const manager = graphs.get(graph.filename); + graphs.set(graphKey, manager); + return manager; + } - // create the graph - const manager = evaluator.initModuleSignature(graph, getModuleSignature); - graphs.set(graphKey, manager); - graphs.set(graph.filename, manager); - await manager.init(); - return manager; - } + // create the graph + const manager = evaluator.initModuleSignature(graph, getModuleSignature); + graphs.set(graphKey, manager); + graphs.set(graph.filename, manager); + await manager.init(); + return manager; + } - // seed graphs - const seedCache: Set = new Set(); - await Promise.all( - evaluator.imports.map(({source, relative}) => { - const cacheKey = `${source}:${relative}`; - if (seedCache.has(cacheKey)) { - return undefined; - } + // seed graphs + const seedCache: Set = new Set(); + await Promise.all( + evaluator.imports.map(({source, relative}) => { + const cacheKey = `${source}:${relative}`; + if (seedCache.has(cacheKey)) { + return undefined; + } - seedCache.add(cacheKey); - return getModuleSignature(source, relative); - }), - ); + seedCache.add(cacheKey); + return getModuleSignature(source, relative); + }), + ); - // link imports - for (const {source, importedName, relative, type} of evaluator.imports) { - const graphKey = `${relative}:${source}`; - const graph = graphs.get(graphKey); - if (graph === undefined) { - // unknown module, an error would have been created in the initial graph prep - continue; - } + // link imports + for (const {source, importedName, relative, type} of evaluator.imports) { + const graphKey = `${relative}:${source}`; + const graph = graphs.get(graphKey); + if (graph === undefined) { + // unknown module, an error would have been created in the initial graph prep + continue; + } - if (importedName === undefined) { - // nothing to link here! - continue; - } + if (importedName === undefined) { + // nothing to link here! + continue; + } - type.setAbsolute(graph.filename); - graph.link(importedName, type); - } - } + type.setAbsolute(graph.filename); + graph.link(importedName, type); + } + } - evaluator.intrinsics.link(); - hub.close(); - return hub; + evaluator.intrinsics.link(); + hub.close(); + return hub; } diff --git a/packages/@romejs/js-analysis/api/check.ts b/packages/@romejs/js-analysis/api/check.ts index aac8b0be3bf..f519494ee4c 100644 --- a/packages/@romejs/js-analysis/api/check.ts +++ b/packages/@romejs/js-analysis/api/check.ts @@ -16,131 +16,131 @@ import buildGraph from './buildGraph'; import {TransformProjectDefinition} from '@romejs/js-compiler'; export default async function check( - opts: { - ast: Program; - project: TransformProjectDefinition; - provider: CheckProvider; - }, + opts: { + ast: Program; + project: TransformProjectDefinition; + provider: CheckProvider; + }, ): Promise { - const hub = await buildGraph({ - ast: opts.ast, - connected: true, - provider: opts.provider, - project: opts.project, - }); - resolveGraph(hub); - return hub.context.diagnostics.getDiagnostics(); + const hub = await buildGraph({ + ast: opts.ast, + connected: true, + provider: opts.provider, + project: opts.project, + }); + resolveGraph(hub); + return hub.context.diagnostics.getDiagnostics(); } function isError(t: undefined | T): boolean { - return t !== undefined && t instanceof E; + return t !== undefined && t instanceof E; } function resolveGraph(hub: Hub): Diagnostics { - const {graph, utils, context} = hub; - - // we track caught errors here as if a normal type returns a error in it's reduce() method - - // then it will be added to the graph, however we'd have already dealt with it - const caughtErrors: Set = new Set(); - - for (const node of graph.nodes) { - const lower = node.value; - - // unconnected node, we'll resolve these if they've been connected to any nodes - if (lower instanceof OpenT) { - continue; - } - - // see if this reduces to a type error - const reduced = utils.reduce(lower); - if (reduced instanceof E) { - if (caughtErrors.has(reduced)) { - continue; - } else { - caughtErrors.add(reduced); - } - - let {description, lowerTarget, upperTarget} = reduced.getError(); - - // ignore errors inside - if (isError(lowerTarget) || isError(upperTarget)) { - continue; - } - - let advice: DiagnosticAdvice = []; - - if (upperTarget !== undefined) { - const marker = - upperTarget && !(upperTarget instanceof reduced.constructor) - ? utils.humanize(upperTarget) - : undefined; - const {originLoc} = upperTarget; - - if (originLoc !== undefined && marker !== undefined) { - advice.push({ - type: 'log', - category: 'info', - text: marker, - }); - } else if (originLoc !== undefined) { - advice.push({ - type: 'frame', - location: { - filename: originLoc.filename, - start: originLoc.start, - end: originLoc.end, - marker, - }, - }); - } - } - - description = { - ...description, - advice: [...advice, ...description.advice], - }; - - context.addNodeDiagnostic( - lowerTarget.originNode, - description, - { - marker: lowerTarget && !(lowerTarget instanceof reduced.constructor) - ? utils.humanize(lowerTarget) - : undefined, - }, - ); - continue; - } - - // ignore unconnected nodes - if (node.lines.length === 0) { - continue; - } - - for (const line of node.lines) { - const upper = line.value; - const compatibility = utils.checkCompability(upper, lower); - - if (compatibility.type === 'incompatible') { - // ignore associated errors, as they've already been handled - if (isError(compatibility.lower) || isError(compatibility.upper)) { - continue; - } - - context.addNodeDiagnostic( - compatibility.lower.originNode, - descriptions.TYPE_CHECK.INCOMPATIBILITY( - utils.humanize(upper), - upper.originLoc, - ), - { - marker: utils.humanize(compatibility.lower), - }, - ); - } - } - } - - return context.diagnostics.getDiagnostics(); + const {graph, utils, context} = hub; + + // we track caught errors here as if a normal type returns a error in it's reduce() method + + // then it will be added to the graph, however we'd have already dealt with it + const caughtErrors: Set = new Set(); + + for (const node of graph.nodes) { + const lower = node.value; + + // unconnected node, we'll resolve these if they've been connected to any nodes + if (lower instanceof OpenT) { + continue; + } + + // see if this reduces to a type error + const reduced = utils.reduce(lower); + if (reduced instanceof E) { + if (caughtErrors.has(reduced)) { + continue; + } else { + caughtErrors.add(reduced); + } + + let {description, lowerTarget, upperTarget} = reduced.getError(); + + // ignore errors inside + if (isError(lowerTarget) || isError(upperTarget)) { + continue; + } + + let advice: DiagnosticAdvice = []; + + if (upperTarget !== undefined) { + const marker = + upperTarget && !(upperTarget instanceof reduced.constructor) + ? utils.humanize(upperTarget) + : undefined; + const {originLoc} = upperTarget; + + if (originLoc !== undefined && marker !== undefined) { + advice.push({ + type: 'log', + category: 'info', + text: marker, + }); + } else if (originLoc !== undefined) { + advice.push({ + type: 'frame', + location: { + filename: originLoc.filename, + start: originLoc.start, + end: originLoc.end, + marker, + }, + }); + } + } + + description = { + ...description, + advice: [...advice, ...description.advice], + }; + + context.addNodeDiagnostic( + lowerTarget.originNode, + description, + { + marker: lowerTarget && !(lowerTarget instanceof reduced.constructor) + ? utils.humanize(lowerTarget) + : undefined, + }, + ); + continue; + } + + // ignore unconnected nodes + if (node.lines.length === 0) { + continue; + } + + for (const line of node.lines) { + const upper = line.value; + const compatibility = utils.checkCompability(upper, lower); + + if (compatibility.type === 'incompatible') { + // ignore associated errors, as they've already been handled + if (isError(compatibility.lower) || isError(compatibility.upper)) { + continue; + } + + context.addNodeDiagnostic( + compatibility.lower.originNode, + descriptions.TYPE_CHECK.INCOMPATIBILITY( + utils.humanize(upper), + upper.originLoc, + ), + { + marker: utils.humanize(compatibility.lower), + }, + ); + } + } + } + + return context.diagnostics.getDiagnostics(); } diff --git a/packages/@romejs/js-analysis/api/getModuleSignature.ts b/packages/@romejs/js-analysis/api/getModuleSignature.ts index e448f146968..e9f5d06bfaa 100644 --- a/packages/@romejs/js-analysis/api/getModuleSignature.ts +++ b/packages/@romejs/js-analysis/api/getModuleSignature.ts @@ -6,10 +6,10 @@ */ import { - CheckProvider, - ModuleSignature, - ModuleSignatureExport, - ModuleSignatureType, + CheckProvider, + ModuleSignature, + ModuleSignatureExport, + ModuleSignatureType, } from '../types'; import {Program} from '@romejs/js-ast'; import buildGraph from './buildGraph'; @@ -21,94 +21,94 @@ import {Dict} from '@romejs/typescript-helpers'; const exportsCache: WeakMap = new WeakMap(); export default async function getModuleSignature( - opts: { - ast: Program; - project: TransformProjectDefinition; - provider: CheckProvider; - }, + opts: { + ast: Program; + project: TransformProjectDefinition; + provider: CheckProvider; + }, ): Promise { - const {ast, provider} = opts; - const {filename} = ast; + const {ast, provider} = opts; + const {filename} = ast; - if (filename.includes('node_modules')) { - return { - filename, - exports: [], - types: {}, - }; - } + if (filename.includes('node_modules')) { + return { + filename, + exports: [], + types: {}, + }; + } - const cached = exportsCache.get(ast); - if (cached !== undefined) { - return cached; - } + const cached = exportsCache.get(ast); + if (cached !== undefined) { + return cached; + } - const { - evaluator: {exports}, - utils, - } = await buildGraph({ - ast, - project: opts.project, - connected: false, - provider, - }); - const types: Dict = {}; - const exportMap: Array = []; + const { + evaluator: {exports}, + utils, + } = await buildGraph({ + ast, + project: opts.project, + connected: false, + provider, + }); + const types: Dict = {}; + const exportMap: Array = []; - const added: Set = new Set(); + const added: Set = new Set(); - function addType(type: T): string { - const reducedType = utils.reduce(type); - if (added.has(reducedType)) { - return reducedType.id; - } else { - added.add(reducedType); - } + function addType(type: T): string { + const reducedType = utils.reduce(type); + if (added.has(reducedType)) { + return reducedType.id; + } else { + added.add(reducedType); + } - // export errors as any types to suppress errors - if (reducedType instanceof E) { - types[reducedType.id] = { - human: undefined, - origin: reducedType.originLoc, - type: 'AnyT', - data: {}, - }; - return reducedType.id; - } + // export errors as any types to suppress errors + if (reducedType instanceof E) { + types[reducedType.id] = { + human: undefined, + origin: reducedType.originLoc, + type: 'AnyT', + data: {}, + }; + return reducedType.id; + } - const data = reducedType.serialize(addType); + const data = reducedType.serialize(addType); - types[reducedType.id] = { - human: reducedType.human, - origin: reducedType.originLoc, - type: reducedType.getConstructor().type, - data, - }; - return reducedType.id; - } + types[reducedType.id] = { + human: reducedType.human, + origin: reducedType.originLoc, + type: reducedType.getConstructor().type, + data, + }; + return reducedType.id; + } - for (const def of exports) { - if (def.type === 'all') { - exportMap.push({ - type: 'all', - source: def.source, - }); - } else if (def.type === 'local') { - exportMap.push({ - type: 'local', - name: def.name, - value: addType(def.value), - }); - } else { - throw new Error('unknown export def type'); - } - } + for (const def of exports) { + if (def.type === 'all') { + exportMap.push({ + type: 'all', + source: def.source, + }); + } else if (def.type === 'local') { + exportMap.push({ + type: 'local', + name: def.name, + value: addType(def.value), + }); + } else { + throw new Error('unknown export def type'); + } + } - const result: ModuleSignature = { - filename, - exports: exportMap, - types, - }; - exportsCache.set(ast, result); - return result; + const result: ModuleSignature = { + filename, + exports: exportMap, + types, + }; + exportsCache.set(ast, result); + return result; } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/ArrayHole.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/ArrayHole.ts index 87ba5ba1ad4..02785b13fc7 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/ArrayHole.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/ArrayHole.ts @@ -8,6 +8,6 @@ import {AnyNode, ArrayHole, arrayHole} from '@romejs/js-ast'; export default function ArrayHole(node: AnyNode) { - node = arrayHole.assert(node); - throw new Error('unimplemented'); + node = arrayHole.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/CatchClause.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/CatchClause.ts index d6ee12f83e7..be9400f7914 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/CatchClause.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/CatchClause.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, CatchClause, catchClause} from '@romejs/js-ast'; export default function CatchClause(node: AnyNode, scope: Scope) { - node = catchClause.assert(node); - scope; - throw new Error('unimplemented'); + node = catchClause.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/ComputedMemberProperty.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/ComputedMemberProperty.ts index b8a2bdf1adf..d47dea419ab 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/ComputedMemberProperty.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/ComputedMemberProperty.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ComputedMemberProperty, - computedMemberProperty, + AnyNode, + ComputedMemberProperty, + computedMemberProperty, } from '@romejs/js-ast'; export default function ComputedMemberProperty(node: AnyNode, scope: Scope) { - node = computedMemberProperty.assert(node); - scope; - throw new Error('unimplemented'); + node = computedMemberProperty.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/FunctionHead.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/FunctionHead.ts index 53312259dc4..bf4337ee4bc 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/FunctionHead.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/FunctionHead.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, FunctionHead, functionHead} from '@romejs/js-ast'; export default function FunctionHead(node: AnyNode, scope: Scope) { - node = functionHead.assert(node); - scope; - throw new Error('unimplemented'); + node = functionHead.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/Identifier.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/Identifier.ts index c86a5b74eb2..9ba217fdff1 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/Identifier.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/Identifier.ts @@ -12,43 +12,43 @@ import OpenT from '../../types/OpenT'; import AnyT from '../../types/AnyT'; export default function Identifier(node: AnyNode, scope: Scope) { - node = identifier.assert(node); + node = identifier.assert(node); - const binding = scope.getBinding(node.name); - if (binding) { - const type = new OpenT(scope, node); - type.shouldMatch(binding); - return type; - } else { - switch (node.name) { - case 'React$PropType$Primitive': - case 'React$PropType$ArrayOf': - case 'React$PropType$InstanceOf': - case 'React$PropType$ObjectOf': - case 'React$PropType$OneOf': - case 'React$PropType$OneOfType': - case 'React$PropTypePartial': - case 'React$ElementProps': - case 'React$ElementRef': - case '$Exact': - case 'Partial': - case '$Keys': - case 'Object$Assign': - case 'Object$GetPrototypeOf': - case 'Object$SetPrototypeOf': - case '$CharSet': - case 'Class': - case '$Compose': - case '$ComposeReverse': - case '$Subtype': - case 'Function$Prototype$Apply': - case 'Function$Prototype$Bind': - case 'Function$Prototype$Call': - case '$Exports': - return new AnyT(scope, node); + const binding = scope.getBinding(node.name); + if (binding) { + const type = new OpenT(scope, node); + type.shouldMatch(binding); + return type; + } else { + switch (node.name) { + case 'React$PropType$Primitive': + case 'React$PropType$ArrayOf': + case 'React$PropType$InstanceOf': + case 'React$PropType$ObjectOf': + case 'React$PropType$OneOf': + case 'React$PropType$OneOfType': + case 'React$PropTypePartial': + case 'React$ElementProps': + case 'React$ElementRef': + case '$Exact': + case 'Partial': + case '$Keys': + case 'Object$Assign': + case 'Object$GetPrototypeOf': + case 'Object$SetPrototypeOf': + case '$CharSet': + case 'Class': + case '$Compose': + case '$ComposeReverse': + case '$Subtype': + case 'Function$Prototype$Apply': + case 'Function$Prototype$Bind': + case 'Function$Prototype$Call': + case '$Exports': + return new AnyT(scope, node); - default: - return new UndeclaredVarE(scope, node, node.name); - } - } + default: + return new UndeclaredVarE(scope, node, node.name); + } + } } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/SpreadElement.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/SpreadElement.ts index 3564856af4c..e9fb60c923e 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/SpreadElement.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/SpreadElement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, SpreadElement, spreadElement} from '@romejs/js-ast'; export default function SpreadElement(node: AnyNode, scope: Scope) { - node = spreadElement.assert(node); - scope; - throw new Error('unimplemented'); + node = spreadElement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/StaticMemberProperty.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/StaticMemberProperty.ts index 111b81e3da1..bbca9e07174 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/StaticMemberProperty.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/StaticMemberProperty.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - StaticMemberProperty, - staticMemberProperty, + AnyNode, + StaticMemberProperty, + staticMemberProperty, } from '@romejs/js-ast'; export default function StaticMemberProperty(node: AnyNode, scope: Scope) { - node = staticMemberProperty.assert(node); - scope; - throw new Error('unimplemented'); + node = staticMemberProperty.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/SwitchCase.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/SwitchCase.ts index 97b3517a51c..6377edd8cf8 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/SwitchCase.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/SwitchCase.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, SwitchCase, switchCase} from '@romejs/js-ast'; export default function SwitchCase(node: AnyNode, scope: Scope) { - node = switchCase.assert(node); - scope; - throw new Error('unimplemented'); + node = switchCase.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/TemplateElement.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/TemplateElement.ts index 1277653e6d0..e652704e414 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/TemplateElement.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/TemplateElement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TemplateElement, templateElement} from '@romejs/js-ast'; export default function TemplateElement(node: AnyNode, scope: Scope) { - node = templateElement.assert(node); - scope; - throw new Error('unimplemented'); + node = templateElement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/VariableDeclaration.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/VariableDeclaration.ts index 69cdd0b01a0..f71d075bb6f 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/VariableDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/VariableDeclaration.ts @@ -7,36 +7,36 @@ import {Scope} from '../../scopes'; import { - AnyNode, - VariableDeclaration, - variableDeclaration, + AnyNode, + VariableDeclaration, + variableDeclaration, } from '@romejs/js-ast'; import OpenT from '../../types/OpenT'; import VoidT from '../../types/VoidT'; import executeAtom from '../../utils/executeAtom'; export default function VariableDeclaration(node: AnyNode, scope: Scope) { - node = variableDeclaration.assert(node); + node = variableDeclaration.assert(node); - for (const declarator of node.declarations) { - const {id, init} = declarator; - let inferredType; + for (const declarator of node.declarations) { + const {id, init} = declarator; + let inferredType; - if (init === undefined) { - inferredType = new OpenT(scope, declarator); - inferredType.shouldMatch(new VoidT(scope, declarator)); - } else { - inferredType = scope.evaluate(init); - } + if (init === undefined) { + inferredType = new OpenT(scope, declarator); + inferredType.shouldMatch(new VoidT(scope, declarator)); + } else { + inferredType = scope.evaluate(init); + } - let actualType = inferredType; + let actualType = inferredType; - if (id.meta !== undefined && id.meta.typeAnnotation !== undefined) { - const annotatedType = scope.evaluate(id.meta.typeAnnotation); - inferredType.shouldMatch(annotatedType); - actualType = annotatedType; - } + if (id.meta !== undefined && id.meta.typeAnnotation !== undefined) { + const annotatedType = scope.evaluate(id.meta.typeAnnotation); + inferredType.shouldMatch(annotatedType); + actualType = annotatedType; + } - executeAtom(id, actualType, scope); - } + executeAtom(id, actualType, scope); + } } diff --git a/packages/@romejs/js-analysis/evaluators/auxiliary/VariableDeclarator.ts b/packages/@romejs/js-analysis/evaluators/auxiliary/VariableDeclarator.ts index 7ad699f23be..39efa7e8f75 100644 --- a/packages/@romejs/js-analysis/evaluators/auxiliary/VariableDeclarator.ts +++ b/packages/@romejs/js-analysis/evaluators/auxiliary/VariableDeclarator.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, VariableDeclarator, variableDeclarator} from '@romejs/js-ast'; export default function VariableDeclarator(node: AnyNode, scope: Scope) { - node = variableDeclarator.assert(node); - scope; - throw new Error('unimplemented'); + node = variableDeclarator.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/classes/ClassDeclaration.ts b/packages/@romejs/js-analysis/evaluators/classes/ClassDeclaration.ts index bce48e5a8e5..5e4aacf5332 100644 --- a/packages/@romejs/js-analysis/evaluators/classes/ClassDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/classes/ClassDeclaration.ts @@ -10,10 +10,10 @@ import {AnyNode, ClassDeclaration, classDeclaration} from '@romejs/js-ast'; import ClassExpression from './ClassExpression'; export default function ClassDeclaration(node: AnyNode, scope: Scope) { - node = classDeclaration.assert(node); - const type = ClassExpression(node, scope); - if (node.id) { - scope.addBinding(node.id.name, type); - } - return type; + node = classDeclaration.assert(node); + const type = ClassExpression(node, scope); + if (node.id) { + scope.addBinding(node.id.name, type); + } + return type; } diff --git a/packages/@romejs/js-analysis/evaluators/classes/ClassExpression.ts b/packages/@romejs/js-analysis/evaluators/classes/ClassExpression.ts index e3efd891735..a5a8fbfa95b 100644 --- a/packages/@romejs/js-analysis/evaluators/classes/ClassExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/classes/ClassExpression.ts @@ -13,72 +13,72 @@ import T from '../../types/T'; import OpenT from '../../types/OpenT'; export default function ClassExpression(node: AnyNode, scope: Scope) { - node = node.type === 'ClassDeclaration' ? node : classExpression.assert(node); + node = node.type === 'ClassDeclaration' ? node : classExpression.assert(node); - const instances = []; - const statics = []; + const instances = []; + const statics = []; - // - const classInstance = new OpenT(scope, node); - const classId = new OpenT(scope, node); + // + const classInstance = new OpenT(scope, node); + const classId = new OpenT(scope, node); - // - const bodyScope = new ClassScope( - {parentScope: scope}, - { - instance: classInstance, - static: classId, - }, - ); + // + const bodyScope = new ClassScope( + {parentScope: scope}, + { + instance: classInstance, + static: classId, + }, + ); - if (node.id !== undefined) { - bodyScope.addBinding(node.id.name, classId); - } + if (node.id !== undefined) { + bodyScope.addBinding(node.id.name, classId); + } - if (node.meta.typeParameters !== undefined) { - bodyScope.evaluate(node.meta.typeParameters); - } + if (node.meta.typeParameters !== undefined) { + bodyScope.evaluate(node.meta.typeParameters); + } - let _constructor: undefined | T = undefined; + let _constructor: undefined | T = undefined; - for (const bodyNode of node.meta.body) { - const type = bodyScope.evaluate(bodyNode); + for (const bodyNode of node.meta.body) { + const type = bodyScope.evaluate(bodyNode); - if (bodyNode.type === 'ClassMethod' && bodyNode.kind === 'constructor') { - _constructor = type; - } else { - if (bodyNode.type !== 'TSIndexSignature' && bodyNode.meta.static === true) { - statics.push(type); - } else { - instances.push(type); - } - } - } + if (bodyNode.type === 'ClassMethod' && bodyNode.kind === 'constructor') { + _constructor = type; + } else { + if (bodyNode.type !== 'TSIndexSignature' && bodyNode.meta.static === true) { + statics.push(type); + } else { + instances.push(type); + } + } + } - // - const classOrigin = node.id ? node.id : node; - let type = new ClassT( - scope, - classOrigin, - { - _constructor, - instances, - statics, - extends: node.meta.superClass - ? scope.evaluate(node.meta.superClass) - : undefined, - }, - ); - if (node.id) { - type.setHuman(node.id.name); - } + // + const classOrigin = node.id ? node.id : node; + let type = new ClassT( + scope, + classOrigin, + { + _constructor, + instances, + statics, + extends: node.meta.superClass + ? scope.evaluate(node.meta.superClass) + : undefined, + }, + ); + if (node.id) { + type.setHuman(node.id.name); + } - // - classId.shouldMatch(type); + // + classId.shouldMatch(type); - // - const instance = new InstanceT(scope, classOrigin, type, []); - classInstance.shouldMatch(instance); + // + const instance = new InstanceT(scope, classOrigin, type, []); + classInstance.shouldMatch(instance); - return type; + return type; } diff --git a/packages/@romejs/js-analysis/evaluators/classes/ClassHead.ts b/packages/@romejs/js-analysis/evaluators/classes/ClassHead.ts index f94d2307311..5a0411d1139 100644 --- a/packages/@romejs/js-analysis/evaluators/classes/ClassHead.ts +++ b/packages/@romejs/js-analysis/evaluators/classes/ClassHead.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ClassHead, classHead} from '@romejs/js-ast'; export default function ClassHead(node: AnyNode, scope: Scope) { - node = classHead.assert(node); - scope; - throw new Error('unimplemented'); + node = classHead.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/classes/ClassMethod.ts b/packages/@romejs/js-analysis/evaluators/classes/ClassMethod.ts index 3979392e79d..64dd2974c84 100644 --- a/packages/@romejs/js-analysis/evaluators/classes/ClassMethod.ts +++ b/packages/@romejs/js-analysis/evaluators/classes/ClassMethod.ts @@ -11,21 +11,19 @@ import ObjPropT from '../../types/ObjPropT'; import executeFunction from '../../utils/executeFunction'; export default function ClassMethod(node: AnyNode, scope: Scope) { - node = classMethod.assert(node); - if (node.key.type === 'ComputedPropertyKey' === true) { - // TODO - return undefined; - } + node = classMethod.assert(node); + if (node.key.type === 'ComputedPropertyKey' === true) { + // TODO + return undefined; + } - const classScope = scope.find(ClassScope); - const thisContext = - node.meta.static === true - ? classScope.meta.static - : classScope.meta.instance; - const func = executeFunction(node, scope, false, thisContext); + const classScope = scope.find(ClassScope); + const thisContext = + node.meta.static === true ? classScope.meta.static : classScope.meta.instance; + const func = executeFunction(node, scope, false, thisContext); - if (node.key.value.type !== 'Identifier') { - throw new Error('Expected only an identifier key'); - } - return new ObjPropT(scope, node, node.key.value.name, func); + if (node.key.value.type !== 'Identifier') { + throw new Error('Expected only an identifier key'); + } + return new ObjPropT(scope, node, node.key.value.name, func); } diff --git a/packages/@romejs/js-analysis/evaluators/classes/ClassPrivateMethod.ts b/packages/@romejs/js-analysis/evaluators/classes/ClassPrivateMethod.ts index 6747c071669..09e1326356c 100644 --- a/packages/@romejs/js-analysis/evaluators/classes/ClassPrivateMethod.ts +++ b/packages/@romejs/js-analysis/evaluators/classes/ClassPrivateMethod.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ClassPrivateMethod, classPrivateMethod} from '@romejs/js-ast'; export default function ClassPrivateMethod(node: AnyNode, scope: Scope) { - node = classPrivateMethod.assert(node); - scope; - throw new Error('unimplemented'); + node = classPrivateMethod.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/classes/ClassPrivateProperty.ts b/packages/@romejs/js-analysis/evaluators/classes/ClassPrivateProperty.ts index 31f2bbfa8d3..25692eeb792 100644 --- a/packages/@romejs/js-analysis/evaluators/classes/ClassPrivateProperty.ts +++ b/packages/@romejs/js-analysis/evaluators/classes/ClassPrivateProperty.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ClassPrivateProperty, - classPrivateProperty, + AnyNode, + ClassPrivateProperty, + classPrivateProperty, } from '@romejs/js-ast'; export default function ClassPrivateProperty(node: AnyNode, scope: Scope) { - node = classPrivateProperty.assert(node); - scope; - throw new Error('unimplemented'); + node = classPrivateProperty.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/classes/ClassProperty.ts b/packages/@romejs/js-analysis/evaluators/classes/ClassProperty.ts index 7ee8095d29f..826aa96a80b 100644 --- a/packages/@romejs/js-analysis/evaluators/classes/ClassProperty.ts +++ b/packages/@romejs/js-analysis/evaluators/classes/ClassProperty.ts @@ -11,47 +11,44 @@ import AnyT from '../../types/AnyT'; import ObjPropT from '../../types/ObjPropT'; export default function ClassProperty(node: AnyNode, scope: Scope) { - node = classProperty.assert(node); + node = classProperty.assert(node); - if (node.key.type === 'ComputedPropertyKey') { - // TODO - return undefined; - } + if (node.key.type === 'ComputedPropertyKey') { + // TODO + return undefined; + } - const classScope = scope.find(ClassScope); - const funcScope = new ThisScope( - {parentScope: scope}, - classScope.meta.instance, - ); + const classScope = scope.find(ClassScope); + const funcScope = new ThisScope({parentScope: scope}, classScope.meta.instance); - let annotatedType; - let inferredType; + let annotatedType; + let inferredType; - if (node.typeAnnotation) { - annotatedType = funcScope.evaluate(node.typeAnnotation); - } + if (node.typeAnnotation) { + annotatedType = funcScope.evaluate(node.typeAnnotation); + } - if (node.value) { - inferredType = funcScope.evaluate(node.value); + if (node.value) { + inferredType = funcScope.evaluate(node.value); - if (annotatedType !== undefined) { - inferredType.shouldMatch(annotatedType); - } - } + if (annotatedType !== undefined) { + inferredType.shouldMatch(annotatedType); + } + } - if (annotatedType === undefined && inferredType === undefined) { - // TODO what do we do here? - inferredType = new AnyT(scope, node); - } + if (annotatedType === undefined && inferredType === undefined) { + // TODO what do we do here? + inferredType = new AnyT(scope, node); + } - const actualValue = annotatedType === undefined ? inferredType : annotatedType; - if (actualValue === undefined) { - throw new Error('Expected actual value'); - } + const actualValue = annotatedType === undefined ? inferredType : annotatedType; + if (actualValue === undefined) { + throw new Error('Expected actual value'); + } - if (node.key.value.type !== 'Identifier') { - throw new Error('Expected only an identifier key'); - } + if (node.key.value.type !== 'Identifier') { + throw new Error('Expected only an identifier key'); + } - return new ObjPropT(scope, node, node.key.value.name, actualValue); + return new ObjPropT(scope, node, node.key.value.name, actualValue); } diff --git a/packages/@romejs/js-analysis/evaluators/classes/ClassPropertyMeta.ts b/packages/@romejs/js-analysis/evaluators/classes/ClassPropertyMeta.ts index 6e647c6fa6a..fc001fcb6c3 100644 --- a/packages/@romejs/js-analysis/evaluators/classes/ClassPropertyMeta.ts +++ b/packages/@romejs/js-analysis/evaluators/classes/ClassPropertyMeta.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ClassPropertyMeta, classPropertyMeta} from '@romejs/js-ast'; export default function ClassPropertyMeta(node: AnyNode, scope: Scope) { - node = classPropertyMeta.assert(node); - scope; - throw new Error('unimplemented'); + node = classPropertyMeta.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/classes/PrivateName.ts b/packages/@romejs/js-analysis/evaluators/classes/PrivateName.ts index 4ea260dd168..62f21132dc2 100644 --- a/packages/@romejs/js-analysis/evaluators/classes/PrivateName.ts +++ b/packages/@romejs/js-analysis/evaluators/classes/PrivateName.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, PrivateName, privateName} from '@romejs/js-ast'; export default function PrivateName(node: AnyNode, scope: Scope) { - node = privateName.assert(node); - scope; - throw new Error('unimplemented'); + node = privateName.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/core/CommentBlock.ts b/packages/@romejs/js-analysis/evaluators/core/CommentBlock.ts index e6106c1d91a..549d9b319e9 100644 --- a/packages/@romejs/js-analysis/evaluators/core/CommentBlock.ts +++ b/packages/@romejs/js-analysis/evaluators/core/CommentBlock.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, CommentBlock, commentBlock} from '@romejs/js-ast'; export default function CommentBlock(node: AnyNode, scope: Scope) { - node = commentBlock.assert(node); - scope; - throw new Error('unimplemented'); + node = commentBlock.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/core/CommentLine.ts b/packages/@romejs/js-analysis/evaluators/core/CommentLine.ts index 4b3248963d9..035582f056c 100644 --- a/packages/@romejs/js-analysis/evaluators/core/CommentLine.ts +++ b/packages/@romejs/js-analysis/evaluators/core/CommentLine.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, CommentLine, commentLine} from '@romejs/js-ast'; export default function CommentLine(node: AnyNode, scope: Scope) { - node = commentLine.assert(node); - scope; - throw new Error('unimplemented'); + node = commentLine.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/core/Directive.ts b/packages/@romejs/js-analysis/evaluators/core/Directive.ts index 42c51ce3068..4904fbf532d 100644 --- a/packages/@romejs/js-analysis/evaluators/core/Directive.ts +++ b/packages/@romejs/js-analysis/evaluators/core/Directive.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, Directive, directive} from '@romejs/js-ast'; export default function Directive(node: AnyNode, scope: Scope) { - node = directive.assert(node); - scope; - throw new Error('unimplemented'); + node = directive.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/core/InterpreterDirective.ts b/packages/@romejs/js-analysis/evaluators/core/InterpreterDirective.ts index 69b6a9d21c7..9bbede4c277 100644 --- a/packages/@romejs/js-analysis/evaluators/core/InterpreterDirective.ts +++ b/packages/@romejs/js-analysis/evaluators/core/InterpreterDirective.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - InterpreterDirective, - interpreterDirective, + AnyNode, + InterpreterDirective, + interpreterDirective, } from '@romejs/js-ast'; export default function InterpreterDirective(node: AnyNode, scope: Scope) { - node = interpreterDirective.assert(node); - scope; - throw new Error('unimplemented'); + node = interpreterDirective.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/core/Program.ts b/packages/@romejs/js-analysis/evaluators/core/Program.ts index 091e159b266..765329bcc74 100644 --- a/packages/@romejs/js-analysis/evaluators/core/Program.ts +++ b/packages/@romejs/js-analysis/evaluators/core/Program.ts @@ -10,6 +10,6 @@ import {AnyNode, Program, program} from '@romejs/js-ast'; import BlockStatement from '../statements/BlockStatement'; export default function Program(node: AnyNode, scope: Scope) { - node = program.assert(node); - BlockStatement(node, scope); + node = program.assert(node); + BlockStatement(node, scope); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/ArrayExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/ArrayExpression.ts index c417267a527..292690d9bd7 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/ArrayExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/ArrayExpression.ts @@ -11,22 +11,22 @@ import InstanceT from '../../types/InstanceT'; import OpenT from '../../types/OpenT'; export default function ArrayExpression(node: AnyNode, scope: Scope) { - node = arrayExpression.assert(node); - const elems = []; + node = arrayExpression.assert(node); + const elems = []; - for (const expr of node.elements) { - if (expr === undefined) { - // TODO array hole, add undefined here - } else { - elems.push(scope.evaluate(expr)); - } - } + for (const expr of node.elements) { + if (expr === undefined) { + // TODO array hole, add undefined here + } else { + elems.push(scope.evaluate(expr)); + } + } - let value; - if (elems.length === 0) { - value = new OpenT(scope, node); - } else { - value = scope.createUnion(elems, node); - } - return new InstanceT(scope, node, scope.intrinsics.Array, [value]); + let value; + if (elems.length === 0) { + value = new OpenT(scope, node); + } else { + value = scope.createUnion(elems, node); + } + return new InstanceT(scope, node, scope.intrinsics.Array, [value]); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/ArrowFunctionExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/ArrowFunctionExpression.ts index e774fe7facb..cf7cf757941 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/ArrowFunctionExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/ArrowFunctionExpression.ts @@ -7,20 +7,20 @@ import {FunctionScope, Scope} from '../../scopes'; import { - AnyNode, - ArrowFunctionExpression, - arrowFunctionExpression, + AnyNode, + ArrowFunctionExpression, + arrowFunctionExpression, } from '@romejs/js-ast'; import executeFunction from '../../utils/executeFunction'; export default function ArrowFunctionExpression(node: AnyNode, scope: Scope) { - node = arrowFunctionExpression.assert(node); + node = arrowFunctionExpression.assert(node); - let thisContext; - const funcScope = scope.findOptional(FunctionScope); - if (funcScope !== undefined) { - thisContext = funcScope.meta.thisContext; - } + let thisContext; + const funcScope = scope.findOptional(FunctionScope); + if (funcScope !== undefined) { + thisContext = funcScope.meta.thisContext; + } - return executeFunction(node, scope, true, thisContext); + return executeFunction(node, scope, true, thisContext); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/AssignmentExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/AssignmentExpression.ts index 9f05b5ccedc..920a2e25731 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/AssignmentExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/AssignmentExpression.ts @@ -10,17 +10,17 @@ import {AnyNode, assignmentExpression} from '@romejs/js-ast'; import SideEffectT from '../../types/SideEffectT'; export default function AssignmentExpression(node: AnyNode, scope: Scope) { - node = assignmentExpression.assert(node); + node = assignmentExpression.assert(node); - const {left, right, operator} = node; + const {left, right, operator} = node; - if (operator === '=') { - const rightType = scope.evaluate(right); - const leftType = scope.evaluate(left); - leftType.shouldMatch(rightType); - return new SideEffectT(scope, node, rightType); - } else { - // TODO! - return undefined; - } + if (operator === '=') { + const rightType = scope.evaluate(right); + const leftType = scope.evaluate(left); + leftType.shouldMatch(rightType); + return new SideEffectT(scope, node, rightType); + } else { + // TODO! + return undefined; + } } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/AwaitExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/AwaitExpression.ts index 29fcd62ffde..6ccb96727ad 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/AwaitExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/AwaitExpression.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, AwaitExpression, awaitExpression} from '@romejs/js-ast'; export default function AwaitExpression(node: AnyNode, scope: Scope) { - node = awaitExpression.assert(node); - scope; - throw new Error('unimplemented'); + node = awaitExpression.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/BinaryExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/BinaryExpression.ts index c45aef00a70..650fa317810 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/BinaryExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/BinaryExpression.ts @@ -14,77 +14,77 @@ import RefineTypeofT from '../../types/RefineTypeofT'; import BinaryOpT from '../../types/BinaryOpT'; function maybeRefine( - node: AnyNode, - left: AnyNode, - right: AnyNode, - scope: Scope, + node: AnyNode, + left: AnyNode, + right: AnyNode, + scope: Scope, ): boolean { - const evaluator: Evaluator = scope.evaluator; + const evaluator: Evaluator = scope.evaluator; - if (left.type === 'Identifier') { - scope.addBinding(left.name, evaluator.getTypeFromEvaluatedNode(right)); - return true; - } + if (left.type === 'Identifier') { + scope.addBinding(left.name, evaluator.getTypeFromEvaluatedNode(right)); + return true; + } - if ( - left.type === 'UnaryExpression' && - left.operator === 'typeof' && - left.argument.type === 'ReferenceIdentifier' - ) { - const name = left.argument.name; - const binding = scope.getBinding(name); - if (binding !== undefined) { - const type = new RefineTypeofT( - scope, - node, - evaluator.getTypeFromEvaluatedNode(right), - binding, - ); - scope.addBinding(name, type); - return true; - } - } + if ( + left.type === 'UnaryExpression' && + left.operator === 'typeof' && + left.argument.type === 'ReferenceIdentifier' + ) { + const name = left.argument.name; + const binding = scope.getBinding(name); + if (binding !== undefined) { + const type = new RefineTypeofT( + scope, + node, + evaluator.getTypeFromEvaluatedNode(right), + binding, + ); + scope.addBinding(name, type); + return true; + } + } - return false; + return false; } export default function BinaryExpression(node: AnyNode, scope: Scope) { - node = binaryExpression.assert(node); + node = binaryExpression.assert(node); - const left = scope.evaluate(node.left); - const right = scope.evaluate(node.right); + const left = scope.evaluate(node.left); + const right = scope.evaluate(node.right); - // Enforce that the left and right sides of these operators are numbers - switch (node.operator) { - case '<<': - case '>>': - case '>>>': - case '-': - case '*': - case '/': - case '%': - case '**': - case '|': - case '^': - case '&': - case '<': - case '<=': - case '>': - case '>=': { - const num = new NumericT(scope, undefined); - new ExhaustiveT(scope, node, left, num); - new ExhaustiveT(scope, node, right, num); - break; - } - } + // Enforce that the left and right sides of these operators are numbers + switch (node.operator) { + case '<<': + case '>>': + case '>>>': + case '-': + case '*': + case '/': + case '%': + case '**': + case '|': + case '^': + case '&': + case '<': + case '<=': + case '>': + case '>=': { + const num = new NumericT(scope, undefined); + new ExhaustiveT(scope, node, left, num); + new ExhaustiveT(scope, node, right, num); + break; + } + } - // Refinements - let refinedScope = scope; - if (node.operator === '===') { - refinedScope = scope.refine(); - maybeRefine(node, node.left, node.right, refinedScope) || - maybeRefine(node, node.right, node.left, refinedScope); - } + // Refinements + let refinedScope = scope; + if (node.operator === '===') { + refinedScope = scope.refine(); + maybeRefine(node, node.left, node.right, refinedScope) || + maybeRefine(node, node.right, node.left, refinedScope); + } - return new BinaryOpT(refinedScope, node, left, node.operator, right); + return new BinaryOpT(refinedScope, node, left, node.operator, right); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/CallExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/CallExpression.ts index fd210c88824..49cd6e480b4 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/CallExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/CallExpression.ts @@ -10,14 +10,14 @@ import {AnyNode, CallExpression, callExpression} from '@romejs/js-ast'; import CallT from '../../types/CallT'; export default function CallExpression(node: AnyNode, scope: Scope) { - node = callExpression.assert(node); + node = callExpression.assert(node); - return new CallT( - scope, - node, - scope.evaluate(node.callee), - node.arguments.map((arg) => { - return scope.evaluate(arg); - }), - ); + return new CallT( + scope, + node, + scope.evaluate(node.callee), + node.arguments.map((arg) => { + return scope.evaluate(arg); + }), + ); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/ConditionalExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/ConditionalExpression.ts index 27f6445eeb5..1044eb77ded 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/ConditionalExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/ConditionalExpression.ts @@ -10,5 +10,5 @@ import {AnyNode} from '@romejs/js-ast'; import IfStatement from '../statements/IfStatement'; export default function ConditionalExpression(node: AnyNode, scope: Scope) { - return IfStatement(node, scope); + return IfStatement(node, scope); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/DoExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/DoExpression.ts index 2084c784732..722b6f02a36 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/DoExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/DoExpression.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, DoExpression, doExpression} from '@romejs/js-ast'; export default function DoExpression(node: AnyNode, scope: Scope) { - node = doExpression.assert(node); - scope; - throw new Error('unimplemented'); + node = doExpression.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/FunctionExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/FunctionExpression.ts index 2f1d9155024..3cfab03b42e 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/FunctionExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/FunctionExpression.ts @@ -10,6 +10,6 @@ import {AnyNode, FunctionExpression, functionExpression} from '@romejs/js-ast'; import executeFunction from '../../utils/executeFunction'; export default function FunctionExpression(node: AnyNode, scope: Scope) { - node = functionExpression.assert(node); - return executeFunction(node, scope, true); + node = functionExpression.assert(node); + return executeFunction(node, scope, true); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/LogicalExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/LogicalExpression.ts index 78c5b21c960..42efe239529 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/LogicalExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/LogicalExpression.ts @@ -11,47 +11,47 @@ import T from '../../types/T'; import UnionT from '../../types/UnionT'; function uniq(args: Array): Array { - return [...new Set(args)]; + return [...new Set(args)]; } export default function LogicalExpression(node: AnyNode, scope: Scope) { - node = logicalExpression.assert(node); - - switch (node.operator) { - case '||': { - const left = scope.refine().evaluate(node.left); - const right = scope.refine().evaluate(node.right); - - // create a new scope that has unions of all the refined bindings - const refinedScope = scope.refine(); - const refinedNames = uniq([ - ...left.scope.getOwnBindingNames(), - ...right.scope.getOwnBindingNames(), - ]); - const mergeScopes = [left.scope, right.scope]; - for (const name of refinedNames) { - const rawTypes: Set = new Set(); - for (const scope of mergeScopes) { - const binding = scope.getBinding(name); - if (binding !== undefined) { - rawTypes.add(binding); - } - } - - const types = Array.from(rawTypes); - refinedScope.addBinding(name, refinedScope.createUnion(types)); - } - - return new UnionT(refinedScope, node, [left, right]); - } - - case '&&': { - const left = scope.evaluate(node.left); - const right = left.scope.evaluate(node.right); - return new UnionT(right.scope, node, [left, right]); - } - - default: - throw new Error('Unknown operator'); - } + node = logicalExpression.assert(node); + + switch (node.operator) { + case '||': { + const left = scope.refine().evaluate(node.left); + const right = scope.refine().evaluate(node.right); + + // create a new scope that has unions of all the refined bindings + const refinedScope = scope.refine(); + const refinedNames = uniq([ + ...left.scope.getOwnBindingNames(), + ...right.scope.getOwnBindingNames(), + ]); + const mergeScopes = [left.scope, right.scope]; + for (const name of refinedNames) { + const rawTypes: Set = new Set(); + for (const scope of mergeScopes) { + const binding = scope.getBinding(name); + if (binding !== undefined) { + rawTypes.add(binding); + } + } + + const types = Array.from(rawTypes); + refinedScope.addBinding(name, refinedScope.createUnion(types)); + } + + return new UnionT(refinedScope, node, [left, right]); + } + + case '&&': { + const left = scope.evaluate(node.left); + const right = left.scope.evaluate(node.right); + return new UnionT(right.scope, node, [left, right]); + } + + default: + throw new Error('Unknown operator'); + } } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/MemberExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/MemberExpression.ts index 09a1e20e22d..48d27b4c5d3 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/MemberExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/MemberExpression.ts @@ -11,19 +11,19 @@ import StringLiteralT from '../../types/StringLiteralT'; import GetPropT from '../../types/GetPropT'; export default function MemberExpression(node: AnyNode, scope: Scope) { - node = memberExpression.assert(node); - if (node.property.type === 'ComputedMemberProperty') { - throw new Error('Computed properties not supportd yet'); - } + node = memberExpression.assert(node); + if (node.property.type === 'ComputedMemberProperty') { + throw new Error('Computed properties not supportd yet'); + } - if (node.property.value.type === 'PrivateName') { - throw new Error('PrivateName in static member not supported yet'); - } + if (node.property.value.type === 'PrivateName') { + throw new Error('PrivateName in static member not supported yet'); + } - const prop = new StringLiteralT( - scope, - node.property.value, - node.property.value.name, - ); - return new GetPropT(scope, node, scope.evaluate(node.object), prop); + const prop = new StringLiteralT( + scope, + node.property.value, + node.property.value.name, + ); + return new GetPropT(scope, node, scope.evaluate(node.object), prop); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/MetaProperty.ts b/packages/@romejs/js-analysis/evaluators/expressions/MetaProperty.ts index 729d2c30d9b..5f80372b3c5 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/MetaProperty.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/MetaProperty.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, MetaProperty, metaProperty} from '@romejs/js-ast'; export default function MetaProperty(node: AnyNode, scope: Scope) { - node = metaProperty.assert(node); - scope; - throw new Error('unimplemented'); + node = metaProperty.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/NewExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/NewExpression.ts index 978afb0161f..d9f65163cac 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/NewExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/NewExpression.ts @@ -10,6 +10,6 @@ import {AnyNode, NewExpression, newExpression} from '@romejs/js-ast'; import InstanceT from '../../types/InstanceT'; export default function NewExpression(node: AnyNode, scope: Scope) { - node = newExpression.assert(node); - return new InstanceT(scope, node, scope.evaluate(node.callee), []); + node = newExpression.assert(node); + return new InstanceT(scope, node, scope.evaluate(node.callee), []); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/OptionalCallExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/OptionalCallExpression.ts index 72488ea44a0..747c2beec07 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/OptionalCallExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/OptionalCallExpression.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - OptionalCallExpression, - optionalCallExpression, + AnyNode, + OptionalCallExpression, + optionalCallExpression, } from '@romejs/js-ast'; export default function OptionalCallExpression(node: AnyNode, scope: Scope) { - node = optionalCallExpression.assert(node); - scope; - throw new Error('unimplemented'); + node = optionalCallExpression.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/ReferenceIdentifier.ts b/packages/@romejs/js-analysis/evaluators/expressions/ReferenceIdentifier.ts index 4738a6a5e58..47605055afa 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/ReferenceIdentifier.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/ReferenceIdentifier.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ReferenceIdentifier, - referenceIdentifier, + AnyNode, + ReferenceIdentifier, + referenceIdentifier, } from '@romejs/js-ast'; export default function ReferenceIdentifier(node: AnyNode, scope: Scope) { - node = referenceIdentifier.assert(node); - scope; - throw new Error('unimplemented'); + node = referenceIdentifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/SequenceExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/SequenceExpression.ts index 788b398d962..04a63f98ed0 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/SequenceExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/SequenceExpression.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, SequenceExpression, sequenceExpression} from '@romejs/js-ast'; export default function SequenceExpression(node: AnyNode, scope: Scope) { - node = sequenceExpression.assert(node); - scope; - throw new Error('unimplemented'); + node = sequenceExpression.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/Super.ts b/packages/@romejs/js-analysis/evaluators/expressions/Super.ts index 0a62a63c5df..1dc857cef7b 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/Super.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/Super.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, Super, _super} from '@romejs/js-ast'; export default function Super(node: AnyNode, scope: Scope) { - node = _super.assert(node); - scope; - throw new Error('unimplemented'); + node = _super.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/TaggedTemplateExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/TaggedTemplateExpression.ts index 32f176cd116..c461948eb62 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/TaggedTemplateExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/TaggedTemplateExpression.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - TaggedTemplateExpression, - taggedTemplateExpression, + AnyNode, + TaggedTemplateExpression, + taggedTemplateExpression, } from '@romejs/js-ast'; export default function TaggedTemplateExpression(node: AnyNode, scope: Scope) { - node = taggedTemplateExpression.assert(node); - scope; - throw new Error('unimplemented'); + node = taggedTemplateExpression.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/ThisExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/ThisExpression.ts index 4fa662d2f38..d534fe02488 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/ThisExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/ThisExpression.ts @@ -10,14 +10,14 @@ import {AnyNode, thisExpression} from '@romejs/js-ast'; import OpenT from '../../types/OpenT'; export default function ThisExpression(node: AnyNode, scope: Scope) { - node = thisExpression.assert(node); - const thisScope = scope.find(ThisScope); - if (thisScope === undefined) { - // TODO complain - return undefined; - } else { - const type = new OpenT(scope, node); - type.shouldMatch(thisScope.context); - return type; - } + node = thisExpression.assert(node); + const thisScope = scope.find(ThisScope); + if (thisScope === undefined) { + // TODO complain + return undefined; + } else { + const type = new OpenT(scope, node); + type.shouldMatch(thisScope.context); + return type; + } } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/UnaryExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/UnaryExpression.ts index 8db13f091b0..6594525239e 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/UnaryExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/UnaryExpression.ts @@ -13,33 +13,33 @@ import VoidT from '../../types/VoidT'; import TypeofT from '../../types/TypeofT'; export default function UnaryExpression(node: AnyNode, scope: Scope) { - node = unaryExpression.assert(node); - const argType = scope.evaluate(node.argument); - - switch (node.operator) { - case // booleans - 'delete': - case '!': - return new BooleanT(scope, node); - - // numbers - case '+': - case '-': - case '~': - return new NumericT(scope, node); - - // strings - case 'typeof': - return new TypeofT(scope, node, argType); - - // void - case 'void': - return new VoidT(scope, node); - - // empty! - case 'throw': - break; - } - - return undefined; + node = unaryExpression.assert(node); + const argType = scope.evaluate(node.argument); + + switch (node.operator) { + case // booleans + 'delete': + case '!': + return new BooleanT(scope, node); + + // numbers + case '+': + case '-': + case '~': + return new NumericT(scope, node); + + // strings + case 'typeof': + return new TypeofT(scope, node, argType); + + // void + case 'void': + return new VoidT(scope, node); + + // empty! + case 'throw': + break; + } + + return undefined; } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/UpdateExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/UpdateExpression.ts index edf21aab77a..b5618298b36 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/UpdateExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/UpdateExpression.ts @@ -11,8 +11,8 @@ import NumericT from '../../types/NumericT'; import ExhaustiveT from '../../types/ExhaustiveT'; export default function UpdateExpression(node: AnyNode, scope: Scope) { - node = updateExpression.assert(node); - const type = new NumericT(scope, node); - new ExhaustiveT(scope, node.argument, scope.evaluate(node.argument), type); - return type; + node = updateExpression.assert(node); + const type = new NumericT(scope, node); + new ExhaustiveT(scope, node.argument, scope.evaluate(node.argument), type); + return type; } diff --git a/packages/@romejs/js-analysis/evaluators/expressions/YieldExpression.ts b/packages/@romejs/js-analysis/evaluators/expressions/YieldExpression.ts index 04ae8fd0740..664259b903b 100644 --- a/packages/@romejs/js-analysis/evaluators/expressions/YieldExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/expressions/YieldExpression.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, YieldExpression, yieldExpression} from '@romejs/js-ast'; export default function YieldExpression(node: AnyNode, scope: Scope) { - node = yieldExpression.assert(node); - scope; - throw new Error('unimplemented'); + node = yieldExpression.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/index.ts b/packages/@romejs/js-analysis/evaluators/index.ts index 9bd0b85ffbe..19582c91716 100644 --- a/packages/@romejs/js-analysis/evaluators/index.ts +++ b/packages/@romejs/js-analysis/evaluators/index.ts @@ -11,8 +11,8 @@ import {AnyNode} from '@romejs/js-ast'; import T from '../types/T'; const evaluators: Map< - string, - (node: AnyNode, scope: Scope, hub: Hub) => void | undefined | T + string, + (node: AnyNode, scope: Scope, hub: Hub) => void | undefined | T > = new Map(); export default evaluators; @@ -20,8 +20,8 @@ export default evaluators; import AmbiguousFlowTypeCastExpression from './temp/AmbiguousFlowTypeCastExpression'; evaluators.set( - 'AmbiguousFlowTypeCastExpression', - AmbiguousFlowTypeCastExpression, + 'AmbiguousFlowTypeCastExpression', + AmbiguousFlowTypeCastExpression, ); import AnyKeywordTypeAnnotation from './types/AnyKeywordTypeAnnotation'; @@ -53,8 +53,8 @@ evaluators.set('AssignmentObjectPattern', AssignmentObjectPattern); import AssignmentObjectPatternProperty from './patterns/AssignmentObjectPatternProperty'; evaluators.set( - 'AssignmentObjectPatternProperty', - AssignmentObjectPatternProperty, + 'AssignmentObjectPatternProperty', + AssignmentObjectPatternProperty, ); import AwaitExpression from './expressions/AwaitExpression'; @@ -497,8 +497,8 @@ evaluators.set('TSConstructorType', TSConstructorType); import TSConstructSignatureDeclaration from './typescript/TSConstructSignatureDeclaration'; evaluators.set( - 'TSConstructSignatureDeclaration', - TSConstructSignatureDeclaration, + 'TSConstructSignatureDeclaration', + TSConstructSignatureDeclaration, ); import TSDeclareFunction from './typescript/TSDeclareFunction'; diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXAttribute.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXAttribute.ts index 9618b29e475..2c438244df6 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXAttribute.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXAttribute.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, JSXAttribute, jsxAttribute} from '@romejs/js-ast'; export default function JSXAttribute(node: AnyNode, scope: Scope) { - node = jsxAttribute.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxAttribute.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXElement.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXElement.ts index e6705e58eb6..a06d8a798cd 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXElement.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXElement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, JSXElement, jsxElement} from '@romejs/js-ast'; export default function JSXElement(node: AnyNode, scope: Scope) { - node = jsxElement.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxElement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXEmptyExpression.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXEmptyExpression.ts index 66b7eb17d0f..95a0624dda1 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXEmptyExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXEmptyExpression.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, JSXEmptyExpression, jsxEmptyExpression} from '@romejs/js-ast'; export default function JSXEmptyExpression(node: AnyNode, scope: Scope) { - node = jsxEmptyExpression.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxEmptyExpression.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXExpressionContainer.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXExpressionContainer.ts index 409143751fc..82447bd1c5d 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXExpressionContainer.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXExpressionContainer.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - JSXExpressionContainer, - jsxExpressionContainer, + AnyNode, + JSXExpressionContainer, + jsxExpressionContainer, } from '@romejs/js-ast'; export default function JSXExpressionContainer(node: AnyNode, scope: Scope) { - node = jsxExpressionContainer.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxExpressionContainer.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXFragment.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXFragment.ts index ff7c682071d..45a69e18967 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXFragment.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXFragment.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, JSXFragment, jsxFragment} from '@romejs/js-ast'; export default function JSXFragment(node: AnyNode, scope: Scope) { - node = jsxFragment.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxFragment.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXIdentifier.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXIdentifier.ts index be657195ffe..f105e74d685 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXIdentifier.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXIdentifier.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, JSXIdentifier, jsxIdentifier} from '@romejs/js-ast'; export default function JSXIdentifier(node: AnyNode, scope: Scope) { - node = jsxIdentifier.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxIdentifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXMemberExpression.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXMemberExpression.ts index 53bf76306b0..ece02beb882 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXMemberExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXMemberExpression.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - JSXMemberExpression, - jsxMemberExpression, + AnyNode, + JSXMemberExpression, + jsxMemberExpression, } from '@romejs/js-ast'; export default function JSXMemberExpression(node: AnyNode, scope: Scope) { - node = jsxMemberExpression.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxMemberExpression.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXNamespacedName.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXNamespacedName.ts index 96d285491b1..8c0ece3caee 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXNamespacedName.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXNamespacedName.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, JSXNamespacedName, jsxNamespacedName} from '@romejs/js-ast'; export default function JSXNamespacedName(node: AnyNode, scope: Scope) { - node = jsxNamespacedName.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxNamespacedName.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXReferenceIdentifier.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXReferenceIdentifier.ts index 05fbff7f726..e31d2e48c11 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXReferenceIdentifier.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXReferenceIdentifier.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - JSXReferenceIdentifier, - jsxReferenceIdentifier, + AnyNode, + JSXReferenceIdentifier, + jsxReferenceIdentifier, } from '@romejs/js-ast'; export default function JSXReferenceIdentifier(node: AnyNode, scope: Scope) { - node = jsxReferenceIdentifier.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxReferenceIdentifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXSpreadAttribute.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXSpreadAttribute.ts index a561c5bbd7e..2bf95a4397a 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXSpreadAttribute.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXSpreadAttribute.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, JSXSpreadAttribute, jsxSpreadAttribute} from '@romejs/js-ast'; export default function JSXSpreadAttribute(node: AnyNode, scope: Scope) { - node = jsxSpreadAttribute.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxSpreadAttribute.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXSpreadChild.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXSpreadChild.ts index 87c00efb0db..11ed183dc7d 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXSpreadChild.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXSpreadChild.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, JSXSpreadChild, jsxSpreadChild} from '@romejs/js-ast'; export default function JSXSpreadChild(node: AnyNode, scope: Scope) { - node = jsxSpreadChild.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxSpreadChild.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/jsx/JSXText.ts b/packages/@romejs/js-analysis/evaluators/jsx/JSXText.ts index 30029ce867d..bd3a45f99c4 100644 --- a/packages/@romejs/js-analysis/evaluators/jsx/JSXText.ts +++ b/packages/@romejs/js-analysis/evaluators/jsx/JSXText.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, JSXText, jsxText} from '@romejs/js-ast'; export default function JSXText(node: AnyNode, scope: Scope) { - node = jsxText.assert(node); - scope; - throw new Error('unimplemented'); + node = jsxText.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/literals/BigIntLiteral.ts b/packages/@romejs/js-analysis/evaluators/literals/BigIntLiteral.ts index 536b8435841..907e79064e2 100644 --- a/packages/@romejs/js-analysis/evaluators/literals/BigIntLiteral.ts +++ b/packages/@romejs/js-analysis/evaluators/literals/BigIntLiteral.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, BigIntLiteral, bigIntLiteral} from '@romejs/js-ast'; export default function BigIntLiteral(node: AnyNode, scope: Scope) { - node = bigIntLiteral.assert(node); - scope; - throw new Error('unimplemented'); + node = bigIntLiteral.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/literals/BooleanLiteral.ts b/packages/@romejs/js-analysis/evaluators/literals/BooleanLiteral.ts index 360730f4c9b..7547557c75f 100644 --- a/packages/@romejs/js-analysis/evaluators/literals/BooleanLiteral.ts +++ b/packages/@romejs/js-analysis/evaluators/literals/BooleanLiteral.ts @@ -10,6 +10,6 @@ import {AnyNode, BooleanLiteral, booleanLiteral} from '@romejs/js-ast'; import BooleanLiteralT from '../../types/BooleanLiteralT'; export default function BooleanLiteral(node: AnyNode, scope: Scope) { - node = booleanLiteral.assert(node); - return new BooleanLiteralT(scope, node, node.value); + node = booleanLiteral.assert(node); + return new BooleanLiteralT(scope, node, node.value); } diff --git a/packages/@romejs/js-analysis/evaluators/literals/NullLiteral.ts b/packages/@romejs/js-analysis/evaluators/literals/NullLiteral.ts index 4701ed1df50..59c0b3ab817 100644 --- a/packages/@romejs/js-analysis/evaluators/literals/NullLiteral.ts +++ b/packages/@romejs/js-analysis/evaluators/literals/NullLiteral.ts @@ -10,6 +10,6 @@ import {AnyNode, NullLiteral, nullLiteral} from '@romejs/js-ast'; import NullT from '../../types/NullT'; export default function NullLiteral(node: AnyNode, scope: Scope) { - node = node = nullLiteral.assert(node); - return new NullT(scope, node); + node = node = nullLiteral.assert(node); + return new NullT(scope, node); } diff --git a/packages/@romejs/js-analysis/evaluators/literals/NumericLiteral.ts b/packages/@romejs/js-analysis/evaluators/literals/NumericLiteral.ts index f7eea5d8ef7..d8364c48a97 100644 --- a/packages/@romejs/js-analysis/evaluators/literals/NumericLiteral.ts +++ b/packages/@romejs/js-analysis/evaluators/literals/NumericLiteral.ts @@ -10,6 +10,6 @@ import {AnyNode, NumericLiteral, numericLiteral} from '@romejs/js-ast'; import NumericLiteralT from '../../types/NumericLiteralT'; export default function NumericLiteral(node: AnyNode, scope: Scope) { - node = numericLiteral.assert(node); - return new NumericLiteralT(scope, node, node.value); + node = numericLiteral.assert(node); + return new NumericLiteralT(scope, node, node.value); } diff --git a/packages/@romejs/js-analysis/evaluators/literals/RegExpLiteral.ts b/packages/@romejs/js-analysis/evaluators/literals/RegExpLiteral.ts index b804192f606..f63670b423c 100644 --- a/packages/@romejs/js-analysis/evaluators/literals/RegExpLiteral.ts +++ b/packages/@romejs/js-analysis/evaluators/literals/RegExpLiteral.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, RegExpLiteral, regExpLiteral} from '@romejs/js-ast'; export default function RegExpLiteral(node: AnyNode, scope: Scope) { - node = regExpLiteral.assert(node); - scope; - throw new Error('unimplemented'); + node = regExpLiteral.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/literals/StringLiteral.ts b/packages/@romejs/js-analysis/evaluators/literals/StringLiteral.ts index 8e80f4c4577..79e50e398aa 100644 --- a/packages/@romejs/js-analysis/evaluators/literals/StringLiteral.ts +++ b/packages/@romejs/js-analysis/evaluators/literals/StringLiteral.ts @@ -10,6 +10,6 @@ import {AnyNode, StringLiteral, stringLiteral} from '@romejs/js-ast'; import StringLiteralT from '../../types/StringLiteralT'; export default function StringLiteral(node: AnyNode, scope: Scope) { - node = stringLiteral.assert(node); - return new StringLiteralT(scope, node, node.value); + node = stringLiteral.assert(node); + return new StringLiteralT(scope, node, node.value); } diff --git a/packages/@romejs/js-analysis/evaluators/literals/TemplateLiteral.ts b/packages/@romejs/js-analysis/evaluators/literals/TemplateLiteral.ts index 44bc717100b..07bdf00683a 100644 --- a/packages/@romejs/js-analysis/evaluators/literals/TemplateLiteral.ts +++ b/packages/@romejs/js-analysis/evaluators/literals/TemplateLiteral.ts @@ -11,14 +11,14 @@ import ExhaustiveT from '../../types/ExhaustiveT'; import StringT from '../../types/StringT'; export default function TemplateLiteral(node: AnyNode, scope: Scope) { - node = templateLiteral.assert(node); - for (const expr of node.expressions) { - new ExhaustiveT( - scope, - expr, - scope.evaluate(expr), - new StringT(scope, undefined), - ); - } - return new StringT(scope, node); + node = templateLiteral.assert(node); + for (const expr of node.expressions) { + new ExhaustiveT( + scope, + expr, + scope.evaluate(expr), + new StringT(scope, undefined), + ); + } + return new StringT(scope, node); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ExportAllDeclaration.ts b/packages/@romejs/js-analysis/evaluators/modules/ExportAllDeclaration.ts index 2248632ee6d..49036a57547 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ExportAllDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ExportAllDeclaration.ts @@ -7,17 +7,17 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ExportAllDeclaration, - exportAllDeclaration, + AnyNode, + ExportAllDeclaration, + exportAllDeclaration, } from '@romejs/js-ast'; import Hub from '../../Hub'; export default function ExportAllDeclaration( - node: AnyNode, - scope: Scope, - {evaluator}: Hub, + node: AnyNode, + scope: Scope, + {evaluator}: Hub, ) { - node = exportAllDeclaration.assert(node); - evaluator.addExportAll(node.source.value); + node = exportAllDeclaration.assert(node); + evaluator.addExportAll(node.source.value); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ExportDefaultDeclaration.ts b/packages/@romejs/js-analysis/evaluators/modules/ExportDefaultDeclaration.ts index f8545425362..1733ceb3d2d 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ExportDefaultDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ExportDefaultDeclaration.ts @@ -7,21 +7,21 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ExportDefaultDeclaration, - exportDefaultDeclaration, + AnyNode, + ExportDefaultDeclaration, + exportDefaultDeclaration, } from '@romejs/js-ast'; import Hub from '../../Hub'; export default function ExportDefaultDeclaration( - node: AnyNode, - scope: Scope, - {evaluator}: Hub, + node: AnyNode, + scope: Scope, + {evaluator}: Hub, ) { - node = exportDefaultDeclaration.assert(node); + node = exportDefaultDeclaration.assert(node); - const decl = node.declaration; - const declType = scope.evaluate(decl); - evaluator.addExport('default', declType); - return declType; + const decl = node.declaration; + const declType = scope.evaluate(decl); + evaluator.addExport('default', declType); + return declType; } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ExportDefaultSpecifier.ts b/packages/@romejs/js-analysis/evaluators/modules/ExportDefaultSpecifier.ts index 063f8a80e61..22c4ec32a77 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ExportDefaultSpecifier.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ExportDefaultSpecifier.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ExportDefaultSpecifier, - exportDefaultSpecifier, + AnyNode, + ExportDefaultSpecifier, + exportDefaultSpecifier, } from '@romejs/js-ast'; export default function ExportDefaultSpecifier(node: AnyNode, scope: Scope) { - node = exportDefaultSpecifier.assert(node); - scope; - throw new Error('unimplemented'); + node = exportDefaultSpecifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ExportExternalDeclaration.ts b/packages/@romejs/js-analysis/evaluators/modules/ExportExternalDeclaration.ts index 0fffb112474..ea31494e53f 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ExportExternalDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ExportExternalDeclaration.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - ExportExternalDeclaration, - exportExternalDeclaration, + AnyNode, + ExportExternalDeclaration, + exportExternalDeclaration, } from '@romejs/js-ast'; export default function ExportExternalDeclaration(node: AnyNode) { - node = exportExternalDeclaration.assert(node); - throw new Error('unimplemented'); + node = exportExternalDeclaration.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ExportExternalSpecifier.ts b/packages/@romejs/js-analysis/evaluators/modules/ExportExternalSpecifier.ts index 042cbd0d483..0efd830fc3e 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ExportExternalSpecifier.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ExportExternalSpecifier.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - ExportExternalSpecifier, - exportExternalSpecifier, + AnyNode, + ExportExternalSpecifier, + exportExternalSpecifier, } from '@romejs/js-ast'; export default function ExportExternalSpecifier(node: AnyNode) { - node = exportExternalSpecifier.assert(node); - throw new Error('unimplemented'); + node = exportExternalSpecifier.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ExportLocalDeclaration.ts b/packages/@romejs/js-analysis/evaluators/modules/ExportLocalDeclaration.ts index 5e3a2cfa36d..06cc409b2af 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ExportLocalDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ExportLocalDeclaration.ts @@ -12,82 +12,82 @@ import ImportT from '../../types/ImportT'; import Hub from '../../Hub'; export default function ExportLocalDeclaration( - node: AnyNode, - scope: Scope, - {evaluator}: Hub, + node: AnyNode, + scope: Scope, + {evaluator}: Hub, ) { - node = exportLocalDeclaration.assert(node); + node = exportLocalDeclaration.assert(node); - // export const foo = 'bar'; + // export const foo = 'bar'; - // export default function foo() {} - const decl = node.declaration; - if (decl !== undefined) { - const declType = scope.evaluate(decl); + // export default function foo() {} + const decl = node.declaration; + if (decl !== undefined) { + const declType = scope.evaluate(decl); - switch (decl.type) { - case 'FunctionDeclaration': - case 'ClassDeclaration': { - const id = decl.id; - if (id === undefined) { - throw new Error(`Expected id`); - } - evaluator.addExport(id.name, declType); - break; - } + switch (decl.type) { + case 'FunctionDeclaration': + case 'ClassDeclaration': { + const id = decl.id; + if (id === undefined) { + throw new Error(`Expected id`); + } + evaluator.addExport(id.name, declType); + break; + } - case 'VariableDeclarationStatement': { - for (const id of getBindingIdentifiers(decl)) { - const type = scope.getBinding(id.name); - if (type === undefined) { - throw new Error(`Couldn't find binding type for ${id.name}`); - } - evaluator.addExport(id.name, type); - } - break; - } + case 'VariableDeclarationStatement': { + for (const id of getBindingIdentifiers(decl)) { + const type = scope.getBinding(id.name); + if (type === undefined) { + throw new Error(`Couldn't find binding type for ${id.name}`); + } + evaluator.addExport(id.name, type); + } + break; + } - case 'TypeAliasTypeAnnotation': { - const type = scope.getBinding(decl.id.name); - if (type === undefined) { - throw new Error(`Couldn't find binding type for ${decl.id.name}`); - } - evaluator.addExport(decl.id.name, type); - break; - } - } + case 'TypeAliasTypeAnnotation': { + const type = scope.getBinding(decl.id.name); + if (type === undefined) { + throw new Error(`Couldn't find binding type for ${decl.id.name}`); + } + evaluator.addExport(decl.id.name, type); + break; + } + } - return declType; - } + return declType; + } - // export {foo, bar}; + // export {foo, bar}; - // export {foo, bar} from './foo'; - const source = undefined; // TODO node.source === undefined ? undefined : node.source.value; - const {specifiers} = node; - if (specifiers !== undefined) { - for (const specifier of specifiers) { - if ( - specifier.type === 'ExportLocalSpecifier' || - specifier.type === 'ExportExternalSpecifier' - ) { - let type; - if (source === undefined) { - type = scope.evaluate(specifier.local); - } else { - type = new ImportT( - scope, - node, - { - importedName: specifier.local.name, - source, - }, - ); - } - evaluator.addExport(specifier.exported.name, type); - } - } - } + // export {foo, bar} from './foo'; + const source = undefined; // TODO node.source === undefined ? undefined : node.source.value; + const {specifiers} = node; + if (specifiers !== undefined) { + for (const specifier of specifiers) { + if ( + specifier.type === 'ExportLocalSpecifier' || + specifier.type === 'ExportExternalSpecifier' + ) { + let type; + if (source === undefined) { + type = scope.evaluate(specifier.local); + } else { + type = new ImportT( + scope, + node, + { + importedName: specifier.local.name, + source, + }, + ); + } + evaluator.addExport(specifier.exported.name, type); + } + } + } - return undefined; + return undefined; } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ExportLocalSpecifier.ts b/packages/@romejs/js-analysis/evaluators/modules/ExportLocalSpecifier.ts index 481b2c396c6..7c9190d02ec 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ExportLocalSpecifier.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ExportLocalSpecifier.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ExportLocalSpecifier, - exportLocalSpecifier, + AnyNode, + ExportLocalSpecifier, + exportLocalSpecifier, } from '@romejs/js-ast'; export default function ExportLocalSpecifier(node: AnyNode, scope: Scope) { - node = exportLocalSpecifier.assert(node); - scope; - throw new Error('unimplemented'); + node = exportLocalSpecifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ExportNamespaceSpecifier.ts b/packages/@romejs/js-analysis/evaluators/modules/ExportNamespaceSpecifier.ts index 4e85a136ab0..91fe1881478 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ExportNamespaceSpecifier.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ExportNamespaceSpecifier.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ExportNamespaceSpecifier, - exportNamespaceSpecifier, + AnyNode, + ExportNamespaceSpecifier, + exportNamespaceSpecifier, } from '@romejs/js-ast'; export default function ExportNamespaceSpecifier(node: AnyNode, scope: Scope) { - node = exportNamespaceSpecifier.assert(node); - scope; - throw new Error('unimplemented'); + node = exportNamespaceSpecifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ImportCall.ts b/packages/@romejs/js-analysis/evaluators/modules/ImportCall.ts index 38bb02ff3d6..8528f46da92 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ImportCall.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ImportCall.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ImportCall, importCall} from '@romejs/js-ast'; export default function ImportCall(node: AnyNode, scope: Scope) { - node = importCall.assert(node); - scope; - throw new Error('unimplemented'); + node = importCall.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ImportDeclaration.ts b/packages/@romejs/js-analysis/evaluators/modules/ImportDeclaration.ts index 41bdea9a8d5..415b25c8f9e 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ImportDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ImportDeclaration.ts @@ -11,48 +11,48 @@ import ImportT from '../../types/ImportT'; import {getImportSpecifiers} from '@romejs/js-ast-utils'; export default function ImportDeclaration(node: AnyNode, scope: Scope) { - node = importDeclaration.assert(node); + node = importDeclaration.assert(node); - const source = node.source.value; + const source = node.source.value; - for (const specifier of getImportSpecifiers(node)) { - if (specifier.type === 'ImportSpecifier') { - const localName = specifier.local.name.name; - const importedName = specifier.imported.name; + for (const specifier of getImportSpecifiers(node)) { + if (specifier.type === 'ImportSpecifier') { + const localName = specifier.local.name.name; + const importedName = specifier.imported.name; - const open = new ImportT( - scope, - specifier, - { - importedName, - source, - }, - ); - scope.addBinding(localName, open); - } else if (specifier.type === 'ImportDefaultSpecifier') { - const localName = specifier.local.name.name; - const open = new ImportT( - scope, - specifier, - { - importedName: 'default', - source, - }, - ); - scope.addBinding(localName, open); - } else if (specifier.type === 'ImportNamespaceSpecifier') { - const localName = specifier.local.name.name; - const open = new ImportT( - scope, - specifier, - { - importedName: undefined, - source, - }, - ); - scope.addBinding(localName, open); - } else { - // TODO error - } - } + const open = new ImportT( + scope, + specifier, + { + importedName, + source, + }, + ); + scope.addBinding(localName, open); + } else if (specifier.type === 'ImportDefaultSpecifier') { + const localName = specifier.local.name.name; + const open = new ImportT( + scope, + specifier, + { + importedName: 'default', + source, + }, + ); + scope.addBinding(localName, open); + } else if (specifier.type === 'ImportNamespaceSpecifier') { + const localName = specifier.local.name.name; + const open = new ImportT( + scope, + specifier, + { + importedName: undefined, + source, + }, + ); + scope.addBinding(localName, open); + } else { + // TODO error + } + } } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ImportDefaultSpecifier.ts b/packages/@romejs/js-analysis/evaluators/modules/ImportDefaultSpecifier.ts index bcebc10d38c..b77c8d8957c 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ImportDefaultSpecifier.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ImportDefaultSpecifier.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ImportDefaultSpecifier, - importDefaultSpecifier, + AnyNode, + ImportDefaultSpecifier, + importDefaultSpecifier, } from '@romejs/js-ast'; export default function ImportDefaultSpecifier(node: AnyNode, scope: Scope) { - node = importDefaultSpecifier.assert(node); - scope; - throw new Error('unimplemented'); + node = importDefaultSpecifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ImportNamespaceSpecifier.ts b/packages/@romejs/js-analysis/evaluators/modules/ImportNamespaceSpecifier.ts index 3acfd68dfa9..88e2fb0a8d9 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ImportNamespaceSpecifier.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ImportNamespaceSpecifier.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ImportNamespaceSpecifier, - importNamespaceSpecifier, + AnyNode, + ImportNamespaceSpecifier, + importNamespaceSpecifier, } from '@romejs/js-ast'; export default function ImportNamespaceSpecifier(node: AnyNode, scope: Scope) { - node = importNamespaceSpecifier.assert(node); - scope; - throw new Error('unimplemented'); + node = importNamespaceSpecifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ImportSpecifier.ts b/packages/@romejs/js-analysis/evaluators/modules/ImportSpecifier.ts index 0a01d7d46d2..f403b01ce00 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ImportSpecifier.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ImportSpecifier.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ImportSpecifier, importSpecifier} from '@romejs/js-ast'; export default function ImportSpecifier(node: AnyNode, scope: Scope) { - node = importSpecifier.assert(node); - scope; - throw new Error('unimplemented'); + node = importSpecifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/modules/ImportSpecifierLocal.ts b/packages/@romejs/js-analysis/evaluators/modules/ImportSpecifierLocal.ts index 24e4b57a4df..dc36352b8c8 100644 --- a/packages/@romejs/js-analysis/evaluators/modules/ImportSpecifierLocal.ts +++ b/packages/@romejs/js-analysis/evaluators/modules/ImportSpecifierLocal.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ImportSpecifierLocal, - importSpecifierLocal, + AnyNode, + ImportSpecifierLocal, + importSpecifierLocal, } from '@romejs/js-ast'; export default function ImportSpecifierLocal(node: AnyNode, scope: Scope) { - node = importSpecifierLocal.assert(node); - scope; - throw new Error('unimplemented'); + node = importSpecifierLocal.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/objects/ComputedPropertyKey.ts b/packages/@romejs/js-analysis/evaluators/objects/ComputedPropertyKey.ts index db51b7e5cf6..10e17d85432 100644 --- a/packages/@romejs/js-analysis/evaluators/objects/ComputedPropertyKey.ts +++ b/packages/@romejs/js-analysis/evaluators/objects/ComputedPropertyKey.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ComputedPropertyKey, - computedPropertyKey, + AnyNode, + ComputedPropertyKey, + computedPropertyKey, } from '@romejs/js-ast'; export default function ComputedPropertyKey(node: AnyNode, scope: Scope) { - node = computedPropertyKey.assert(node); - scope; - throw new Error('unimplemented'); + node = computedPropertyKey.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/objects/ObjectExpression.ts b/packages/@romejs/js-analysis/evaluators/objects/ObjectExpression.ts index 1adcca89efb..dcb6bd3726a 100644 --- a/packages/@romejs/js-analysis/evaluators/objects/ObjectExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/objects/ObjectExpression.ts @@ -11,47 +11,47 @@ import ObjPropT from '../../types/ObjPropT'; import ObjT from '../../types/ObjT'; export default function ObjectExpression(node: AnyNode, scope: Scope) { - node = objectExpression.assert(node); - const props = []; + node = objectExpression.assert(node); + const props = []; - for (const prop of node.properties) { - if (prop.type === 'SpreadProperty') { - // TODO - } else if (prop.type === 'ObjectProperty') { - if (prop.key.type === 'ComputedPropertyKey') { - // TODO - } else { - const { - key: {value: key}, - value, - } = prop; + for (const prop of node.properties) { + if (prop.type === 'SpreadProperty') { + // TODO + } else if (prop.type === 'ObjectProperty') { + if (prop.key.type === 'ComputedPropertyKey') { + // TODO + } else { + const { + key: {value: key}, + value, + } = prop; - let keyStr; - if (key.type === 'Identifier') { - keyStr = key.name; - } else { - // TODO - continue; - } + let keyStr; + if (key.type === 'Identifier') { + keyStr = key.name; + } else { + // TODO + continue; + } - if (keyStr === undefined) { - throw new Error('Expected keyStr'); - } + if (keyStr === undefined) { + throw new Error('Expected keyStr'); + } - props.push(new ObjPropT(scope, prop, keyStr, scope.evaluate(value))); - } - } else { - // TODO - } - } + props.push(new ObjPropT(scope, prop, keyStr, scope.evaluate(value))); + } + } else { + // TODO + } + } - return new ObjT( - scope, - node, - { - calls: [], - props, - proto: scope.intrinsics.ObjectPrototype, - }, - ); + return new ObjT( + scope, + node, + { + calls: [], + props, + proto: scope.intrinsics.ObjectPrototype, + }, + ); } diff --git a/packages/@romejs/js-analysis/evaluators/objects/ObjectMethod.ts b/packages/@romejs/js-analysis/evaluators/objects/ObjectMethod.ts index 8920173ecb3..dd7e23c4b32 100644 --- a/packages/@romejs/js-analysis/evaluators/objects/ObjectMethod.ts +++ b/packages/@romejs/js-analysis/evaluators/objects/ObjectMethod.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ObjectMethod, objectMethod} from '@romejs/js-ast'; export default function ObjectMethod(node: AnyNode, scope: Scope) { - node = objectMethod.assert(node); - scope; - throw new Error('unimplemented'); + node = objectMethod.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/objects/ObjectProperty.ts b/packages/@romejs/js-analysis/evaluators/objects/ObjectProperty.ts index 1527b373f7a..c3186306126 100644 --- a/packages/@romejs/js-analysis/evaluators/objects/ObjectProperty.ts +++ b/packages/@romejs/js-analysis/evaluators/objects/ObjectProperty.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ObjectProperty, objectProperty} from '@romejs/js-ast'; export default function ObjectProperty(node: AnyNode, scope: Scope) { - node = objectProperty.assert(node); - scope; - throw new Error('unimplemented'); + node = objectProperty.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/objects/SpreadProperty.ts b/packages/@romejs/js-analysis/evaluators/objects/SpreadProperty.ts index a9bb1ec81a6..6a34da22943 100644 --- a/packages/@romejs/js-analysis/evaluators/objects/SpreadProperty.ts +++ b/packages/@romejs/js-analysis/evaluators/objects/SpreadProperty.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, SpreadProperty, spreadProperty} from '@romejs/js-ast'; export default function SpreadProperty(node: AnyNode, scope: Scope) { - node = spreadProperty.assert(node); - scope; - throw new Error('unimplemented'); + node = spreadProperty.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/objects/StaticPropertyKey.ts b/packages/@romejs/js-analysis/evaluators/objects/StaticPropertyKey.ts index a608896d791..cfbe59f6d7d 100644 --- a/packages/@romejs/js-analysis/evaluators/objects/StaticPropertyKey.ts +++ b/packages/@romejs/js-analysis/evaluators/objects/StaticPropertyKey.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, StaticPropertyKey, staticPropertyKey} from '@romejs/js-ast'; export default function StaticPropertyKey(node: AnyNode, scope: Scope) { - node = staticPropertyKey.assert(node); - scope; - throw new Error('unimplemented'); + node = staticPropertyKey.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/AssignmentArrayPattern.ts b/packages/@romejs/js-analysis/evaluators/patterns/AssignmentArrayPattern.ts index c684c208bad..2eaedfc0bc1 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/AssignmentArrayPattern.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/AssignmentArrayPattern.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - AssignmentArrayPattern, - assignmentArrayPattern, + AnyNode, + AssignmentArrayPattern, + assignmentArrayPattern, } from '@romejs/js-ast'; export default function AssignmentArrayPattern(node: AnyNode, scope: Scope) { - node = assignmentArrayPattern.assert(node); - scope; - throw new Error('unimplemented'); + node = assignmentArrayPattern.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/AssignmentAssignmentPattern.ts b/packages/@romejs/js-analysis/evaluators/patterns/AssignmentAssignmentPattern.ts index 52eee68fdbe..b811c7562d4 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/AssignmentAssignmentPattern.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/AssignmentAssignmentPattern.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - AssignmentAssignmentPattern, - assignmentAssignmentPattern, + AnyNode, + AssignmentAssignmentPattern, + assignmentAssignmentPattern, } from '@romejs/js-ast'; export default function AssignmentAssignmentPattern(node: AnyNode) { - node = assignmentAssignmentPattern.assert(node); - throw new Error('unimplemented'); + node = assignmentAssignmentPattern.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/AssignmentIdentifier.ts b/packages/@romejs/js-analysis/evaluators/patterns/AssignmentIdentifier.ts index 29606162cbb..56715dec203 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/AssignmentIdentifier.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/AssignmentIdentifier.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, assignmentIdentifier} from '@romejs/js-ast'; export default function AssignmentIdentifier(node: AnyNode, scope: Scope) { - node = assignmentIdentifier.assert(node); - scope; - throw new Error('unimplemented'); + node = assignmentIdentifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/AssignmentObjectPattern.ts b/packages/@romejs/js-analysis/evaluators/patterns/AssignmentObjectPattern.ts index 640da148aa5..28e444d5882 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/AssignmentObjectPattern.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/AssignmentObjectPattern.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - AssignmentObjectPattern, - assignmentObjectPattern, + AnyNode, + AssignmentObjectPattern, + assignmentObjectPattern, } from '@romejs/js-ast'; export default function AssignmentObjectPattern(node: AnyNode, scope: Scope) { - node = assignmentObjectPattern.assert(node); - scope; - throw new Error('unimplemented'); + node = assignmentObjectPattern.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/AssignmentObjectPatternProperty.ts b/packages/@romejs/js-analysis/evaluators/patterns/AssignmentObjectPatternProperty.ts index 98d0cdeee30..51aa6b90d6c 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/AssignmentObjectPatternProperty.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/AssignmentObjectPatternProperty.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - AssignmentObjectPatternProperty, - assignmentObjectPatternProperty, + AnyNode, + AssignmentObjectPatternProperty, + assignmentObjectPatternProperty, } from '@romejs/js-ast'; export default function AssignmentObjectPatternProperty(node: AnyNode) { - node = assignmentObjectPatternProperty.assert(node); - throw new Error('unimplemented'); + node = assignmentObjectPatternProperty.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/BindingArrayPattern.ts b/packages/@romejs/js-analysis/evaluators/patterns/BindingArrayPattern.ts index 98980bb99c2..77e4d81fb60 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/BindingArrayPattern.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/BindingArrayPattern.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - BindingArrayPattern, - bindingArrayPattern, + AnyNode, + BindingArrayPattern, + bindingArrayPattern, } from '@romejs/js-ast'; export default function BindingArrayPattern(node: AnyNode, scope: Scope) { - node = bindingArrayPattern.assert(node); - scope; - throw new Error('unimplemented'); + node = bindingArrayPattern.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/BindingAssignmentPattern.ts b/packages/@romejs/js-analysis/evaluators/patterns/BindingAssignmentPattern.ts index c84c8e52760..c716da5adf5 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/BindingAssignmentPattern.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/BindingAssignmentPattern.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - BindingAssignmentPattern, - bindingAssignmentPattern, + AnyNode, + BindingAssignmentPattern, + bindingAssignmentPattern, } from '@romejs/js-ast'; export default function BindingAssignmentPattern(node: AnyNode, scope: Scope) { - node = bindingAssignmentPattern.assert(node); - scope; - throw new Error('unimplemented'); + node = bindingAssignmentPattern.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/BindingIdentifier.ts b/packages/@romejs/js-analysis/evaluators/patterns/BindingIdentifier.ts index f5b977727ad..eac62ade8ed 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/BindingIdentifier.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/BindingIdentifier.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, BindingIdentifier, bindingIdentifier} from '@romejs/js-ast'; export default function BindingIdentifier(node: AnyNode, scope: Scope) { - node = bindingIdentifier.assert(node); - scope; - throw new Error('unimplemented'); + node = bindingIdentifier.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/BindingObjectPattern.ts b/packages/@romejs/js-analysis/evaluators/patterns/BindingObjectPattern.ts index 348f43d0443..1d48cb355ca 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/BindingObjectPattern.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/BindingObjectPattern.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - BindingObjectPattern, - bindingObjectPattern, + AnyNode, + BindingObjectPattern, + bindingObjectPattern, } from '@romejs/js-ast'; export default function BindingObjectPattern(node: AnyNode, scope: Scope) { - node = bindingObjectPattern.assert(node); - scope; - throw new Error('unimplemented'); + node = bindingObjectPattern.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/BindingObjectPatternProperty.ts b/packages/@romejs/js-analysis/evaluators/patterns/BindingObjectPatternProperty.ts index 04e3d3716e9..12cd1f37d78 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/BindingObjectPatternProperty.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/BindingObjectPatternProperty.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - BindingObjectPatternProperty, - bindingObjectPatternProperty, + AnyNode, + BindingObjectPatternProperty, + bindingObjectPatternProperty, } from '@romejs/js-ast'; export default function BindingObjectPatternProperty(node: AnyNode) { - node = bindingObjectPatternProperty.assert(node); - throw new Error('unimplemented'); + node = bindingObjectPatternProperty.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/patterns/PatternMeta.ts b/packages/@romejs/js-analysis/evaluators/patterns/PatternMeta.ts index 3ccc9f2e74a..6633d860f96 100644 --- a/packages/@romejs/js-analysis/evaluators/patterns/PatternMeta.ts +++ b/packages/@romejs/js-analysis/evaluators/patterns/PatternMeta.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, PatternMeta, patternMeta} from '@romejs/js-ast'; export default function PatternMeta(node: AnyNode, scope: Scope) { - node = patternMeta.assert(node); - scope; - throw new Error('unimplemented'); + node = patternMeta.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpAlternation.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpAlternation.ts index 3674bb84d31..e3ed6d9f72f 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpAlternation.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpAlternation.ts @@ -8,6 +8,6 @@ import {AnyNode, RegExpAlternation, regExpAlternation} from '@romejs/js-ast'; export default function RegExpAlternation(node: AnyNode) { - node = regExpAlternation.assert(node); - throw new Error('unimplemented'); + node = regExpAlternation.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpAnyCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpAnyCharacter.ts index cace820bc56..511f676e481 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpAnyCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpAnyCharacter.ts @@ -8,6 +8,6 @@ import {AnyNode, RegExpAnyCharacter, regExpAnyCharacter} from '@romejs/js-ast'; export default function RegExpAnyCharacter(node: AnyNode) { - node = regExpAnyCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpAnyCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpCharSet.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpCharSet.ts index 6f626584c0b..df1667aa1fb 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpCharSet.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpCharSet.ts @@ -8,6 +8,6 @@ import {AnyNode, RegExpCharSet, regExpCharSet} from '@romejs/js-ast'; export default function RegExpCharSet(node: AnyNode) { - node = regExpCharSet.assert(node); - throw new Error('unimplemented'); + node = regExpCharSet.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpCharSetRange.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpCharSetRange.ts index bd7b54501c9..14990c5651b 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpCharSetRange.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpCharSetRange.ts @@ -8,6 +8,6 @@ import {AnyNode, RegExpCharSetRange, regExpCharSetRange} from '@romejs/js-ast'; export default function RegExpCharSetRange(node: AnyNode) { - node = regExpCharSetRange.assert(node); - throw new Error('unimplemented'); + node = regExpCharSetRange.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpCharacter.ts index 2c373a236c1..3f25b9dcc61 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpCharacter.ts @@ -8,6 +8,6 @@ import {AnyNode, RegExpCharacter, regExpCharacter} from '@romejs/js-ast'; export default function RegExpCharacter(node: AnyNode) { - node = regExpCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpControlCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpControlCharacter.ts index 0b880449e16..6a0a3970345 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpControlCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpControlCharacter.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpControlCharacter, - regExpControlCharacter, + AnyNode, + RegExpControlCharacter, + regExpControlCharacter, } from '@romejs/js-ast'; export default function RegExpControlCharacter(node: AnyNode) { - node = regExpControlCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpControlCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpDigitCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpDigitCharacter.ts index f2c0d78a057..f448b284de6 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpDigitCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpDigitCharacter.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpDigitCharacter, - regExpDigitCharacter, + AnyNode, + RegExpDigitCharacter, + regExpDigitCharacter, } from '@romejs/js-ast'; export default function RegExpDigitCharacter(node: AnyNode) { - node = regExpDigitCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpDigitCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpEndCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpEndCharacter.ts index 99f0260aa25..8dd867f2a6f 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpEndCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpEndCharacter.ts @@ -8,6 +8,6 @@ import {AnyNode, RegExpEndCharacter, regExpEndCharacter} from '@romejs/js-ast'; export default function RegExpEndCharacter(node: AnyNode) { - node = regExpEndCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpEndCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpGroupCapture.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpGroupCapture.ts index 3dea46008c0..76a5d0d4440 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpGroupCapture.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpGroupCapture.ts @@ -8,6 +8,6 @@ import {AnyNode, RegExpGroupCapture, regExpGroupCapture} from '@romejs/js-ast'; export default function RegExpGroupCapture(node: AnyNode) { - node = regExpGroupCapture.assert(node); - throw new Error('unimplemented'); + node = regExpGroupCapture.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpGroupNonCapture.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpGroupNonCapture.ts index 0ff368c8f37..13e77e0581b 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpGroupNonCapture.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpGroupNonCapture.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpGroupNonCapture, - regExpGroupNonCapture, + AnyNode, + RegExpGroupNonCapture, + regExpGroupNonCapture, } from '@romejs/js-ast'; export default function RegExpGroupNonCapture(node: AnyNode) { - node = regExpGroupNonCapture.assert(node); - throw new Error('unimplemented'); + node = regExpGroupNonCapture.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpNamedBackReference.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpNamedBackReference.ts index c2ed3e37949..db45efae5c3 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpNamedBackReference.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpNamedBackReference.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpNamedBackReference, - regExpNamedBackReference, + AnyNode, + RegExpNamedBackReference, + regExpNamedBackReference, } from '@romejs/js-ast'; export default function RegExpNamedBackReference(node: AnyNode) { - node = regExpNamedBackReference.assert(node); - throw new Error('unimplemented'); + node = regExpNamedBackReference.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpNonDigitCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpNonDigitCharacter.ts index 25319e00970..b1ebc6e3905 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpNonDigitCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpNonDigitCharacter.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpNonDigitCharacter, - regExpNonDigitCharacter, + AnyNode, + RegExpNonDigitCharacter, + regExpNonDigitCharacter, } from '@romejs/js-ast'; export default function RegExpNonDigitCharacter(node: AnyNode) { - node = regExpNonDigitCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpNonDigitCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWhiteSpaceCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWhiteSpaceCharacter.ts index 608eac663f3..1cc2724b3d2 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWhiteSpaceCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWhiteSpaceCharacter.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpNonWhiteSpaceCharacter, - regExpNonWhiteSpaceCharacter, + AnyNode, + RegExpNonWhiteSpaceCharacter, + regExpNonWhiteSpaceCharacter, } from '@romejs/js-ast'; export default function RegExpNonWhiteSpaceCharacter(node: AnyNode) { - node = regExpNonWhiteSpaceCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpNonWhiteSpaceCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWordBoundaryCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWordBoundaryCharacter.ts index 6292f5a2fe2..e6b9b7be96a 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWordBoundaryCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWordBoundaryCharacter.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpNonWordBoundaryCharacter, - regExpNonWordBoundaryCharacter, + AnyNode, + RegExpNonWordBoundaryCharacter, + regExpNonWordBoundaryCharacter, } from '@romejs/js-ast'; export default function RegExpNonWordBoundaryCharacter(node: AnyNode) { - node = regExpNonWordBoundaryCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpNonWordBoundaryCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWordCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWordCharacter.ts index a0b1ade8186..d4efe430e7e 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWordCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpNonWordCharacter.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpNonWordCharacter, - regExpNonWordCharacter, + AnyNode, + RegExpNonWordCharacter, + regExpNonWordCharacter, } from '@romejs/js-ast'; export default function RegExpNonWordCharacter(node: AnyNode) { - node = regExpNonWordCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpNonWordCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpNumericBackReference.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpNumericBackReference.ts index fb2c166fe4b..359dfa2d435 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpNumericBackReference.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpNumericBackReference.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpNumericBackReference, - regExpNumericBackReference, + AnyNode, + RegExpNumericBackReference, + regExpNumericBackReference, } from '@romejs/js-ast'; export default function RegExpNumericBackReference(node: AnyNode) { - node = regExpNumericBackReference.assert(node); - throw new Error('unimplemented'); + node = regExpNumericBackReference.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpQuantified.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpQuantified.ts index bf52b33fe9a..e9251076b0e 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpQuantified.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpQuantified.ts @@ -8,6 +8,6 @@ import {AnyNode, RegExpQuantified, regExpQuantified} from '@romejs/js-ast'; export default function RegExpQuantified(node: AnyNode) { - node = regExpQuantified.assert(node); - throw new Error('unimplemented'); + node = regExpQuantified.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpStartCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpStartCharacter.ts index 1bb30018466..cfc2356ed50 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpStartCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpStartCharacter.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpStartCharacter, - regExpStartCharacter, + AnyNode, + RegExpStartCharacter, + regExpStartCharacter, } from '@romejs/js-ast'; export default function RegExpStartCharacter(node: AnyNode) { - node = regExpStartCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpStartCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpSubExpression.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpSubExpression.ts index ff4e09591b7..f079bc769fa 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpSubExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpSubExpression.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpSubExpression, - regExpSubExpression, + AnyNode, + RegExpSubExpression, + regExpSubExpression, } from '@romejs/js-ast'; export default function RegExpSubExpression(node: AnyNode) { - node = regExpSubExpression.assert(node); - throw new Error('unimplemented'); + node = regExpSubExpression.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpWhiteSpaceCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpWhiteSpaceCharacter.ts index 1c4ce6b2544..dd864bf5cdd 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpWhiteSpaceCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpWhiteSpaceCharacter.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpWhiteSpaceCharacter, - regExpWhiteSpaceCharacter, + AnyNode, + RegExpWhiteSpaceCharacter, + regExpWhiteSpaceCharacter, } from '@romejs/js-ast'; export default function RegExpWhiteSpaceCharacter(node: AnyNode) { - node = regExpWhiteSpaceCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpWhiteSpaceCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpWordBoundaryCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpWordBoundaryCharacter.ts index 7c8fd3c4aac..9d6b78df3ab 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpWordBoundaryCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpWordBoundaryCharacter.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpWordBoundaryCharacter, - regExpWordBoundaryCharacter, + AnyNode, + RegExpWordBoundaryCharacter, + regExpWordBoundaryCharacter, } from '@romejs/js-ast'; export default function RegExpWordBoundaryCharacter(node: AnyNode) { - node = regExpWordBoundaryCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpWordBoundaryCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/regex/RegExpWordCharacter.ts b/packages/@romejs/js-analysis/evaluators/regex/RegExpWordCharacter.ts index 233a465f179..4a545d47c53 100644 --- a/packages/@romejs/js-analysis/evaluators/regex/RegExpWordCharacter.ts +++ b/packages/@romejs/js-analysis/evaluators/regex/RegExpWordCharacter.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - RegExpWordCharacter, - regExpWordCharacter, + AnyNode, + RegExpWordCharacter, + regExpWordCharacter, } from '@romejs/js-ast'; export default function RegExpWordCharacter(node: AnyNode) { - node = regExpWordCharacter.assert(node); - throw new Error('unimplemented'); + node = regExpWordCharacter.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/BlockStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/BlockStatement.ts index ee49889443b..5a59fb7cb2d 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/BlockStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/BlockStatement.ts @@ -11,57 +11,57 @@ import {getBindingIdentifiers, isTypeNode} from '@romejs/js-ast-utils'; import BlockT from '../../types/BlockT'; function shouldHoistExecute(node: undefined | AnyNode): boolean { - if (node === undefined) { - return false; - } + if (node === undefined) { + return false; + } - if (node.type === 'FunctionDeclaration' || isTypeNode(node)) { - return true; - } + if (node.type === 'FunctionDeclaration' || isTypeNode(node)) { + return true; + } - if ( - node.type === 'ExportLocalDeclaration' || - node.type === 'ExportDefaultDeclaration' - ) { - return shouldHoistExecute(node.declaration); - } + if ( + node.type === 'ExportLocalDeclaration' || + node.type === 'ExportDefaultDeclaration' + ) { + return shouldHoistExecute(node.declaration); + } - return false; + return false; } export default function BlockStatement(node: AnyNode, scope: Scope) { - node = node.type === 'Program' ? node : blockStatement.assert(node); + node = node.type === 'Program' ? node : blockStatement.assert(node); - // Declare variables - for (const child of node.body) { - if (child.type === 'ImportDeclaration') { - scope.evaluate(child); - } + // Declare variables + for (const child of node.body) { + if (child.type === 'ImportDeclaration') { + scope.evaluate(child); + } - const declarations = getBindingIdentifiers(child); - for (const id of declarations) { - scope.declareBinding(id.name, id); - } - } + const declarations = getBindingIdentifiers(child); + for (const id of declarations) { + scope.declareBinding(id.name, id); + } + } - const types = []; + const types = []; - // Execute hoisted nodes - const body = []; - for (const child of node.body) { - if (child.type === 'ImportDeclaration') { - // already executed - } else if (shouldHoistExecute(child)) { - types.push(scope.evaluate(child)); - } else { - body.push(child); - } - } + // Execute hoisted nodes + const body = []; + for (const child of node.body) { + if (child.type === 'ImportDeclaration') { + // already executed + } else if (shouldHoistExecute(child)) { + types.push(scope.evaluate(child)); + } else { + body.push(child); + } + } - // Execute rest - for (const child of body) { - types.push(scope.evaluate(child)); - } + // Execute rest + for (const child of body) { + types.push(scope.evaluate(child)); + } - return new BlockT(scope, node, types); + return new BlockT(scope, node, types); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/BreakStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/BreakStatement.ts index fbe7286244f..cee203e4f88 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/BreakStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/BreakStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, BreakStatement, breakStatement} from '@romejs/js-ast'; export default function BreakStatement(node: AnyNode, scope: Scope) { - node = breakStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = breakStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/ContinueStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/ContinueStatement.ts index 2a6b4306903..7c12745bd9e 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/ContinueStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/ContinueStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ContinueStatement, continueStatement} from '@romejs/js-ast'; export default function ContinueStatement(node: AnyNode, scope: Scope) { - node = continueStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = continueStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/DebuggerStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/DebuggerStatement.ts index 91b8bc9ae33..9a5f8bbbb83 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/DebuggerStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/DebuggerStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, DebuggerStatement, debuggerStatement} from '@romejs/js-ast'; export default function DebuggerStatement(node: AnyNode, scope: Scope) { - node = debuggerStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = debuggerStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/DoWhileStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/DoWhileStatement.ts index 306055a5356..cb73d58a704 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/DoWhileStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/DoWhileStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, DoWhileStatement, doWhileStatement} from '@romejs/js-ast'; export default function DoWhileStatement(node: AnyNode, scope: Scope) { - node = doWhileStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = doWhileStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/EmptyStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/EmptyStatement.ts index 8872919a555..abefc9e1c2a 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/EmptyStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/EmptyStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, EmptyStatement, emptyStatement} from '@romejs/js-ast'; export default function EmptyStatement(node: AnyNode, scope: Scope) { - node = emptyStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = emptyStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/ExpressionStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/ExpressionStatement.ts index f4e96bd1abf..e5ce2801e65 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/ExpressionStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/ExpressionStatement.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ExpressionStatement, - expressionStatement, + AnyNode, + ExpressionStatement, + expressionStatement, } from '@romejs/js-ast'; export default function ExpressionStatement(node: AnyNode, scope: Scope) { - node = expressionStatement.assert(node); + node = expressionStatement.assert(node); - return scope.evaluate(node.expression); + return scope.evaluate(node.expression); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/ForInStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/ForInStatement.ts index 2e4a7bcc364..b0165be919d 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/ForInStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/ForInStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ForInStatement, forInStatement} from '@romejs/js-ast'; export default function ForInStatement(node: AnyNode, scope: Scope) { - node = forInStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = forInStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/ForOfStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/ForOfStatement.ts index 82f0f0e1805..f8813312006 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/ForOfStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/ForOfStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ForOfStatement, forOfStatement} from '@romejs/js-ast'; export default function ForOfStatement(node: AnyNode, scope: Scope) { - node = forOfStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = forOfStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/ForStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/ForStatement.ts index 7932ae53e1b..f01b7584e54 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/ForStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/ForStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ForStatement, forStatement} from '@romejs/js-ast'; export default function ForStatement(node: AnyNode, scope: Scope) { - node = forStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = forStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/FunctionDeclaration.ts b/packages/@romejs/js-analysis/evaluators/statements/FunctionDeclaration.ts index db731e99552..e0878c70311 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/FunctionDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/FunctionDeclaration.ts @@ -7,18 +7,18 @@ import {Scope} from '../../scopes'; import { - AnyNode, - FunctionDeclaration, - functionDeclaration, + AnyNode, + FunctionDeclaration, + functionDeclaration, } from '@romejs/js-ast'; import executeFunction from '../../utils/executeFunction'; export default function FunctionDeclaration(node: AnyNode, scope: Scope) { - node = functionDeclaration.assert(node); + node = functionDeclaration.assert(node); - const func = executeFunction(node, scope, false); - if (node.id !== undefined) { - scope.addBinding(node.id.name, func); - } - return func; + const func = executeFunction(node, scope, false); + if (node.id !== undefined) { + scope.addBinding(node.id.name, func); + } + return func; } diff --git a/packages/@romejs/js-analysis/evaluators/statements/IfStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/IfStatement.ts index 9b542a35dd3..ffc038d5742 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/IfStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/IfStatement.ts @@ -12,22 +12,22 @@ import ExhaustiveT from '../../types/ExhaustiveT'; import UnionT from '../../types/UnionT'; export default function IfStatement(node: AnyNode, scope: Scope) { - node = node.type === 'ConditionalExpression' ? node : ifStatement.assert(node); + node = node.type === 'ConditionalExpression' ? node : ifStatement.assert(node); - const test = scope.evaluate(node.test); - new ExhaustiveT(scope, node, test, new BooleanT(scope, undefined)); + const test = scope.evaluate(node.test); + new ExhaustiveT(scope, node, test, new BooleanT(scope, undefined)); - const hasRefinedTest: boolean = test.scope instanceof RefineScope; + const hasRefinedTest: boolean = test.scope instanceof RefineScope; - const consequentScope: Scope = hasRefinedTest ? test.scope : scope; - const consequent = consequentScope.evaluate(node.consequent); + const consequentScope: Scope = hasRefinedTest ? test.scope : scope; + const consequent = consequentScope.evaluate(node.consequent); - if (node.alternate === undefined) { - return consequent; - } else { - const alternateScope = scope.fork(); + if (node.alternate === undefined) { + return consequent; + } else { + const alternateScope = scope.fork(); - /*if (hasRefinedTest) { + /*if (hasRefinedTest) { // get bindings from 'test.scope and flip them for (const name of test.scope.getOwnBindingNames()) { const outerBinding = scope.getBinding(name); @@ -40,10 +40,10 @@ export default function IfStatement(node: AnyNode, scope: Scope) { alternateScope.addBinding(name, opposite); } }*/ - return new UnionT( - scope, - undefined, - [consequent, alternateScope.evaluate(node.alternate)], - ); - } + return new UnionT( + scope, + undefined, + [consequent, alternateScope.evaluate(node.alternate)], + ); + } } diff --git a/packages/@romejs/js-analysis/evaluators/statements/LabeledStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/LabeledStatement.ts index dc8088905a2..ceff2ceb3d2 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/LabeledStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/LabeledStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, LabeledStatement, labeledStatement} from '@romejs/js-ast'; export default function LabeledStatement(node: AnyNode, scope: Scope) { - node = labeledStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = labeledStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/ReturnStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/ReturnStatement.ts index 19ad108438b..c70b71ee34b 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/ReturnStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/ReturnStatement.ts @@ -9,12 +9,12 @@ import {FunctionScope, Scope} from '../../scopes'; import {AnyNode, ReturnStatement, returnStatement} from '@romejs/js-ast'; export default function ReturnStatement(node: AnyNode, scope: Scope) { - node = returnStatement.assert(node); - const funcScope = scope.find(FunctionScope); - if (node.argument === undefined) { - // TODO connect to undefined - } else { - const type = scope.evaluate(node.argument); - funcScope.meta.returnType.shouldMatch(type); - } + node = returnStatement.assert(node); + const funcScope = scope.find(FunctionScope); + if (node.argument === undefined) { + // TODO connect to undefined + } else { + const type = scope.evaluate(node.argument); + funcScope.meta.returnType.shouldMatch(type); + } } diff --git a/packages/@romejs/js-analysis/evaluators/statements/SwitchStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/SwitchStatement.ts index 45a03aa27d5..6284590687c 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/SwitchStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/SwitchStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, SwitchStatement, switchStatement} from '@romejs/js-ast'; export default function SwitchStatement(node: AnyNode, scope: Scope) { - node = switchStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = switchStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/ThrowStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/ThrowStatement.ts index eff4ebea64e..777a401f85f 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/ThrowStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/ThrowStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, ThrowStatement, throwStatement} from '@romejs/js-ast'; export default function ThrowStatement(node: AnyNode, scope: Scope) { - node = throwStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = throwStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/TryStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/TryStatement.ts index bedf7579929..1891468fc17 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/TryStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/TryStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TryStatement, tryStatement} from '@romejs/js-ast'; export default function TryStatement(node: AnyNode, scope: Scope) { - node = tryStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = tryStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/VariableDeclarationStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/VariableDeclarationStatement.ts index ac426d70c8f..cb4aa5478ec 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/VariableDeclarationStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/VariableDeclarationStatement.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - VariableDeclarationStatement, - variableDeclarationStatement, + AnyNode, + VariableDeclarationStatement, + variableDeclarationStatement, } from '@romejs/js-ast'; export default function VariableDeclarationStatement(node: AnyNode) { - node = variableDeclarationStatement.assert(node); - throw new Error('unimplemented'); + node = variableDeclarationStatement.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/WhileStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/WhileStatement.ts index 9172288bd40..9608f157176 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/WhileStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/WhileStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, WhileStatement, whileStatement} from '@romejs/js-ast'; export default function WhileStatement(node: AnyNode, scope: Scope) { - node = whileStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = whileStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/statements/WithStatement.ts b/packages/@romejs/js-analysis/evaluators/statements/WithStatement.ts index c66813eea3b..47b540840e9 100644 --- a/packages/@romejs/js-analysis/evaluators/statements/WithStatement.ts +++ b/packages/@romejs/js-analysis/evaluators/statements/WithStatement.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, WithStatement, withStatement} from '@romejs/js-ast'; export default function WithStatement(node: AnyNode, scope: Scope) { - node = withStatement.assert(node); - scope; - throw new Error('unimplemented'); + node = withStatement.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/temp/AmbiguousFlowTypeCastExpression.ts b/packages/@romejs/js-analysis/evaluators/temp/AmbiguousFlowTypeCastExpression.ts index 743cde21e0b..2391e1b0179 100644 --- a/packages/@romejs/js-analysis/evaluators/temp/AmbiguousFlowTypeCastExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/temp/AmbiguousFlowTypeCastExpression.ts @@ -6,12 +6,12 @@ */ import { - AmbiguousFlowTypeCastExpression, - AnyNode, - ambiguousFlowTypeCastExpression, + AmbiguousFlowTypeCastExpression, + AnyNode, + ambiguousFlowTypeCastExpression, } from '@romejs/js-ast'; export default function AmbiguousFlowTypeCastExpression(node: AnyNode) { - node = ambiguousFlowTypeCastExpression.assert(node); - throw new Error('unimplemented'); + node = ambiguousFlowTypeCastExpression.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/temp/MockParent.ts b/packages/@romejs/js-analysis/evaluators/temp/MockParent.ts index b78d5dba232..1b41f63bb0b 100644 --- a/packages/@romejs/js-analysis/evaluators/temp/MockParent.ts +++ b/packages/@romejs/js-analysis/evaluators/temp/MockParent.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, MockParent, mockParent} from '@romejs/js-ast'; export default function MockParent(node: AnyNode, scope: Scope) { - node = mockParent.assert(node); - scope; - throw new Error('unimplemented'); + node = mockParent.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/AnyKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/AnyKeywordTypeAnnotation.ts index cd14c08d84d..9dd579fdf21 100644 --- a/packages/@romejs/js-analysis/evaluators/types/AnyKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/AnyKeywordTypeAnnotation.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyKeywordTypeAnnotation, - AnyNode, - anyKeywordTypeAnnotation, + AnyKeywordTypeAnnotation, + AnyNode, + anyKeywordTypeAnnotation, } from '@romejs/js-ast'; import AnyT from '../../types/AnyT'; export default function AnyKeywordTypeAnnotation(node: AnyNode, scope: Scope) { - node = anyKeywordTypeAnnotation.assert(node); - return new AnyT(scope, node); + node = anyKeywordTypeAnnotation.assert(node); + return new AnyT(scope, node); } diff --git a/packages/@romejs/js-analysis/evaluators/types/BigIntKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/BigIntKeywordTypeAnnotation.ts index a0d2b7c649e..7f3855bb72a 100644 --- a/packages/@romejs/js-analysis/evaluators/types/BigIntKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/BigIntKeywordTypeAnnotation.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - BigIntKeywordTypeAnnotation, - bigIntKeywordTypeAnnotation, + AnyNode, + BigIntKeywordTypeAnnotation, + bigIntKeywordTypeAnnotation, } from '@romejs/js-ast'; export default function BigIntKeywordTypeAnnotation(node: AnyNode, scope: Scope) { - node = bigIntKeywordTypeAnnotation.assert(node); - scope; - throw new Error('unimplemented'); + node = bigIntKeywordTypeAnnotation.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/BooleanKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/BooleanKeywordTypeAnnotation.ts index 94946f8dc72..5c28a6756a2 100644 --- a/packages/@romejs/js-analysis/evaluators/types/BooleanKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/BooleanKeywordTypeAnnotation.ts @@ -7,16 +7,16 @@ import {Scope} from '../../scopes'; import { - AnyNode, - BooleanKeywordTypeAnnotation, - booleanKeywordTypeAnnotation, + AnyNode, + BooleanKeywordTypeAnnotation, + booleanKeywordTypeAnnotation, } from '@romejs/js-ast'; import BooleanT from '../../types/BooleanT'; export default function BooleanKeywordTypeAnnotation( - node: AnyNode, - scope: Scope, + node: AnyNode, + scope: Scope, ) { - node = booleanKeywordTypeAnnotation.assert(node); - return new BooleanT(scope, node); + node = booleanKeywordTypeAnnotation.assert(node); + return new BooleanT(scope, node); } diff --git a/packages/@romejs/js-analysis/evaluators/types/BooleanLiteralTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/BooleanLiteralTypeAnnotation.ts index edc9bb962f7..37cec130521 100644 --- a/packages/@romejs/js-analysis/evaluators/types/BooleanLiteralTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/BooleanLiteralTypeAnnotation.ts @@ -7,16 +7,16 @@ import {Scope} from '../../scopes'; import { - AnyNode, - BooleanLiteralTypeAnnotation, - booleanLiteralTypeAnnotation, + AnyNode, + BooleanLiteralTypeAnnotation, + booleanLiteralTypeAnnotation, } from '@romejs/js-ast'; export default function BooleanLiteralTypeAnnotation( - node: AnyNode, - scope: Scope, + node: AnyNode, + scope: Scope, ) { - node = booleanLiteralTypeAnnotation.assert(node); - scope; - throw new Error('unimplemented'); + node = booleanLiteralTypeAnnotation.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/EmptyKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/EmptyKeywordTypeAnnotation.ts index d95cd8ce546..3af8be1777f 100644 --- a/packages/@romejs/js-analysis/evaluators/types/EmptyKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/EmptyKeywordTypeAnnotation.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - EmptyKeywordTypeAnnotation, - emptyKeywordTypeAnnotation, + AnyNode, + EmptyKeywordTypeAnnotation, + emptyKeywordTypeAnnotation, } from '@romejs/js-ast'; export default function EmptyKeywordTypeAnnotation(node: AnyNode, scope: Scope) { - node = emptyKeywordTypeAnnotation.assert(node); - scope; - throw new Error('unimplemented'); + node = emptyKeywordTypeAnnotation.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/IntersectionTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/IntersectionTypeAnnotation.ts index 6b6667c3f5a..8e68fc72000 100644 --- a/packages/@romejs/js-analysis/evaluators/types/IntersectionTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/IntersectionTypeAnnotation.ts @@ -7,20 +7,20 @@ import {Scope} from '../../scopes'; import { - AnyNode, - IntersectionTypeAnnotation, - intersectionTypeAnnotation, + AnyNode, + IntersectionTypeAnnotation, + intersectionTypeAnnotation, } from '@romejs/js-ast'; import IntersectionT from '../../types/IntersectionT'; export default function IntersectionTypeAnnotation(node: AnyNode, scope: Scope) { - node = intersectionTypeAnnotation.assert(node); + node = intersectionTypeAnnotation.assert(node); - return new IntersectionT( - scope, - node, - node.types.map((type) => { - return scope.evaluate(type); - }), - ); + return new IntersectionT( + scope, + node, + node.types.map((type) => { + return scope.evaluate(type); + }), + ); } diff --git a/packages/@romejs/js-analysis/evaluators/types/MixedKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/MixedKeywordTypeAnnotation.ts index 0cd6bcd628a..fa8f0ee39d1 100644 --- a/packages/@romejs/js-analysis/evaluators/types/MixedKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/MixedKeywordTypeAnnotation.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - MixedKeywordTypeAnnotation, - mixedKeywordTypeAnnotation, + AnyNode, + MixedKeywordTypeAnnotation, + mixedKeywordTypeAnnotation, } from '@romejs/js-ast'; import MixedT from '../../types/MixedT'; export default function MixedKeywordTypeAnnotation(node: AnyNode, scope: Scope) { - node = mixedKeywordTypeAnnotation.assert(node); - return new MixedT(scope, node); + node = mixedKeywordTypeAnnotation.assert(node); + return new MixedT(scope, node); } diff --git a/packages/@romejs/js-analysis/evaluators/types/NeverKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/NeverKeywordTypeAnnotation.ts index c4d754f5706..106203c27a7 100644 --- a/packages/@romejs/js-analysis/evaluators/types/NeverKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/NeverKeywordTypeAnnotation.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - NeverKeywordTypeAnnotation, - neverKeywordTypeAnnotation, + AnyNode, + NeverKeywordTypeAnnotation, + neverKeywordTypeAnnotation, } from '@romejs/js-ast'; export default function NeverKeywordTypeAnnotation(node: AnyNode) { - node = neverKeywordTypeAnnotation.assert(node); - throw new Error('unimplemented'); + node = neverKeywordTypeAnnotation.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/NullKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/NullKeywordTypeAnnotation.ts index 055f819e03a..4301c3a7b9f 100644 --- a/packages/@romejs/js-analysis/evaluators/types/NullKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/NullKeywordTypeAnnotation.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - NullKeywordTypeAnnotation, - nullKeywordTypeAnnotation, + AnyNode, + NullKeywordTypeAnnotation, + nullKeywordTypeAnnotation, } from '@romejs/js-ast'; import NullT from '../../types/NullT'; export default function NullKeywordTypeAnnotation(node: AnyNode, scope: Scope) { - node = nullKeywordTypeAnnotation.assert(node); - return new NullT(scope, node); + node = nullKeywordTypeAnnotation.assert(node); + return new NullT(scope, node); } diff --git a/packages/@romejs/js-analysis/evaluators/types/NumberKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/NumberKeywordTypeAnnotation.ts index 751e583e2df..8d8c5ae5eae 100644 --- a/packages/@romejs/js-analysis/evaluators/types/NumberKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/NumberKeywordTypeAnnotation.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - NumberKeywordTypeAnnotation, - numberKeywordTypeAnnotation, + AnyNode, + NumberKeywordTypeAnnotation, + numberKeywordTypeAnnotation, } from '@romejs/js-ast'; import NumericT from '../../types/NumericT'; export default function NumberKeywordTypeAnnotation(node: AnyNode, scope: Scope) { - node = numberKeywordTypeAnnotation.assert(node); - return new NumericT(scope, node); + node = numberKeywordTypeAnnotation.assert(node); + return new NumericT(scope, node); } diff --git a/packages/@romejs/js-analysis/evaluators/types/NumericLiteralTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/NumericLiteralTypeAnnotation.ts index 0601297c435..262850694f3 100644 --- a/packages/@romejs/js-analysis/evaluators/types/NumericLiteralTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/NumericLiteralTypeAnnotation.ts @@ -7,16 +7,16 @@ import {Scope} from '../../scopes'; import { - AnyNode, - NumericLiteralTypeAnnotation, - numericLiteralTypeAnnotation, + AnyNode, + NumericLiteralTypeAnnotation, + numericLiteralTypeAnnotation, } from '@romejs/js-ast'; export default function NumericLiteralTypeAnnotation( - node: AnyNode, - scope: Scope, + node: AnyNode, + scope: Scope, ) { - node = numericLiteralTypeAnnotation.assert(node); - scope; - throw new Error('unimplemented'); + node = numericLiteralTypeAnnotation.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/ObjectKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/ObjectKeywordTypeAnnotation.ts index 1424d9656f0..0d81497ce23 100644 --- a/packages/@romejs/js-analysis/evaluators/types/ObjectKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/ObjectKeywordTypeAnnotation.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - ObjectKeywordTypeAnnotation, - objectKeywordTypeAnnotation, + AnyNode, + ObjectKeywordTypeAnnotation, + objectKeywordTypeAnnotation, } from '@romejs/js-ast'; export default function ObjectKeywordTypeAnnotation(node: AnyNode, scope: Scope) { - node = objectKeywordTypeAnnotation.assert(node); - scope; - throw new Error('unimplemented'); + node = objectKeywordTypeAnnotation.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/StringKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/StringKeywordTypeAnnotation.ts index 1c1422d201b..dc98e12e1da 100644 --- a/packages/@romejs/js-analysis/evaluators/types/StringKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/StringKeywordTypeAnnotation.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - StringKeywordTypeAnnotation, - stringKeywordTypeAnnotation, + AnyNode, + StringKeywordTypeAnnotation, + stringKeywordTypeAnnotation, } from '@romejs/js-ast'; import StringT from '../../types/StringT'; export default function StringKeywordTypeAnnotation(node: AnyNode, scope: Scope) { - node = stringKeywordTypeAnnotation.assert(node); - return new StringT(scope, node); + node = stringKeywordTypeAnnotation.assert(node); + return new StringT(scope, node); } diff --git a/packages/@romejs/js-analysis/evaluators/types/StringLiteralTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/StringLiteralTypeAnnotation.ts index e90b2e73d37..42eb4b6c5a5 100644 --- a/packages/@romejs/js-analysis/evaluators/types/StringLiteralTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/StringLiteralTypeAnnotation.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - StringLiteralTypeAnnotation, - stringLiteralTypeAnnotation, + AnyNode, + StringLiteralTypeAnnotation, + stringLiteralTypeAnnotation, } from '@romejs/js-ast'; export default function StringLiteralTypeAnnotation(node: AnyNode, scope: Scope) { - node = stringLiteralTypeAnnotation.assert(node); - scope; - throw new Error('unimplemented'); + node = stringLiteralTypeAnnotation.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/SymbolKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/SymbolKeywordTypeAnnotation.ts index edc7003d3a1..d0a6db5418e 100644 --- a/packages/@romejs/js-analysis/evaluators/types/SymbolKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/SymbolKeywordTypeAnnotation.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - SymbolKeywordTypeAnnotation, - symbolKeywordTypeAnnotation, + AnyNode, + SymbolKeywordTypeAnnotation, + symbolKeywordTypeAnnotation, } from '@romejs/js-ast'; export default function SymbolKeywordTypeAnnotation(node: AnyNode) { - node = symbolKeywordTypeAnnotation.assert(node); - throw new Error('unimplemented'); + node = symbolKeywordTypeAnnotation.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/TemplateLiteralTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/TemplateLiteralTypeAnnotation.ts index 5866ccd48ec..c0c4bca4f89 100644 --- a/packages/@romejs/js-analysis/evaluators/types/TemplateLiteralTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/TemplateLiteralTypeAnnotation.ts @@ -7,16 +7,16 @@ import {Scope} from '../../scopes'; import { - AnyNode, - TemplateLiteralTypeAnnotation, - templateLiteralTypeAnnotation, + AnyNode, + TemplateLiteralTypeAnnotation, + templateLiteralTypeAnnotation, } from '@romejs/js-ast'; export default function TemplateLiteralTypeAnnotation( - node: AnyNode, - scope: Scope, + node: AnyNode, + scope: Scope, ) { - node = templateLiteralTypeAnnotation.assert(node); - scope; - throw new Error('unimplemented'); + node = templateLiteralTypeAnnotation.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/TypeAliasTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/TypeAliasTypeAnnotation.ts index 7d4c2f19864..0ca08f7a586 100644 --- a/packages/@romejs/js-analysis/evaluators/types/TypeAliasTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/TypeAliasTypeAnnotation.ts @@ -7,20 +7,20 @@ import {Scope} from '../../scopes'; import { - AnyNode, - TypeAliasTypeAnnotation, - typeAliasTypeAnnotation, + AnyNode, + TypeAliasTypeAnnotation, + typeAliasTypeAnnotation, } from '@romejs/js-ast'; export default function TypeAliasTypeAnnotation(node: AnyNode, scope: Scope) { - node = typeAliasTypeAnnotation.assert(node); + node = typeAliasTypeAnnotation.assert(node); - const typeScope = scope.fork(); - if (node.typeParameters) { - typeScope.evaluate(node.typeParameters); - } + const typeScope = scope.fork(); + if (node.typeParameters) { + typeScope.evaluate(node.typeParameters); + } - const right = typeScope.evaluate(node.right); - scope.addBinding(node.id.name, right); - return right; + const right = typeScope.evaluate(node.right); + scope.addBinding(node.id.name, right); + return right; } diff --git a/packages/@romejs/js-analysis/evaluators/types/UndefinedKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/UndefinedKeywordTypeAnnotation.ts index 2fd8abaef99..8325551dc14 100644 --- a/packages/@romejs/js-analysis/evaluators/types/UndefinedKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/UndefinedKeywordTypeAnnotation.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - UndefinedKeywordTypeAnnotation, - undefinedKeywordTypeAnnotation, + AnyNode, + UndefinedKeywordTypeAnnotation, + undefinedKeywordTypeAnnotation, } from '@romejs/js-ast'; export default function UndefinedKeywordTypeAnnotation(node: AnyNode) { - node = undefinedKeywordTypeAnnotation.assert(node); - throw new Error('unimplemented'); + node = undefinedKeywordTypeAnnotation.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/UnionTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/UnionTypeAnnotation.ts index 44f057cec9e..1106e3d050c 100644 --- a/packages/@romejs/js-analysis/evaluators/types/UnionTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/UnionTypeAnnotation.ts @@ -7,20 +7,20 @@ import {Scope} from '../../scopes'; import { - AnyNode, - UnionTypeAnnotation, - unionTypeAnnotation, + AnyNode, + UnionTypeAnnotation, + unionTypeAnnotation, } from '@romejs/js-ast'; import UnionT from '../../types/UnionT'; export default function UnionTypeAnnotation(node: AnyNode, scope: Scope) { - node = unionTypeAnnotation.assert(node); + node = unionTypeAnnotation.assert(node); - return new UnionT( - scope, - node, - node.types.map((type) => { - return scope.evaluate(type); - }), - ); + return new UnionT( + scope, + node, + node.types.map((type) => { + return scope.evaluate(type); + }), + ); } diff --git a/packages/@romejs/js-analysis/evaluators/types/UnknownKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/UnknownKeywordTypeAnnotation.ts index 8fd5177c380..1700081ab7f 100644 --- a/packages/@romejs/js-analysis/evaluators/types/UnknownKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/UnknownKeywordTypeAnnotation.ts @@ -7,16 +7,16 @@ import {Scope} from '../../scopes'; import { - AnyNode, - UnknownKeywordTypeAnnotation, - unknownKeywordTypeAnnotation, + AnyNode, + UnknownKeywordTypeAnnotation, + unknownKeywordTypeAnnotation, } from '@romejs/js-ast'; export default function UnknownKeywordTypeAnnotation( - node: AnyNode, - scope: Scope, + node: AnyNode, + scope: Scope, ) { - node = unknownKeywordTypeAnnotation.assert(node); - scope; - throw new Error('unimplemented'); + node = unknownKeywordTypeAnnotation.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/types/VoidKeywordTypeAnnotation.ts b/packages/@romejs/js-analysis/evaluators/types/VoidKeywordTypeAnnotation.ts index dc7d2e4d1cb..87719529625 100644 --- a/packages/@romejs/js-analysis/evaluators/types/VoidKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-analysis/evaluators/types/VoidKeywordTypeAnnotation.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - VoidKeywordTypeAnnotation, - voidKeywordTypeAnnotation, + AnyNode, + VoidKeywordTypeAnnotation, + voidKeywordTypeAnnotation, } from '@romejs/js-ast'; import VoidT from '../../types/VoidT'; export default function VoidKeywordTypeAnnotation(node: AnyNode, scope: Scope) { - node = voidKeywordTypeAnnotation.assert(node); - return new VoidT(scope, node); + node = voidKeywordTypeAnnotation.assert(node); + return new VoidT(scope, node); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSArrayType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSArrayType.ts index 3117f6e2ef1..177d30c3b48 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSArrayType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSArrayType.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSArrayType, tsArrayType} from '@romejs/js-ast'; export default function TSArrayType(node: AnyNode, scope: Scope) { - node = tsArrayType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsArrayType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSAsExpression.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSAsExpression.ts index 18284ef126d..74b86c52504 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSAsExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSAsExpression.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSAsExpression, tsAsExpression} from '@romejs/js-ast'; export default function TSAsExpression(node: AnyNode, scope: Scope) { - node = tsAsExpression.assert(node); - scope; - throw new Error('unimplemented'); + node = tsAsExpression.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentAsExpression.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentAsExpression.ts index b1e11c84cf4..77efb11f318 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentAsExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentAsExpression.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSAssignmentAsExpression, - tsAssignmentAsExpression, + AnyNode, + TSAssignmentAsExpression, + tsAssignmentAsExpression, } from '@romejs/js-ast'; export default function TSAssignmentAsExpression(node: AnyNode) { - node = tsAssignmentAsExpression.assert(node); - throw new Error('unimplemented'); + node = tsAssignmentAsExpression.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentNonNullExpression.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentNonNullExpression.ts index af954ff3520..3112502c62f 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentNonNullExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentNonNullExpression.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSAssignmentNonNullExpression, - tsAssignmentNonNullExpression, + AnyNode, + TSAssignmentNonNullExpression, + tsAssignmentNonNullExpression, } from '@romejs/js-ast'; export default function TSAssignmentNonNullExpression(node: AnyNode) { - node = tsAssignmentNonNullExpression.assert(node); - throw new Error('unimplemented'); + node = tsAssignmentNonNullExpression.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentTypeAssertion.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentTypeAssertion.ts index 500f148c4c5..dd1e6b33625 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentTypeAssertion.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSAssignmentTypeAssertion.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSAssignmentTypeAssertion, - tsAssignmentTypeAssertion, + AnyNode, + TSAssignmentTypeAssertion, + tsAssignmentTypeAssertion, } from '@romejs/js-ast'; export default function TSAssignmentTypeAssertion(node: AnyNode) { - node = tsAssignmentTypeAssertion.assert(node); - throw new Error('unimplemented'); + node = tsAssignmentTypeAssertion.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSCallSignatureDeclaration.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSCallSignatureDeclaration.ts index 3480c399627..7db31b12d25 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSCallSignatureDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSCallSignatureDeclaration.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSCallSignatureDeclaration, - tsCallSignatureDeclaration, + AnyNode, + TSCallSignatureDeclaration, + tsCallSignatureDeclaration, } from '@romejs/js-ast'; export default function TSCallSignatureDeclaration(node: AnyNode) { - node = tsCallSignatureDeclaration.assert(node); - throw new Error('unimplemented'); + node = tsCallSignatureDeclaration.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSConditionalType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSConditionalType.ts index 997038d1e83..62fcf99cf37 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSConditionalType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSConditionalType.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSConditionalType, tsConditionalType} from '@romejs/js-ast'; export default function TSConditionalType(node: AnyNode, scope: Scope) { - node = tsConditionalType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsConditionalType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSConstructSignatureDeclaration.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSConstructSignatureDeclaration.ts index 25408098db9..2ac27489222 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSConstructSignatureDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSConstructSignatureDeclaration.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSConstructSignatureDeclaration, - tsConstructSignatureDeclaration, + AnyNode, + TSConstructSignatureDeclaration, + tsConstructSignatureDeclaration, } from '@romejs/js-ast'; export default function TSConstructSignatureDeclaration(node: AnyNode) { - node = tsConstructSignatureDeclaration.assert(node); - throw new Error('unimplemented'); + node = tsConstructSignatureDeclaration.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSConstructorType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSConstructorType.ts index d202360609a..60c5ac55bdc 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSConstructorType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSConstructorType.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSConstructorType, tsConstructorType} from '@romejs/js-ast'; export default function TSConstructorType(node: AnyNode, scope: Scope) { - node = tsConstructorType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsConstructorType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSDeclareFunction.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSDeclareFunction.ts index 149a3d90541..06ede0b9d54 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSDeclareFunction.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSDeclareFunction.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSDeclareFunction, tsDeclareFunction} from '@romejs/js-ast'; export default function TSDeclareFunction(node: AnyNode, scope: Scope) { - node = tsDeclareFunction.assert(node); - scope; - throw new Error('unimplemented'); + node = tsDeclareFunction.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSDeclareMethod.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSDeclareMethod.ts index 68c39597564..c50ec02eb26 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSDeclareMethod.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSDeclareMethod.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSDeclareMethod, tsDeclareMethod} from '@romejs/js-ast'; export default function TSDeclareMethod(node: AnyNode, scope: Scope) { - node = tsDeclareMethod.assert(node); - scope; - throw new Error('unimplemented'); + node = tsDeclareMethod.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSEnumDeclaration.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSEnumDeclaration.ts index 28acc93f1e1..7e9c12a3728 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSEnumDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSEnumDeclaration.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSEnumDeclaration, tsEnumDeclaration} from '@romejs/js-ast'; export default function TSEnumDeclaration(node: AnyNode, scope: Scope) { - node = tsEnumDeclaration.assert(node); - scope; - throw new Error('unimplemented'); + node = tsEnumDeclaration.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSEnumMember.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSEnumMember.ts index 8487f33231e..c31e76445c5 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSEnumMember.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSEnumMember.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSEnumMember, tsEnumMember} from '@romejs/js-ast'; export default function TSEnumMember(node: AnyNode, scope: Scope) { - node = tsEnumMember.assert(node); - scope; - throw new Error('unimplemented'); + node = tsEnumMember.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSExportAssignment.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSExportAssignment.ts index c2c32a28aa3..24a39d45dc1 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSExportAssignment.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSExportAssignment.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSExportAssignment, tsExportAssignment} from '@romejs/js-ast'; export default function TSExportAssignment(node: AnyNode, scope: Scope) { - node = tsExportAssignment.assert(node); - scope; - throw new Error('unimplemented'); + node = tsExportAssignment.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSExpressionWithTypeArguments.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSExpressionWithTypeArguments.ts index 95f41a764b9..7f3c2e78bd0 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSExpressionWithTypeArguments.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSExpressionWithTypeArguments.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSExpressionWithTypeArguments, - tsExpressionWithTypeArguments, + AnyNode, + TSExpressionWithTypeArguments, + tsExpressionWithTypeArguments, } from '@romejs/js-ast'; export default function TSExpressionWithTypeArguments(node: AnyNode) { - node = tsExpressionWithTypeArguments.assert(node); - throw new Error('unimplemented'); + node = tsExpressionWithTypeArguments.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSExternalModuleReference.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSExternalModuleReference.ts index 71a2750532c..4cc51da9a08 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSExternalModuleReference.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSExternalModuleReference.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSExternalModuleReference, - tsExternalModuleReference, + AnyNode, + TSExternalModuleReference, + tsExternalModuleReference, } from '@romejs/js-ast'; export default function TSExternalModuleReference(node: AnyNode) { - node = tsExternalModuleReference.assert(node); - throw new Error('unimplemented'); + node = tsExternalModuleReference.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSFunctionType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSFunctionType.ts index 3ef87f18bbd..a55c482a33e 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSFunctionType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSFunctionType.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSFunctionType, tsFunctionType} from '@romejs/js-ast'; export default function TSFunctionType(node: AnyNode, scope: Scope) { - node = tsFunctionType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsFunctionType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSImportEqualsDeclaration.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSImportEqualsDeclaration.ts index 5d62dc104da..6cf5e6342eb 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSImportEqualsDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSImportEqualsDeclaration.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSImportEqualsDeclaration, - tsImportEqualsDeclaration, + AnyNode, + TSImportEqualsDeclaration, + tsImportEqualsDeclaration, } from '@romejs/js-ast'; export default function TSImportEqualsDeclaration(node: AnyNode) { - node = tsImportEqualsDeclaration.assert(node); - throw new Error('unimplemented'); + node = tsImportEqualsDeclaration.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSImportType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSImportType.ts index 4e126792304..dd0e31fa879 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSImportType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSImportType.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSImportType, tsImportType} from '@romejs/js-ast'; export default function TSImportType(node: AnyNode, scope: Scope) { - node = tsImportType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsImportType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSIndexSignature.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSIndexSignature.ts index 2cd26888f98..223ee23ad82 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSIndexSignature.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSIndexSignature.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSIndexSignature, tsIndexSignature} from '@romejs/js-ast'; export default function TSIndexSignature(node: AnyNode, scope: Scope) { - node = tsIndexSignature.assert(node); - scope; - throw new Error('unimplemented'); + node = tsIndexSignature.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSIndexedAccessType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSIndexedAccessType.ts index 6f675f85bda..326a9276e47 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSIndexedAccessType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSIndexedAccessType.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - TSIndexedAccessType, - tsIndexedAccessType, + AnyNode, + TSIndexedAccessType, + tsIndexedAccessType, } from '@romejs/js-ast'; export default function TSIndexedAccessType(node: AnyNode, scope: Scope) { - node = tsIndexedAccessType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsIndexedAccessType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSInferType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSInferType.ts index b46062078bb..8a9fb69e604 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSInferType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSInferType.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSInferType, tsInferType} from '@romejs/js-ast'; export default function TSInferType(node: AnyNode, scope: Scope) { - node = tsInferType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsInferType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSInterfaceBody.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSInterfaceBody.ts index 8ba7625c13d..aeeb16d9dc9 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSInterfaceBody.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSInterfaceBody.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSInterfaceBody, tsInterfaceBody} from '@romejs/js-ast'; export default function TSInterfaceBody(node: AnyNode, scope: Scope) { - node = tsInterfaceBody.assert(node); - scope; - throw new Error('unimplemented'); + node = tsInterfaceBody.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSInterfaceDeclaration.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSInterfaceDeclaration.ts index 577787befb2..dd2c4fc734a 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSInterfaceDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSInterfaceDeclaration.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - TSInterfaceDeclaration, - tsInterfaceDeclaration, + AnyNode, + TSInterfaceDeclaration, + tsInterfaceDeclaration, } from '@romejs/js-ast'; export default function TSInterfaceDeclaration(node: AnyNode, scope: Scope) { - node = tsInterfaceDeclaration.assert(node); - scope; - throw new Error('unimplemented'); + node = tsInterfaceDeclaration.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSMappedType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSMappedType.ts index d7608730649..f4b1b1e862e 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSMappedType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSMappedType.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSMappedType, tsMappedType} from '@romejs/js-ast'; export default function TSMappedType(node: AnyNode, scope: Scope) { - node = tsMappedType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsMappedType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSMethodSignature.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSMethodSignature.ts index 1d04d289ece..1370b9bac56 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSMethodSignature.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSMethodSignature.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSMethodSignature, tsMethodSignature} from '@romejs/js-ast'; export default function TSMethodSignature(node: AnyNode, scope: Scope) { - node = tsMethodSignature.assert(node); - scope; - throw new Error('unimplemented'); + node = tsMethodSignature.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSModuleBlock.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSModuleBlock.ts index fe0e115406c..24905ae782c 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSModuleBlock.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSModuleBlock.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSModuleBlock, tsModuleBlock} from '@romejs/js-ast'; export default function TSModuleBlock(node: AnyNode, scope: Scope) { - node = tsModuleBlock.assert(node); - scope; - throw new Error('unimplemented'); + node = tsModuleBlock.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSModuleDeclaration.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSModuleDeclaration.ts index 9bae2c67d6b..5b8fc7828a0 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSModuleDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSModuleDeclaration.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - TSModuleDeclaration, - tsModuleDeclaration, + AnyNode, + TSModuleDeclaration, + tsModuleDeclaration, } from '@romejs/js-ast'; export default function TSModuleDeclaration(node: AnyNode, scope: Scope) { - node = tsModuleDeclaration.assert(node); - scope; - throw new Error('unimplemented'); + node = tsModuleDeclaration.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSNamespaceExportDeclaration.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSNamespaceExportDeclaration.ts index 6472904ddb7..12707909df7 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSNamespaceExportDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSNamespaceExportDeclaration.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSNamespaceExportDeclaration, - tsNamespaceExportDeclaration, + AnyNode, + TSNamespaceExportDeclaration, + tsNamespaceExportDeclaration, } from '@romejs/js-ast'; export default function TSNamespaceExportDeclaration(node: AnyNode) { - node = tsNamespaceExportDeclaration.assert(node); - throw new Error('unimplemented'); + node = tsNamespaceExportDeclaration.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSNonNullExpression.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSNonNullExpression.ts index 7c1bb1e28dc..0757ba66de7 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSNonNullExpression.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSNonNullExpression.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - TSNonNullExpression, - tsNonNullExpression, + AnyNode, + TSNonNullExpression, + tsNonNullExpression, } from '@romejs/js-ast'; export default function TSNonNullExpression(node: AnyNode, scope: Scope) { - node = tsNonNullExpression.assert(node); - scope; - throw new Error('unimplemented'); + node = tsNonNullExpression.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSOptionalType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSOptionalType.ts index ba0b2ad209f..ea9df8af8fb 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSOptionalType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSOptionalType.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSOptionalType, tsOptionalType} from '@romejs/js-ast'; export default function TSOptionalType(node: AnyNode, scope: Scope) { - node = tsOptionalType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsOptionalType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSParenthesizedType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSParenthesizedType.ts index 1ce345fd547..32168ffa71e 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSParenthesizedType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSParenthesizedType.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - TSParenthesizedType, - tsParenthesizedType, + AnyNode, + TSParenthesizedType, + tsParenthesizedType, } from '@romejs/js-ast'; export default function TSParenthesizedType(node: AnyNode, scope: Scope) { - node = tsParenthesizedType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsParenthesizedType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSPropertySignature.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSPropertySignature.ts index e9f6f274a78..96483d80a88 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSPropertySignature.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSPropertySignature.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - TSPropertySignature, - tsPropertySignature, + AnyNode, + TSPropertySignature, + tsPropertySignature, } from '@romejs/js-ast'; export default function TSPropertySignature(node: AnyNode, scope: Scope) { - node = tsPropertySignature.assert(node); - scope; - throw new Error('unimplemented'); + node = tsPropertySignature.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSQualifiedName.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSQualifiedName.ts index ef3c6b118f9..2cf54edd7ef 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSQualifiedName.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSQualifiedName.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSQualifiedName, tsQualifiedName} from '@romejs/js-ast'; export default function TSQualifiedName(node: AnyNode, scope: Scope) { - node = tsQualifiedName.assert(node); - scope; - throw new Error('unimplemented'); + node = tsQualifiedName.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSSignatureDeclarationMeta.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSSignatureDeclarationMeta.ts index df3179c2abe..5c6db05a930 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSSignatureDeclarationMeta.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSSignatureDeclarationMeta.ts @@ -7,13 +7,13 @@ import {Scope} from '../../scopes'; import { - AnyNode, - TSSignatureDeclarationMeta, - tsSignatureDeclarationMeta, + AnyNode, + TSSignatureDeclarationMeta, + tsSignatureDeclarationMeta, } from '@romejs/js-ast'; export default function TSSignatureDeclarationMeta(node: AnyNode, scope: Scope) { - node = tsSignatureDeclarationMeta.assert(node); - scope; - throw new Error('unimplemented'); + node = tsSignatureDeclarationMeta.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSThisType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSThisType.ts index f2276e9504d..5b57b17d25e 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSThisType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSThisType.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSThisType, tsThisType} from '@romejs/js-ast'; export default function TSThisType(node: AnyNode, scope: Scope) { - node = tsThisType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsThisType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSTupleType.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSTupleType.ts index c37de72ddd9..805e3728475 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSTupleType.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSTupleType.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSTupleType, tsTupleType} from '@romejs/js-ast'; export default function TSTupleType(node: AnyNode, scope: Scope) { - node = tsTupleType.assert(node); - scope; - throw new Error('unimplemented'); + node = tsTupleType.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeAssertion.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeAssertion.ts index 0e7b1e2f826..fb15070b367 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeAssertion.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeAssertion.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSTypeAssertion, tsTypeAssertion} from '@romejs/js-ast'; export default function TSTypeAssertion(node: AnyNode, scope: Scope) { - node = tsTypeAssertion.assert(node); - scope; - throw new Error('unimplemented'); + node = tsTypeAssertion.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeLiteral.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeLiteral.ts index eee354ce513..6990a7d82b4 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeLiteral.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeLiteral.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSTypeLiteral, tsTypeLiteral} from '@romejs/js-ast'; export default function TSTypeLiteral(node: AnyNode, scope: Scope) { - node = tsTypeLiteral.assert(node); - scope; - throw new Error('unimplemented'); + node = tsTypeLiteral.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeOperator.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeOperator.ts index 5f3059899a9..0655cde6456 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeOperator.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeOperator.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSTypeOperator, tsTypeOperator} from '@romejs/js-ast'; export default function TSTypeOperator(node: AnyNode, scope: Scope) { - node = tsTypeOperator.assert(node); - scope; - throw new Error('unimplemented'); + node = tsTypeOperator.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameter.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameter.ts index 22ec8d7b526..16193220510 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameter.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameter.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSTypeParameter, tsTypeParameter} from '@romejs/js-ast'; export default function TSTypeParameter(node: AnyNode, scope: Scope) { - node = tsTypeParameter.assert(node); - scope; - throw new Error('unimplemented'); + node = tsTypeParameter.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameterDeclaration.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameterDeclaration.ts index c685519e6ab..4f8df7240bf 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameterDeclaration.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameterDeclaration.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSTypeParameterDeclaration, - tsTypeParameterDeclaration, + AnyNode, + TSTypeParameterDeclaration, + tsTypeParameterDeclaration, } from '@romejs/js-ast'; export default function TSTypeParameterDeclaration(node: AnyNode) { - node = tsTypeParameterDeclaration.assert(node); - throw new Error('unimplemented'); + node = tsTypeParameterDeclaration.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameterInstantiation.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameterInstantiation.ts index 8f5c1374605..443fafedc2b 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameterInstantiation.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeParameterInstantiation.ts @@ -6,12 +6,12 @@ */ import { - AnyNode, - TSTypeParameterInstantiation, - tsTypeParameterInstantiation, + AnyNode, + TSTypeParameterInstantiation, + tsTypeParameterInstantiation, } from '@romejs/js-ast'; export default function TSTypeParameterInstantiation(node: AnyNode) { - node = tsTypeParameterInstantiation.assert(node); - throw new Error('unimplemented'); + node = tsTypeParameterInstantiation.assert(node); + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSTypePredicate.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSTypePredicate.ts index 3fc89ec7b45..afacad0624c 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSTypePredicate.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSTypePredicate.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSTypePredicate, tsTypePredicate} from '@romejs/js-ast'; export default function TSTypePredicate(node: AnyNode, scope: Scope) { - node = tsTypePredicate.assert(node); - scope; - throw new Error('unimplemented'); + node = tsTypePredicate.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeQuery.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeQuery.ts index 87f84332542..40bda7b87c1 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeQuery.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeQuery.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSTypeQuery, tsTypeQuery} from '@romejs/js-ast'; export default function TSTypeQuery(node: AnyNode, scope: Scope) { - node = tsTypeQuery.assert(node); - scope; - throw new Error('unimplemented'); + node = tsTypeQuery.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeReference.ts b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeReference.ts index 8cf97ce4f4a..9746bf05ee1 100644 --- a/packages/@romejs/js-analysis/evaluators/typescript/TSTypeReference.ts +++ b/packages/@romejs/js-analysis/evaluators/typescript/TSTypeReference.ts @@ -9,7 +9,7 @@ import {Scope} from '../../scopes'; import {AnyNode, TSTypeReference, tsTypeReference} from '@romejs/js-ast'; export default function TSTypeReference(node: AnyNode, scope: Scope) { - node = tsTypeReference.assert(node); - scope; - throw new Error('unimplemented'); + node = tsTypeReference.assert(node); + scope; + throw new Error('unimplemented'); } diff --git a/packages/@romejs/js-analysis/scopes.ts b/packages/@romejs/js-analysis/scopes.ts index f44ab641043..a2b2d5078dc 100644 --- a/packages/@romejs/js-analysis/scopes.ts +++ b/packages/@romejs/js-analysis/scopes.ts @@ -20,179 +20,179 @@ import {Class} from '@romejs/typescript-helpers'; type BindingStatus = 'declared' | 'initialized'; type Binding = { - type: T; - status: BindingStatus; + type: T; + status: BindingStatus; }; export type ScopeOptions = { - evaluator?: Evaluator; - parentScope?: Scope; + evaluator?: Evaluator; + parentScope?: Scope; }; export class Scope { - constructor(opts: ScopeOptions) { - let {evaluator, parentScope} = opts; - if (evaluator === undefined && parentScope !== undefined) { - evaluator = parentScope.evaluator; - } - - if (evaluator === undefined) { - throw new Error('No evaluator was passed or inferred'); - } - - this.intrinsics = evaluator.intrinsics; - this.evaluator = evaluator; - this.hub = evaluator.hub; - this.parentScope = parentScope; - - this.bindings = new Map(); - } - - hub: Hub; - intrinsics: Intrinsics; - evaluator: Evaluator; - parentScope: undefined | Scope; - bindings: Map; - - getBinding(name: string): undefined | T { - let scope: undefined | Scope = this; - while (scope) { - const binding = scope.bindings.get(name); - if (binding) { - return binding.type; - } - scope = scope.parentScope; - } - return undefined; - } - - getBindingAssert(name: string): T { - const binding = this.getBinding(name); - if (binding === undefined) { - throw new Error(`Expected binding ${name}`); - } - return binding; - } - - query(paths: Array): T { - let initial = this.getBinding(paths[0]); - if (initial === undefined) { - throw new Error( - `Expected "${paths[0]}" binding, found ${JSON.stringify( - this.getBindingNames(), - )} ${this.evaluator.filename}`, - ); - } - - //invariant(initial !== undefined, `Expected "${paths[0]}" binding`); - for (let i = 1; i < paths.length; i++) { - initial = new GetPropT( - this, - undefined, - initial, - new StringLiteralT(this, undefined, paths[i]), - ); - } - - return initial; - } - - declareBinding(name: string, originNode: AnyNode) { - if (name === undefined) { - throw new Error('Expected name'); - } - this.bindings.set( - name, - { - type: new OpenT(this, originNode), - status: 'declared', - }, - ); - } - - addBinding(name: string, type: T) { - if (name === undefined) { - throw new Error('Expected name'); - } - - const existingBinding = this.bindings.get(name); - if (existingBinding !== undefined && existingBinding.status === 'declared') { - if (!(existingBinding.type instanceof OpenT)) { - throw new Error('expected OpenT'); - } - - existingBinding.type.shouldMatch(type); - } - - this.bindings.set( - name, - { - type, - status: 'initialized', - }, - ); - } - - getBindingNames(): Array { - const names: Set = new Set( - this.parentScope ? this.parentScope.getBindingNames() : [], - ); - - for (const [name] of this.bindings) { - names.add(name); - } - - return Array.from(names); - } - - getOwnBindingNames(): Array { - return Array.from(this.bindings.keys()); - } - - createUnion(types: Array, originNode?: AnyNode): T { - if (types.length === 0) { - return new UnknownT(this, originNode); - } else if (types.length === 1) { - return types[0]; - } else { - return new UnionT(this, originNode, types); - } - } - - fork() { - return new Scope({evaluator: this.evaluator, parentScope: this}); - } - - find(klass: Class): S { - const scope = this.findOptional(klass); - if (scope === undefined) { - throw new Error('Failed to find class'); - } else { - return scope; - } - } - - findOptional(klass: Class): undefined | S { - let scope: undefined | Scope = this; - - do { - if (scope instanceof klass) { - return scope; - } - - scope = scope.parentScope; - } while (scope !== undefined); - - return undefined; - } - - refine(): Scope { - return new RefineScope({evaluator: this.evaluator, parentScope: this}); - } - - evaluate(node: undefined | AnyNode): T { - return this.evaluator.evaluate(node, this); - } + constructor(opts: ScopeOptions) { + let {evaluator, parentScope} = opts; + if (evaluator === undefined && parentScope !== undefined) { + evaluator = parentScope.evaluator; + } + + if (evaluator === undefined) { + throw new Error('No evaluator was passed or inferred'); + } + + this.intrinsics = evaluator.intrinsics; + this.evaluator = evaluator; + this.hub = evaluator.hub; + this.parentScope = parentScope; + + this.bindings = new Map(); + } + + hub: Hub; + intrinsics: Intrinsics; + evaluator: Evaluator; + parentScope: undefined | Scope; + bindings: Map; + + getBinding(name: string): undefined | T { + let scope: undefined | Scope = this; + while (scope) { + const binding = scope.bindings.get(name); + if (binding) { + return binding.type; + } + scope = scope.parentScope; + } + return undefined; + } + + getBindingAssert(name: string): T { + const binding = this.getBinding(name); + if (binding === undefined) { + throw new Error(`Expected binding ${name}`); + } + return binding; + } + + query(paths: Array): T { + let initial = this.getBinding(paths[0]); + if (initial === undefined) { + throw new Error( + `Expected "${paths[0]}" binding, found ${JSON.stringify( + this.getBindingNames(), + )} ${this.evaluator.filename}`, + ); + } + + //invariant(initial !== undefined, `Expected "${paths[0]}" binding`); + for (let i = 1; i < paths.length; i++) { + initial = new GetPropT( + this, + undefined, + initial, + new StringLiteralT(this, undefined, paths[i]), + ); + } + + return initial; + } + + declareBinding(name: string, originNode: AnyNode) { + if (name === undefined) { + throw new Error('Expected name'); + } + this.bindings.set( + name, + { + type: new OpenT(this, originNode), + status: 'declared', + }, + ); + } + + addBinding(name: string, type: T) { + if (name === undefined) { + throw new Error('Expected name'); + } + + const existingBinding = this.bindings.get(name); + if (existingBinding !== undefined && existingBinding.status === 'declared') { + if (!(existingBinding.type instanceof OpenT)) { + throw new Error('expected OpenT'); + } + + existingBinding.type.shouldMatch(type); + } + + this.bindings.set( + name, + { + type, + status: 'initialized', + }, + ); + } + + getBindingNames(): Array { + const names: Set = new Set( + this.parentScope ? this.parentScope.getBindingNames() : [], + ); + + for (const [name] of this.bindings) { + names.add(name); + } + + return Array.from(names); + } + + getOwnBindingNames(): Array { + return Array.from(this.bindings.keys()); + } + + createUnion(types: Array, originNode?: AnyNode): T { + if (types.length === 0) { + return new UnknownT(this, originNode); + } else if (types.length === 1) { + return types[0]; + } else { + return new UnionT(this, originNode, types); + } + } + + fork() { + return new Scope({evaluator: this.evaluator, parentScope: this}); + } + + find(klass: Class): S { + const scope = this.findOptional(klass); + if (scope === undefined) { + throw new Error('Failed to find class'); + } else { + return scope; + } + } + + findOptional(klass: Class): undefined | S { + let scope: undefined | Scope = this; + + do { + if (scope instanceof klass) { + return scope; + } + + scope = scope.parentScope; + } while (scope !== undefined); + + return undefined; + } + + refine(): Scope { + return new RefineScope({evaluator: this.evaluator, parentScope: this}); + } + + evaluate(node: undefined | AnyNode): T { + return this.evaluator.evaluate(node, this); + } } //# @@ -200,40 +200,40 @@ export class RefineScope extends Scope {} //# type ClassScopeMeta = { - instance: OpenT; - static: OpenT; + instance: OpenT; + static: OpenT; }; export class ClassScope extends Scope { - constructor(opts: ScopeOptions, meta: ClassScopeMeta) { - super(opts); - this.meta = meta; - } + constructor(opts: ScopeOptions, meta: ClassScopeMeta) { + super(opts); + this.meta = meta; + } - meta: ClassScopeMeta; + meta: ClassScopeMeta; } //# export class ThisScope extends Scope { - constructor(opts: ScopeOptions, context: T) { - super(opts); - this.context = context; - } + constructor(opts: ScopeOptions, context: T) { + super(opts); + this.context = context; + } - context: T; + context: T; } //# type FunctionScopeMeta = { - thisContext: T; - returnType: OpenT; + thisContext: T; + returnType: OpenT; }; export class FunctionScope extends ThisScope { - constructor(opts: ScopeOptions, meta: FunctionScopeMeta) { - super(opts, meta.thisContext); - this.meta = meta; - } + constructor(opts: ScopeOptions, meta: FunctionScopeMeta) { + super(opts, meta.thisContext); + this.meta = meta; + } - meta: FunctionScopeMeta; + meta: FunctionScopeMeta; } diff --git a/packages/@romejs/js-analysis/tests/basic.test.ts b/packages/@romejs/js-analysis/tests/basic.test.ts index 975c255b37d..29a6242361b 100644 --- a/packages/@romejs/js-analysis/tests/basic.test.ts +++ b/packages/@romejs/js-analysis/tests/basic.test.ts @@ -13,35 +13,35 @@ import {parseJS} from '@romejs/js-parser'; import {createUnknownFilePath} from '@romejs/path'; async function testCheck(code: string): Promise { - const ast = parseJS({ - input: code, - sourceType: 'module', - path: createUnknownFilePath('unknown'), - }); + const ast = parseJS({ + input: code, + sourceType: 'module', + path: createUnknownFilePath('unknown'), + }); - return check({ - ast, - project: { - folder: undefined, - config: DEFAULT_PROJECT_CONFIG, - }, - provider: { - getExportTypes() { - return Promise.reject('unsupported'); - }, - }, - }); + return check({ + ast, + project: { + folder: undefined, + config: DEFAULT_PROJECT_CONFIG, + }, + provider: { + getExportTypes() { + return Promise.reject('unsupported'); + }, + }, + }); } test( - "discovers require('module') call", - async () => { - testCheck; + "discovers require('module') call", + async () => { + testCheck; - /*const diagnostics = await testCheck(` + /*const diagnostics = await testCheck(` const a: number = ''; `); console.log(diagnostics);*/ - }, + }, ); diff --git a/packages/@romejs/js-analysis/types.ts b/packages/@romejs/js-analysis/types.ts index be83ba0d56d..9020ec0c3bd 100644 --- a/packages/@romejs/js-analysis/types.ts +++ b/packages/@romejs/js-analysis/types.ts @@ -11,35 +11,35 @@ import {HydrateData} from './Evaluator'; import {Dict} from '@romejs/typescript-helpers'; export type CheckProvider = { - libs?: Array; - getExportTypes: ( - origin: string, - relative: string, - ) => Promise; + libs?: Array; + getExportTypes: ( + origin: string, + relative: string, + ) => Promise; }; export type TypeCheckProvider = CheckProvider; export type ModuleSignatureType = { - human?: string; - origin: undefined | SourceLocation; - type: string; - data: HydrateData; + human?: string; + origin: undefined | SourceLocation; + type: string; + data: HydrateData; }; export type ModuleSignatureExport = - | { - type: 'local'; - name: string; - value: string; - } - | { - type: 'all'; - source: string; - }; + | { + type: 'local'; + name: string; + value: string; + } + | { + type: 'all'; + source: string; + }; export type ModuleSignature = { - filename: string; - exports: Array; - types: Dict; + filename: string; + exports: Array; + types: Dict; }; diff --git a/packages/@romejs/js-analysis/types/AnyT.ts b/packages/@romejs/js-analysis/types/AnyT.ts index cc6fee62dad..75ed36fffd5 100644 --- a/packages/@romejs/js-analysis/types/AnyT.ts +++ b/packages/@romejs/js-analysis/types/AnyT.ts @@ -11,21 +11,21 @@ import {Scope} from '../scopes'; import T from './T'; export default class AnyT extends T { - static type = 'AnyT'; + static type = 'AnyT'; - serialize(): HydrateData { - return {}; - } + serialize(): HydrateData { + return {}; + } - static hydrate(scope: Scope, originNode: AnyNode): T { - return new AnyT(scope, originNode); - } + static hydrate(scope: Scope, originNode: AnyNode): T { + return new AnyT(scope, originNode); + } - compatibleWith(): boolean { - return true; - } + compatibleWith(): boolean { + return true; + } - humanize(): string { - return 'any'; - } + humanize(): string { + return 'any'; + } } diff --git a/packages/@romejs/js-analysis/types/BinaryOpT.ts b/packages/@romejs/js-analysis/types/BinaryOpT.ts index 217db1ef388..b1de56cd45a 100644 --- a/packages/@romejs/js-analysis/types/BinaryOpT.ts +++ b/packages/@romejs/js-analysis/types/BinaryOpT.ts @@ -17,113 +17,106 @@ import AnyT from './AnyT'; import StringLiteralT from './StringLiteralT'; function isNumber(t: T): boolean { - return t instanceof NumericT || t instanceof NumericLiteralT; + return t instanceof NumericT || t instanceof NumericLiteralT; } export default class BinaryOpT extends T { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - left: T, - operator: string, - right: T, - ) { - super(scope, originNode); - this.operator = operator; - this.left = left; - this.right = right; - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + left: T, + operator: string, + right: T, + ) { + super(scope, originNode); + this.operator = operator; + this.left = left; + this.right = right; + } - static type = 'BinaryOpT'; + static type = 'BinaryOpT'; - operator: string; - left: T; - right: T; + operator: string; + left: T; + right: T; - serialize(addType: SerialTypeFactory): HydrateData { - return { - left: addType(this.left), - right: addType(this.right), - }; - } + serialize(addType: SerialTypeFactory): HydrateData { + return { + left: addType(this.left), + right: addType(this.right), + }; + } - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new BinaryOpT( - scope, - originNode, - getType(data.left), - String(data.operator), - getType(data.right), - ); - } + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new BinaryOpT( + scope, + originNode, + getType(data.left), + String(data.operator), + getType(data.right), + ); + } - reduce(): T { - const left = this.utils.reduce(this.left); - const right = this.utils.reduce(this.right); - const {scope, originNode, operator} = this; + reduce(): T { + const left = this.utils.reduce(this.left); + const right = this.utils.reduce(this.right); + const {scope, originNode, operator} = this; - // return type - switch (operator) { - case // returns booleans - '===': - case '==': - case '!=': - case '!==': - case '<': - case '<=': - case '>': - case '>=': - case 'in': - case 'instanceof': - // TODO return BooleanLiteralT in the cases whe we have all the info - return new BooleanT(scope, originNode); + // return type + switch (operator) { + case // returns booleans + '===': + case '==': + case '!=': + case '!==': + case '<': + case '<=': + case '>': + case '>=': + case 'in': + case 'instanceof': + // TODO return BooleanLiteralT in the cases whe we have all the info + return new BooleanT(scope, originNode); - // Returns a string or a number - case '+': - if (left instanceof AnyT || right instanceof AnyT) { - return new AnyT(scope, originNode); - } else if ( - left instanceof NumericLiteralT && - right instanceof NumericLiteralT - ) { - return new NumericLiteralT( - scope, - originNode, - left.value + right.value, - ); - } else if (isNumber(left) && isNumber(right)) { - return new NumericT(scope, originNode); - } else if ( - left instanceof StringLiteralT && - right instanceof StringLiteralT - ) { - return new StringLiteralT(scope, originNode, left.value + right.value); - } else { - return new StringT(scope, originNode); - } + // Returns a string or a number + case '+': + if (left instanceof AnyT || right instanceof AnyT) { + return new AnyT(scope, originNode); + } else if ( + left instanceof NumericLiteralT && + right instanceof NumericLiteralT + ) { + return new NumericLiteralT(scope, originNode, left.value + right.value); + } else if (isNumber(left) && isNumber(right)) { + return new NumericT(scope, originNode); + } else if (left instanceof StringLiteralT && right instanceof StringLiteralT) { + return new StringLiteralT(scope, originNode, left.value + right.value); + } else { + return new StringT(scope, originNode); + } - // returns a number - case '<<': - case '>>': - case '>>>': - case '-': - case '*': - case '/': - case '%': - case '**': - case '|': - case '^': - case '&': - // TODO return NumericLiteralT if left/right are literals too - return new NumericT(scope, originNode); + // returns a number + case '<<': + case '>>': + case '>>>': + case '-': + case '*': + case '/': + case '%': + case '**': + case '|': + case '^': + case '&': + // TODO return NumericLiteralT if left/right are literals too + return new NumericT(scope, originNode); - default: - throw new Error('Unknown operator'); - } - } + default: + throw new Error('Unknown operator'); + } + } } diff --git a/packages/@romejs/js-analysis/types/BlockT.ts b/packages/@romejs/js-analysis/types/BlockT.ts index 5c2dae41be6..7f24dedf632 100644 --- a/packages/@romejs/js-analysis/types/BlockT.ts +++ b/packages/@romejs/js-analysis/types/BlockT.ts @@ -10,35 +10,35 @@ import {Scope} from '../scopes'; import T from './T'; export default class BlockT extends T { - constructor(scope: Scope, originNode: undefined | AnyNode, body: Array) { - super(scope, originNode); - this.body = body; - } - - static type = 'BlockT'; - - body: Array; - - reduce(): T { - const body = []; - let changed = false; - - for (const type of this.body) { - const reduced = this.utils.reduce(type); - body.push(reduced); - if (reduced !== type) { - changed = true; - } - } - - if (changed) { - return new BlockT(this.scope, this.originNode, body); - } else { - return this; - } - } - - humanize(): string { - return '{}'; - } + constructor(scope: Scope, originNode: undefined | AnyNode, body: Array) { + super(scope, originNode); + this.body = body; + } + + static type = 'BlockT'; + + body: Array; + + reduce(): T { + const body = []; + let changed = false; + + for (const type of this.body) { + const reduced = this.utils.reduce(type); + body.push(reduced); + if (reduced !== type) { + changed = true; + } + } + + if (changed) { + return new BlockT(this.scope, this.originNode, body); + } else { + return this; + } + } + + humanize(): string { + return '{}'; + } } diff --git a/packages/@romejs/js-analysis/types/BooleanLiteralT.ts b/packages/@romejs/js-analysis/types/BooleanLiteralT.ts index 0ed73ec0ab1..9467158a0c2 100644 --- a/packages/@romejs/js-analysis/types/BooleanLiteralT.ts +++ b/packages/@romejs/js-analysis/types/BooleanLiteralT.ts @@ -11,36 +11,36 @@ import {Scope} from '../scopes'; import T from './T'; export default class BooleanLiteralT extends T { - constructor(scope: Scope, originNode: undefined | AnyNode, value: boolean) { - super(scope, originNode); - this.value = value; - } - - static type = 'BooleanLiteralT'; - - value: boolean; - - serialize(): HydrateData { - return {value: this.value}; - } - - static hydrate( - scope: Scope, - originNode: undefined | AnyNode, - data: HydrateData, - ): T { - return new BooleanLiteralT(scope, originNode, Boolean(data.value)); - } - - humanize(): string { - if (this.value === true) { - return 'true'; - } else { - return 'false'; - } - } - - compatibleWith(type: T): boolean { - return type instanceof BooleanLiteralT && type.value === this.value; - } + constructor(scope: Scope, originNode: undefined | AnyNode, value: boolean) { + super(scope, originNode); + this.value = value; + } + + static type = 'BooleanLiteralT'; + + value: boolean; + + serialize(): HydrateData { + return {value: this.value}; + } + + static hydrate( + scope: Scope, + originNode: undefined | AnyNode, + data: HydrateData, + ): T { + return new BooleanLiteralT(scope, originNode, Boolean(data.value)); + } + + humanize(): string { + if (this.value === true) { + return 'true'; + } else { + return 'false'; + } + } + + compatibleWith(type: T): boolean { + return type instanceof BooleanLiteralT && type.value === this.value; + } } diff --git a/packages/@romejs/js-analysis/types/BooleanT.ts b/packages/@romejs/js-analysis/types/BooleanT.ts index 46a904cf9e5..e5f67dce621 100644 --- a/packages/@romejs/js-analysis/types/BooleanT.ts +++ b/packages/@romejs/js-analysis/types/BooleanT.ts @@ -12,22 +12,22 @@ import {Scope} from '../scopes'; import T from './T'; export default class BooleanT extends T { - static type = 'BooleanT'; + static type = 'BooleanT'; - serialize(): HydrateData { - return {}; - } + serialize(): HydrateData { + return {}; + } - static hydrate(scope: Scope, originNode: undefined | AnyNode): T { - return new BooleanT(scope, originNode); - } + static hydrate(scope: Scope, originNode: undefined | AnyNode): T { + return new BooleanT(scope, originNode); + } - humanize(): string { - return 'boolean'; - } + humanize(): string { + return 'boolean'; + } - compatibleWith(type: T): boolean { - // A boolean literal can flow into a generic boolean - return type instanceof BooleanT || type instanceof BooleanLiteralT; - } + compatibleWith(type: T): boolean { + // A boolean literal can flow into a generic boolean + return type instanceof BooleanT || type instanceof BooleanLiteralT; + } } diff --git a/packages/@romejs/js-analysis/types/CallT.ts b/packages/@romejs/js-analysis/types/CallT.ts index 101ea33404f..b3905872969 100644 --- a/packages/@romejs/js-analysis/types/CallT.ts +++ b/packages/@romejs/js-analysis/types/CallT.ts @@ -15,34 +15,34 @@ import E from './errors/E'; import T from './T'; export default class CallT extends T { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - callee: T, - args: Array, - ) { - super(scope, originNode); - this.callee = callee; - this.args = args; - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + callee: T, + args: Array, + ) { + super(scope, originNode); + this.callee = callee; + this.args = args; + } - static type = 'CallT'; + static type = 'CallT'; - callee: T; - args: Array; + callee: T; + args: Array; - reduce(): T { - let callee = this.utils.reduce(this.callee); - if (callee instanceof ObjT && callee.calls.length) { - callee = this.utils.reduce(callee.calls[0]); - } + reduce(): T { + let callee = this.utils.reduce(this.callee); + if (callee instanceof ObjT && callee.calls.length) { + callee = this.utils.reduce(callee.calls[0]); + } - if (callee instanceof AnyT || callee instanceof E) { - return new AnyT(this.scope, this.originNode); - } else if (callee instanceof FunctionT) { - return this.utils.reduce(callee.returns); - } else { - return new NotCallableE(this.scope, this.originNode, this.callee); - } - } + if (callee instanceof AnyT || callee instanceof E) { + return new AnyT(this.scope, this.originNode); + } else if (callee instanceof FunctionT) { + return this.utils.reduce(callee.returns); + } else { + return new NotCallableE(this.scope, this.originNode, this.callee); + } + } } diff --git a/packages/@romejs/js-analysis/types/ClassT.ts b/packages/@romejs/js-analysis/types/ClassT.ts index e6e19b4a2e6..b9b7e28ff79 100644 --- a/packages/@romejs/js-analysis/types/ClassT.ts +++ b/packages/@romejs/js-analysis/types/ClassT.ts @@ -16,106 +16,106 @@ import OpenT from './OpenT'; import ObjT from './ObjT'; export default class ClassT extends ObjT { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - opts: { - _constructor: undefined | T; - statics: Array; - instances: Array; - extends?: T; - calls?: Array; - }, - ) { - // point `class.prototype.__proto__` to `superClass.prototype` - let protoProp = undefined; - if (opts.extends) { - const originNode = opts.extends.originNode; - protoProp = new GetPropT( - scope, - originNode, - opts.extends, - new StringLiteralT(scope, originNode, 'prototype'), - ); - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + opts: { + _constructor: undefined | T; + statics: Array; + instances: Array; + extends?: T; + calls?: Array; + }, + ) { + // point `class.prototype.__proto__` to `superClass.prototype` + let protoProp = undefined; + if (opts.extends) { + const originNode = opts.extends.originNode; + protoProp = new GetPropT( + scope, + originNode, + opts.extends, + new StringLiteralT(scope, originNode, 'prototype'), + ); + } - // create `class.prototype.constructor` - const constructorOpen = new OpenT(scope, undefined); - const constructorProp = new ObjPropT( - scope, - undefined, - 'constructor', - constructorOpen, - ); - const instances = [...opts.instances, constructorProp]; + // create `class.prototype.constructor` + const constructorOpen = new OpenT(scope, undefined); + const constructorProp = new ObjPropT( + scope, + undefined, + 'constructor', + constructorOpen, + ); + const instances = [...opts.instances, constructorProp]; - // create `class.prototype` - const protoObj = new ObjT( - scope, - originNode, - { - props: instances, - proto: protoProp, - calls: [], - }, - ); + // create `class.prototype` + const protoObj = new ObjT( + scope, + originNode, + { + props: instances, + proto: protoProp, + calls: [], + }, + ); - super( - scope, - originNode, - { - props: [ - ...opts.statics, - new ObjPropT(scope, originNode, 'prototype', protoObj), - ], - proto: opts.extends, - calls: opts.calls === undefined ? [] : opts.calls, - }, - ); + super( + scope, + originNode, + { + props: [ + ...opts.statics, + new ObjPropT(scope, originNode, 'prototype', protoObj), + ], + proto: opts.extends, + calls: opts.calls === undefined ? [] : opts.calls, + }, + ); - constructorOpen.shouldMatch(this); + constructorOpen.shouldMatch(this); - this._constructor = opts._constructor; - this._statics = opts.statics; - this._instances = opts.instances; - this._extends = opts.extends; - } + this._constructor = opts._constructor; + this._statics = opts.statics; + this._instances = opts.instances; + this._extends = opts.extends; + } - static type = 'ClassT'; + static type = 'ClassT'; - _statics: Array; - _instances: Array; - _extends: undefined | T; - _constructor: undefined | T; + _statics: Array; + _instances: Array; + _extends: undefined | T; + _constructor: undefined | T; - serialize(addType: SerialTypeFactory): HydrateData { - return { - constructor: this._constructor === undefined - ? undefined - : addType(this._constructor), - statics: this._statics.map((type) => addType(type)), - instances: this._instances.map((type) => addType(type)), - extends: this._extends === undefined ? undefined : addType(this._extends), - }; - } + serialize(addType: SerialTypeFactory): HydrateData { + return { + constructor: this._constructor === undefined + ? undefined + : addType(this._constructor), + statics: this._statics.map((type) => addType(type)), + instances: this._instances.map((type) => addType(type)), + extends: this._extends === undefined ? undefined : addType(this._extends), + }; + } - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new ClassT( - scope, - originNode, - { - _constructor: data.constructor === undefined - ? undefined - : getType(data.constructor), - statics: Array(data.statics).map((id) => getType(id)), - instances: Array(data.instances).map((id) => getType(id)), - extends: data.extends === undefined ? undefined : getType(data.extends), - }, - ); - } + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new ClassT( + scope, + originNode, + { + _constructor: data.constructor === undefined + ? undefined + : getType(data.constructor), + statics: Array(data.statics).map((id) => getType(id)), + instances: Array(data.instances).map((id) => getType(id)), + extends: data.extends === undefined ? undefined : getType(data.extends), + }, + ); + } } diff --git a/packages/@romejs/js-analysis/types/EmptyT.ts b/packages/@romejs/js-analysis/types/EmptyT.ts index c825e72517e..123c8e8984a 100644 --- a/packages/@romejs/js-analysis/types/EmptyT.ts +++ b/packages/@romejs/js-analysis/types/EmptyT.ts @@ -12,21 +12,21 @@ import VoidT from './VoidT'; import T from './T'; export default class EmptyT extends T { - static type = 'EmptyT'; + static type = 'EmptyT'; - serialize(): HydrateData { - return {}; - } + serialize(): HydrateData { + return {}; + } - static hydrate(scope: Scope, originNode: undefined | AnyNode): T { - return new EmptyT(scope, originNode); - } + static hydrate(scope: Scope, originNode: undefined | AnyNode): T { + return new EmptyT(scope, originNode); + } - humanize(): string { - return 'empty'; - } + humanize(): string { + return 'empty'; + } - compatibleWith(otherType: T): boolean { - return otherType instanceof EmptyT || otherType instanceof VoidT; - } + compatibleWith(otherType: T): boolean { + return otherType instanceof EmptyT || otherType instanceof VoidT; + } } diff --git a/packages/@romejs/js-analysis/types/ExhaustiveT.ts b/packages/@romejs/js-analysis/types/ExhaustiveT.ts index e4bbd5d4fc0..67174f572a4 100644 --- a/packages/@romejs/js-analysis/types/ExhaustiveT.ts +++ b/packages/@romejs/js-analysis/types/ExhaustiveT.ts @@ -15,110 +15,110 @@ import AnyT from './AnyT'; import {descriptions} from '@romejs/diagnostics'; class ENotExhaustive extends E { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - target: T, - only: T, - extraenous: Array, - ) { - super(scope, originNode); - this.target = target; - this.only = only; - this.extraenous = extraenous; - } - - target: T; - only: T; - extraenous: Array; - - static type = 'ENotExhaustive'; - - getError(): ErrorDefinition { - return { - description: descriptions.TYPE_CHECK.NOT_EXHAUSTIVE( - this.utils.humanize(this.only), - this.utils.humanize(this.target), - ), - lowerTarget: this.target, - }; - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + target: T, + only: T, + extraenous: Array, + ) { + super(scope, originNode); + this.target = target; + this.only = only; + this.extraenous = extraenous; + } + + target: T; + only: T; + extraenous: Array; + + static type = 'ENotExhaustive'; + + getError(): ErrorDefinition { + return { + description: descriptions.TYPE_CHECK.NOT_EXHAUSTIVE( + this.utils.humanize(this.only), + this.utils.humanize(this.target), + ), + lowerTarget: this.target, + }; + } } export default class ExhaustiveT extends T { - constructor(scope: Scope, originNode: undefined | AnyNode, target: T, only: T) { - super(scope, originNode); - this.target = target; - this.only = only; - } - - target: T; - only: T; - - static type = 'ExhaustiveT'; - - serialize(addType: SerialTypeFactory): HydrateData { - return { - target: addType(this.target), - only: addType(this.only), - }; - } - - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new ExhaustiveT( - scope, - originNode, - getType(data.target), - getType(data.only), - ); - } - - reduce(): T { - const target = this.utils.reduce(this.target); - const only = this.utils.reduce(this.only); - if (target instanceof AnyT || only instanceof AnyT) { - return this.only; - } - - const targetCandidates = this.utils.explodeUnion(target); - const onlyCandidates = this.utils.explodeUnion(only); - - const extraneous = []; - for (const possible of targetCandidates) { - let compatible = false; - - for (const otherType of onlyCandidates) { - if (this.utils.isCompatibleWith(possible, otherType)) { - compatible = true; - } - } - - if (compatible === false) { - extraneous.push(possible); - } - } - - if (extraneous.length === 0) { - return target; - } else { - return new ENotExhaustive( - this.scope, - this.originNode, - this.target, - this.only, - extraneous, - ); - } - } - - humanize(builder: HumanBuilder): string { - return `exhaustive ${builder.humanize(this.target)} should only match ${builder.humanize( - this.target, - )}`; - } + constructor(scope: Scope, originNode: undefined | AnyNode, target: T, only: T) { + super(scope, originNode); + this.target = target; + this.only = only; + } + + target: T; + only: T; + + static type = 'ExhaustiveT'; + + serialize(addType: SerialTypeFactory): HydrateData { + return { + target: addType(this.target), + only: addType(this.only), + }; + } + + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new ExhaustiveT( + scope, + originNode, + getType(data.target), + getType(data.only), + ); + } + + reduce(): T { + const target = this.utils.reduce(this.target); + const only = this.utils.reduce(this.only); + if (target instanceof AnyT || only instanceof AnyT) { + return this.only; + } + + const targetCandidates = this.utils.explodeUnion(target); + const onlyCandidates = this.utils.explodeUnion(only); + + const extraneous = []; + for (const possible of targetCandidates) { + let compatible = false; + + for (const otherType of onlyCandidates) { + if (this.utils.isCompatibleWith(possible, otherType)) { + compatible = true; + } + } + + if (compatible === false) { + extraneous.push(possible); + } + } + + if (extraneous.length === 0) { + return target; + } else { + return new ENotExhaustive( + this.scope, + this.originNode, + this.target, + this.only, + extraneous, + ); + } + } + + humanize(builder: HumanBuilder): string { + return `exhaustive ${builder.humanize(this.target)} should only match ${builder.humanize( + this.target, + )}`; + } } diff --git a/packages/@romejs/js-analysis/types/FunctionT.ts b/packages/@romejs/js-analysis/types/FunctionT.ts index a044be07e75..e9db43b66d1 100644 --- a/packages/@romejs/js-analysis/types/FunctionT.ts +++ b/packages/@romejs/js-analysis/types/FunctionT.ts @@ -13,102 +13,102 @@ import {HumanBuilder} from '../Utils'; import ObjT from './ObjT'; export default class FunctionT extends ObjT { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - opts: { - params: Array; - rest: undefined | T; - returns: T; - props?: Array; - proto?: T; - body?: T; - }, - ) { - super( - scope, - originNode, - { - props: opts.props, - proto: opts.proto, - calls: [], - }, - ); - this.params = opts.params; - this.rest = opts.rest; - this.returns = opts.returns; - this.body = opts.body; - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + opts: { + params: Array; + rest: undefined | T; + returns: T; + props?: Array; + proto?: T; + body?: T; + }, + ) { + super( + scope, + originNode, + { + props: opts.props, + proto: opts.proto, + calls: [], + }, + ); + this.params = opts.params; + this.rest = opts.rest; + this.returns = opts.returns; + this.body = opts.body; + } - static type = 'FunctionT'; + static type = 'FunctionT'; - params: Array; - rest: undefined | T; - returns: T; - body: undefined | T; + params: Array; + rest: undefined | T; + returns: T; + body: undefined | T; - serialize(addType: SerialTypeFactory): HydrateData { - return { - params: this.params.map((type) => addType(type)), - rest: this.rest ? addType(this.rest) : undefined, - returns: addType(this.returns), - proto: this.proto === undefined ? undefined : addType(this.proto), - body: this.body === undefined ? undefined : addType(this.body), - props: this.props.map((type) => addType(type)), - }; - } + serialize(addType: SerialTypeFactory): HydrateData { + return { + params: this.params.map((type) => addType(type)), + rest: this.rest ? addType(this.rest) : undefined, + returns: addType(this.returns), + proto: this.proto === undefined ? undefined : addType(this.proto), + body: this.body === undefined ? undefined : addType(this.body), + props: this.props.map((type) => addType(type)), + }; + } - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new FunctionT( - scope, - originNode, - { - params: Array(data.params).map((id) => getType(id)), - rest: data.rest === undefined ? undefined : getType(data.rest), - returns: getType(data.returns), - props: Array(data.props).map((id) => getType(id)), - proto: data.proto === undefined ? undefined : getType(data.proto), - body: data.body === undefined ? undefined : getType(data.body), - }, - ); - } + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new FunctionT( + scope, + originNode, + { + params: Array(data.params).map((id) => getType(id)), + rest: data.rest === undefined ? undefined : getType(data.rest), + returns: getType(data.returns), + props: Array(data.props).map((id) => getType(id)), + proto: data.proto === undefined ? undefined : getType(data.proto), + body: data.body === undefined ? undefined : getType(data.body), + }, + ); + } - humanize(builder: HumanBuilder): string { - return `(${this.params.map((param) => builder.humanize(param)).join(', ')}) => ${builder.humanize( - this.returns, - )}`; - } + humanize(builder: HumanBuilder): string { + return `(${this.params.map((param) => builder.humanize(param)).join(', ')}) => ${builder.humanize( + this.returns, + )}`; + } - reduce(): T { - // No body, just a type signature - const {body} = this; - if (body === undefined) { - return this; - } + reduce(): T { + // No body, just a type signature + const {body} = this; + if (body === undefined) { + return this; + } - // Reduce the body and create a new function - const reducedBody = this.utils.reduce(body); - if (reducedBody !== body) { - return new FunctionT( - this.scope, - this.originNode, - { - params: this.params, - rest: this.rest, - returns: this.returns, - props: this.props, - proto: this.proto, - body: reducedBody, - }, - ); - } + // Reduce the body and create a new function + const reducedBody = this.utils.reduce(body); + if (reducedBody !== body) { + return new FunctionT( + this.scope, + this.originNode, + { + params: this.params, + rest: this.rest, + returns: this.returns, + props: this.props, + proto: this.proto, + body: reducedBody, + }, + ); + } - // Already been reduced - return this; - } + // Already been reduced + return this; + } } diff --git a/packages/@romejs/js-analysis/types/GenericT.ts b/packages/@romejs/js-analysis/types/GenericT.ts index 062071ff36d..9a15f32f58f 100644 --- a/packages/@romejs/js-analysis/types/GenericT.ts +++ b/packages/@romejs/js-analysis/types/GenericT.ts @@ -13,53 +13,48 @@ import ClassT from './ClassT'; import InstanceT from './InstanceT'; export default class GenericT extends T { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - name: string, - type: T, - ) { - super(scope, originNode); - this.name = name; - this.type = type; - } - - name: string; - type: T; - - static type = 'GenericT'; - - serialize(addType: SerialTypeFactory): HydrateData { - return { - name: this.name, - type: addType(this.type), - }; - } - - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new GenericT( - scope, - originNode, - String(data.name), - getType(data.type), - ); - } - - humanize(): string { - return this.name; - } - - reduce(): T { - const type = this.utils.reduce(this.type); - if (type instanceof ClassT) { - return new InstanceT(this.scope, this.originNode, this.type, []); - } else { - return type; - } - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + name: string, + type: T, + ) { + super(scope, originNode); + this.name = name; + this.type = type; + } + + name: string; + type: T; + + static type = 'GenericT'; + + serialize(addType: SerialTypeFactory): HydrateData { + return { + name: this.name, + type: addType(this.type), + }; + } + + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new GenericT(scope, originNode, String(data.name), getType(data.type)); + } + + humanize(): string { + return this.name; + } + + reduce(): T { + const type = this.utils.reduce(this.type); + if (type instanceof ClassT) { + return new InstanceT(this.scope, this.originNode, this.type, []); + } else { + return type; + } + } } diff --git a/packages/@romejs/js-analysis/types/GetPropT.ts b/packages/@romejs/js-analysis/types/GetPropT.ts index 898ec4e93f8..f32ac2a2aba 100644 --- a/packages/@romejs/js-analysis/types/GetPropT.ts +++ b/packages/@romejs/js-analysis/types/GetPropT.ts @@ -19,128 +19,128 @@ import ObjT from './ObjT'; import E from './errors/E'; export default class GetPropT extends T { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - object: T, - property: T, - ) { - super(scope, originNode); - this.object = object; - this.property = property; - } - - static type = 'GetPropT'; - - object: T; - property: T; - - serialize(addType: SerialTypeFactory): HydrateData { - return { - object: addType(this.object), - property: addType(this.property), - }; - } - - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new GetPropT( - scope, - originNode, - getType(data.object), - getType(data.property), - ); - } - - lookup( - object: T, - property: T, - opts: { - topObject?: T; - protoKeys?: Array; - } = {}, - ): T { - object = this.utils.reduce(object); - property = this.utils.reduce(property); - - const thisKeys: Set = new Set(); - - // - const protoKeys = opts.protoKeys === undefined ? [] : opts.protoKeys; - const topObject = opts.topObject === undefined ? object : opts.topObject; - - // turn property into string key - let key: undefined | string; - if (property instanceof StringLiteralT) { - key = property.value; - } - - // look up on object - if (key !== undefined && object instanceof ObjT) { - // - const indexers: Array = []; - for (const maybePropRaw of object.props) { - const maybeProp = this.utils.reduce(maybePropRaw); - if (maybeProp instanceof ObjPropT) { - if (maybeProp.key === key) { - // TODO collate these in case there's multiple properties of this name - return this.utils.reduce(maybeProp.value); - } else { - thisKeys.add(maybeProp.key); - } - } else if (maybeProp instanceof ObjIndexPropT) { - indexers.push(maybeProp); - } - } - - // - for (const indexer of indexers) { - if (this.utils.isCompatibleWith(indexer.key, property)) { - return this.utils.reduce(indexer.value); - } - } - - // - if (object.proto) { - return this.lookup( - object.proto, - property, - { - topObject, - protoKeys: [...protoKeys, ...thisKeys], - }, - ); - } - } - - // property lookups on an `any` return `any`! - if (object instanceof AnyT || object instanceof E) { - return new AnyT(this.scope, this.originNode); - } - - // - if (typeof key === 'string') { - return new UnknownPropE( - this.scope, - this.originNode, - { - object: topObject, - property, - key, - thisKeys: Array.from(thisKeys), - protoKeys, - }, - ); - } else { - return new UnknownT(this.scope, this.originNode); - } - } - - reduce(): T { - return this.lookup(this.object, this.property); - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + object: T, + property: T, + ) { + super(scope, originNode); + this.object = object; + this.property = property; + } + + static type = 'GetPropT'; + + object: T; + property: T; + + serialize(addType: SerialTypeFactory): HydrateData { + return { + object: addType(this.object), + property: addType(this.property), + }; + } + + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new GetPropT( + scope, + originNode, + getType(data.object), + getType(data.property), + ); + } + + lookup( + object: T, + property: T, + opts: { + topObject?: T; + protoKeys?: Array; + } = {}, + ): T { + object = this.utils.reduce(object); + property = this.utils.reduce(property); + + const thisKeys: Set = new Set(); + + // + const protoKeys = opts.protoKeys === undefined ? [] : opts.protoKeys; + const topObject = opts.topObject === undefined ? object : opts.topObject; + + // turn property into string key + let key: undefined | string; + if (property instanceof StringLiteralT) { + key = property.value; + } + + // look up on object + if (key !== undefined && object instanceof ObjT) { + // + const indexers: Array = []; + for (const maybePropRaw of object.props) { + const maybeProp = this.utils.reduce(maybePropRaw); + if (maybeProp instanceof ObjPropT) { + if (maybeProp.key === key) { + // TODO collate these in case there's multiple properties of this name + return this.utils.reduce(maybeProp.value); + } else { + thisKeys.add(maybeProp.key); + } + } else if (maybeProp instanceof ObjIndexPropT) { + indexers.push(maybeProp); + } + } + + // + for (const indexer of indexers) { + if (this.utils.isCompatibleWith(indexer.key, property)) { + return this.utils.reduce(indexer.value); + } + } + + // + if (object.proto) { + return this.lookup( + object.proto, + property, + { + topObject, + protoKeys: [...protoKeys, ...thisKeys], + }, + ); + } + } + + // property lookups on an `any` return `any`! + if (object instanceof AnyT || object instanceof E) { + return new AnyT(this.scope, this.originNode); + } + + // + if (typeof key === 'string') { + return new UnknownPropE( + this.scope, + this.originNode, + { + object: topObject, + property, + key, + thisKeys: Array.from(thisKeys), + protoKeys, + }, + ); + } else { + return new UnknownT(this.scope, this.originNode); + } + } + + reduce(): T { + return this.lookup(this.object, this.property); + } } diff --git a/packages/@romejs/js-analysis/types/ImportT.ts b/packages/@romejs/js-analysis/types/ImportT.ts index b26dff5aef7..e1c120223c9 100644 --- a/packages/@romejs/js-analysis/types/ImportT.ts +++ b/packages/@romejs/js-analysis/types/ImportT.ts @@ -12,93 +12,93 @@ import {Scope} from '../scopes'; import T from './T'; export default class ImportT extends T { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - opts: { - importedName: undefined | string; - relative?: string; - source: string; - }, - ) { - super(scope, originNode); - this.importedName = opts.importedName; - this.relative = - opts.relative === undefined ? scope.evaluator.filename : opts.relative; - this.source = opts.source; - this.absolute = undefined; - this.resolvedType = undefined; - scope.evaluator.addImport( - this, - { - importedName: this.importedName, - relative: this.relative, - source: this.source, - }, - ); - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + opts: { + importedName: undefined | string; + relative?: string; + source: string; + }, + ) { + super(scope, originNode); + this.importedName = opts.importedName; + this.relative = + opts.relative === undefined ? scope.evaluator.filename : opts.relative; + this.source = opts.source; + this.absolute = undefined; + this.resolvedType = undefined; + scope.evaluator.addImport( + this, + { + importedName: this.importedName, + relative: this.relative, + source: this.source, + }, + ); + } - static type = 'ImportT'; - importedName: undefined | string; - absolute: undefined | string; - resolvedType: undefined | T; - relative: string; - source: string; + static type = 'ImportT'; + importedName: undefined | string; + absolute: undefined | string; + resolvedType: undefined | T; + relative: string; + source: string; - setAbsolute(absolute: undefined | string) { - this.absolute = absolute; - } + setAbsolute(absolute: undefined | string) { + this.absolute = absolute; + } - setResolvedType(resolvedType: T) { - this.resolvedType = resolvedType; - } + setResolvedType(resolvedType: T) { + this.resolvedType = resolvedType; + } - serialize(): HydrateData { - return { - importedName: this.importedName, - relative: this.relative, - source: this.source, - }; - } + serialize(): HydrateData { + return { + importedName: this.importedName, + relative: this.relative, + source: this.source, + }; + } - static hydrate( - scope: Scope, - originNode: undefined | AnyNode, - data: HydrateData, - ): T { - return new ImportT( - scope, - originNode, - { - importedName: String(data.importedName), - source: String(data.source), - relative: String(data.relative), - }, - ); - } + static hydrate( + scope: Scope, + originNode: undefined | AnyNode, + data: HydrateData, + ): T { + return new ImportT( + scope, + originNode, + { + importedName: String(data.importedName), + source: String(data.source), + relative: String(data.relative), + }, + ); + } - humanize(builder: HumanBuilder): string { - let object; - if (this.resolvedType !== undefined) { - object = builder.humanize(this.resolvedType); - } else if (this.absolute === undefined) { - object = `$Exports<"${this.source}", "${this.relative}">`; - } else { - object = `$Exports<"${this.absolute}">`; - } + humanize(builder: HumanBuilder): string { + let object; + if (this.resolvedType !== undefined) { + object = builder.humanize(this.resolvedType); + } else if (this.absolute === undefined) { + object = `$Exports<"${this.source}", "${this.relative}">`; + } else { + object = `$Exports<"${this.absolute}">`; + } - if (this.importedName === undefined) { - return object; - } else { - return `${object}.${this.importedName}`; - } - } + if (this.importedName === undefined) { + return object; + } else { + return `${object}.${this.importedName}`; + } + } - reduce(): T { - if (this.resolvedType === undefined) { - return this; - } else { - return this.resolvedType; - } - } + reduce(): T { + if (this.resolvedType === undefined) { + return this; + } else { + return this.resolvedType; + } + } } diff --git a/packages/@romejs/js-analysis/types/InstanceT.ts b/packages/@romejs/js-analysis/types/InstanceT.ts index cb591b7f5ea..6412df937fa 100644 --- a/packages/@romejs/js-analysis/types/InstanceT.ts +++ b/packages/@romejs/js-analysis/types/InstanceT.ts @@ -15,74 +15,74 @@ import GetPropT from './GetPropT'; import ObjT from './ObjT'; export default class InstanceT extends ObjT { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - target: T, - typeParameters: Array, - ) { - const prototype = new GetPropT( - scope, - originNode, - target, - new StringLiteralT(scope, originNode, 'prototype'), - ); - super( - scope, - originNode, - { - props: [], - proto: prototype, - calls: [], - }, - ); + constructor( + scope: Scope, + originNode: undefined | AnyNode, + target: T, + typeParameters: Array, + ) { + const prototype = new GetPropT( + scope, + originNode, + target, + new StringLiteralT(scope, originNode, 'prototype'), + ); + super( + scope, + originNode, + { + props: [], + proto: prototype, + calls: [], + }, + ); - this.typeParameters = typeParameters; - this.target = target; - } + this.typeParameters = typeParameters; + this.target = target; + } - typeParameters: Array; - target: T; + typeParameters: Array; + target: T; - static type = 'InstanceT'; + static type = 'InstanceT'; - serialize(addType: SerialTypeFactory): HydrateData { - return { - target: addType(this.target), - params: this.typeParameters.map((type) => addType(type)), - }; - } + serialize(addType: SerialTypeFactory): HydrateData { + return { + target: addType(this.target), + params: this.typeParameters.map((type) => addType(type)), + }; + } - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new InstanceT( - scope, - originNode, - getType(data.target), - Array(data.params).map((id) => getType(id)), - ); - } + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new InstanceT( + scope, + originNode, + getType(data.target), + Array(data.params).map((id) => getType(id)), + ); + } - humanize(builder: HumanBuilder): string { - const name = builder.humanize(this.target); - const typeParams = this.typeParameters; - if (typeParams.length === 0) { - return name; - } else { - return `${name}<${typeParams.map((param) => builder.humanize(param)).join( - ', ', - )}>`; - } - } + humanize(builder: HumanBuilder): string { + const name = builder.humanize(this.target); + const typeParams = this.typeParameters; + if (typeParams.length === 0) { + return name; + } else { + return `${name}<${typeParams.map((param) => builder.humanize(param)).join( + ', ', + )}>`; + } + } - compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { - return ( - otherType instanceof InstanceT && - this.utils.checkCompability(this.target, otherType.target) - ); - } + compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { + return ( + otherType instanceof InstanceT && + this.utils.checkCompability(this.target, otherType.target) + ); + } } diff --git a/packages/@romejs/js-analysis/types/IntersectionT.ts b/packages/@romejs/js-analysis/types/IntersectionT.ts index 8655f8bd248..e8f1f900e1a 100644 --- a/packages/@romejs/js-analysis/types/IntersectionT.ts +++ b/packages/@romejs/js-analysis/types/IntersectionT.ts @@ -12,44 +12,44 @@ import {Scope} from '../scopes'; import {HumanBuilder} from '../Utils'; export default class IntersectionT extends T { - constructor(scope: Scope, originNode: undefined | AnyNode, types: Array) { - super(scope, originNode); - this.types = types; - } + constructor(scope: Scope, originNode: undefined | AnyNode, types: Array) { + super(scope, originNode); + this.types = types; + } - static type = 'IntersectionT'; - types: Array; + static type = 'IntersectionT'; + types: Array; - serialize(addType: SerialTypeFactory): HydrateData { - return { - types: this.types.map((type) => addType(type)), - }; - } + serialize(addType: SerialTypeFactory): HydrateData { + return { + types: this.types.map((type) => addType(type)), + }; + } - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new IntersectionT( - scope, - originNode, - Array(data.types).map((id) => getType(id)), - ); - } + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new IntersectionT( + scope, + originNode, + Array(data.types).map((id) => getType(id)), + ); + } - compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { - for (const type of this.types) { - const compatibility = this.utils.checkCompability(type, otherType); - if (compatibility.type === 'incompatible') { - return compatibility; - } - } - return true; - } + compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { + for (const type of this.types) { + const compatibility = this.utils.checkCompability(type, otherType); + if (compatibility.type === 'incompatible') { + return compatibility; + } + } + return true; + } - humanize(builder: HumanBuilder): string { - return this.types.map((type) => builder.humanize(type)).join(' & '); - } + humanize(builder: HumanBuilder): string { + return this.types.map((type) => builder.humanize(type)).join(' & '); + } } diff --git a/packages/@romejs/js-analysis/types/MaybeT.ts b/packages/@romejs/js-analysis/types/MaybeT.ts index 2c96a56cd69..d036343ddeb 100644 --- a/packages/@romejs/js-analysis/types/MaybeT.ts +++ b/packages/@romejs/js-analysis/types/MaybeT.ts @@ -14,50 +14,50 @@ import VoidT from './VoidT'; import NullT from './NullT'; export default class MaybeT extends T { - constructor(scope: Scope, originNode: undefined | AnyNode, parent: T) { - super(scope, originNode); - this.parent = parent; - } - - static type = 'MaybeT'; - parent: T; - - serialize(addType: SerialTypeFactory): HydrateData { - return { - parent: addType(this.parent), - }; - } - - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new MaybeT(scope, originNode, getType(data.parent)); - } - - humanize(builder: HumanBuilder): string { - return `?${builder.humanize(this.parent)}`; - } - - explodeUnion(): Array { - return [ - new VoidT(this.scope, this.originNode), - new NullT(this.scope, this.originNode), - ...this.utils.explodeUnion(this.parent), - ]; - } - - compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { - if (otherType instanceof MaybeT) { - return this.utils.checkCompability(this.parent, otherType.parent); - } else { - return ( - otherType instanceof VoidT || - otherType instanceof NullT || - this.utils.checkCompability(this.parent, otherType) - ); - } - } + constructor(scope: Scope, originNode: undefined | AnyNode, parent: T) { + super(scope, originNode); + this.parent = parent; + } + + static type = 'MaybeT'; + parent: T; + + serialize(addType: SerialTypeFactory): HydrateData { + return { + parent: addType(this.parent), + }; + } + + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new MaybeT(scope, originNode, getType(data.parent)); + } + + humanize(builder: HumanBuilder): string { + return `?${builder.humanize(this.parent)}`; + } + + explodeUnion(): Array { + return [ + new VoidT(this.scope, this.originNode), + new NullT(this.scope, this.originNode), + ...this.utils.explodeUnion(this.parent), + ]; + } + + compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { + if (otherType instanceof MaybeT) { + return this.utils.checkCompability(this.parent, otherType.parent); + } else { + return ( + otherType instanceof VoidT || + otherType instanceof NullT || + this.utils.checkCompability(this.parent, otherType) + ); + } + } } diff --git a/packages/@romejs/js-analysis/types/MixedT.ts b/packages/@romejs/js-analysis/types/MixedT.ts index d8dcce101fb..b88a2758958 100644 --- a/packages/@romejs/js-analysis/types/MixedT.ts +++ b/packages/@romejs/js-analysis/types/MixedT.ts @@ -11,21 +11,21 @@ import {Scope} from '../scopes'; import T from './T'; export default class MixedT extends T { - static type = 'MixedT'; + static type = 'MixedT'; - serialize(): HydrateData { - return {}; - } + serialize(): HydrateData { + return {}; + } - static hydrate(scope: Scope, originNode: undefined | AnyNode): T { - return new MixedT(scope, originNode); - } + static hydrate(scope: Scope, originNode: undefined | AnyNode): T { + return new MixedT(scope, originNode); + } - compatibleWith(): boolean { - return false; - } + compatibleWith(): boolean { + return false; + } - humanize(): string { - return 'mixed'; - } + humanize(): string { + return 'mixed'; + } } diff --git a/packages/@romejs/js-analysis/types/NullT.ts b/packages/@romejs/js-analysis/types/NullT.ts index f35bc3926ea..ee8f33d687a 100644 --- a/packages/@romejs/js-analysis/types/NullT.ts +++ b/packages/@romejs/js-analysis/types/NullT.ts @@ -11,17 +11,17 @@ import {Scope} from '../scopes'; import T from './T'; export default class NullT extends T { - static type = 'NullT'; + static type = 'NullT'; - serialize(): HydrateData { - return {}; - } + serialize(): HydrateData { + return {}; + } - static hydrate(scope: Scope, originNode: undefined | AnyNode): T { - return new NullT(scope, originNode); - } + static hydrate(scope: Scope, originNode: undefined | AnyNode): T { + return new NullT(scope, originNode); + } - humanize(): string { - return 'null'; - } + humanize(): string { + return 'null'; + } } diff --git a/packages/@romejs/js-analysis/types/NumericLiteralT.ts b/packages/@romejs/js-analysis/types/NumericLiteralT.ts index 52020a1405b..cf7db67b574 100644 --- a/packages/@romejs/js-analysis/types/NumericLiteralT.ts +++ b/packages/@romejs/js-analysis/types/NumericLiteralT.ts @@ -13,43 +13,43 @@ import NumericT from './NumericT'; import ObjT from './ObjT'; export default class NumericLiteralT extends ObjT { - constructor(scope: Scope, originNode: undefined | AnyNode, value: number) { - super( - scope, - originNode, - { - props: [], - proto: scope.intrinsics.NumberPrototype, - calls: [], - }, - ); - this.value = value; - } - - static type = 'NumericLiteralT'; - - value: number; - - serialize(): HydrateData { - return {value: this.value}; - } - - static hydrate( - scope: Scope, - originNode: undefined | AnyNode, - data: HydrateData, - ): T { - return new NumericLiteralT(scope, originNode, Number(data.value)); - } - - humanize(): string { - return String(this.value); - } - - compatibleWith(type: T): boolean { - return ( - type instanceof NumericT || - (type instanceof NumericLiteralT && type.value === this.value) - ); - } + constructor(scope: Scope, originNode: undefined | AnyNode, value: number) { + super( + scope, + originNode, + { + props: [], + proto: scope.intrinsics.NumberPrototype, + calls: [], + }, + ); + this.value = value; + } + + static type = 'NumericLiteralT'; + + value: number; + + serialize(): HydrateData { + return {value: this.value}; + } + + static hydrate( + scope: Scope, + originNode: undefined | AnyNode, + data: HydrateData, + ): T { + return new NumericLiteralT(scope, originNode, Number(data.value)); + } + + humanize(): string { + return String(this.value); + } + + compatibleWith(type: T): boolean { + return ( + type instanceof NumericT || + (type instanceof NumericLiteralT && type.value === this.value) + ); + } } diff --git a/packages/@romejs/js-analysis/types/NumericT.ts b/packages/@romejs/js-analysis/types/NumericT.ts index 6dcf21a3bb0..ab211fb795c 100644 --- a/packages/@romejs/js-analysis/types/NumericT.ts +++ b/packages/@romejs/js-analysis/types/NumericT.ts @@ -13,34 +13,34 @@ import ObjT from './ObjT'; import T from './T'; export default class NumericT extends ObjT { - constructor(scope: Scope, originNode: undefined | AnyNode) { - super( - scope, - originNode, - { - props: [], - proto: scope.intrinsics.NumberPrototype, - calls: [], - }, - ); - } + constructor(scope: Scope, originNode: undefined | AnyNode) { + super( + scope, + originNode, + { + props: [], + proto: scope.intrinsics.NumberPrototype, + calls: [], + }, + ); + } - static type = 'NumericT'; + static type = 'NumericT'; - serialize(): HydrateData { - return {}; - } + serialize(): HydrateData { + return {}; + } - static hydrate(scope: Scope, originNode: undefined | AnyNode): T { - return new NumericT(scope, originNode); - } + static hydrate(scope: Scope, originNode: undefined | AnyNode): T { + return new NumericT(scope, originNode); + } - humanize(): string { - return 'number'; - } + humanize(): string { + return 'number'; + } - compatibleWith(type: T): boolean { - // a numeric literal can flow into a generic number - return type instanceof NumericT || type instanceof NumericLiteralT; - } + compatibleWith(type: T): boolean { + // a numeric literal can flow into a generic number + return type instanceof NumericT || type instanceof NumericLiteralT; + } } diff --git a/packages/@romejs/js-analysis/types/ObjIndexPropT.ts b/packages/@romejs/js-analysis/types/ObjIndexPropT.ts index 39c7c761bf3..cd5a5a3c206 100644 --- a/packages/@romejs/js-analysis/types/ObjIndexPropT.ts +++ b/packages/@romejs/js-analysis/types/ObjIndexPropT.ts @@ -12,39 +12,39 @@ import {HumanBuilder} from '../Utils'; import {Scope} from '../scopes'; export default class ObjIndexPropT extends T { - constructor(scope: Scope, originNode: undefined | AnyNode, key: T, value: T) { - super(scope, originNode); - this.key = key; - this.value = value; - } + constructor(scope: Scope, originNode: undefined | AnyNode, key: T, value: T) { + super(scope, originNode); + this.key = key; + this.value = value; + } - static type = 'ObjIndexPropT'; + static type = 'ObjIndexPropT'; - key: T; - value: T; + key: T; + value: T; - serialize(addType: SerialTypeFactory): HydrateData { - return { - key: addType(this.key), - value: addType(this.value), - }; - } + serialize(addType: SerialTypeFactory): HydrateData { + return { + key: addType(this.key), + value: addType(this.value), + }; + } - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new ObjIndexPropT( - scope, - originNode, - getType(data.key), - getType(data.value), - ); - } + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new ObjIndexPropT( + scope, + originNode, + getType(data.key), + getType(data.value), + ); + } - humanize(builder: HumanBuilder): string { - return `[${builder.humanize(this.key)}]: ${builder.humanize(this.value)}`; - } + humanize(builder: HumanBuilder): string { + return `[${builder.humanize(this.key)}]: ${builder.humanize(this.value)}`; + } } diff --git a/packages/@romejs/js-analysis/types/ObjPropT.ts b/packages/@romejs/js-analysis/types/ObjPropT.ts index 7aafd38dde2..35ef466c9c8 100644 --- a/packages/@romejs/js-analysis/types/ObjPropT.ts +++ b/packages/@romejs/js-analysis/types/ObjPropT.ts @@ -12,51 +12,46 @@ import {Scope} from '../scopes'; import {AnyNode} from '@romejs/js-ast'; export default class ObjPropT extends T { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - key: string, - value: T, - ) { - super(scope, originNode); - this.key = key; - this.value = value; - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + key: string, + value: T, + ) { + super(scope, originNode); + this.key = key; + this.value = value; + } - static type = 'ObjPropT'; - key: string; - value: T; + static type = 'ObjPropT'; + key: string; + value: T; - serialize(addType: SerialTypeFactory): HydrateData { - return { - key: this.key, - value: addType(this.value), - }; - } + serialize(addType: SerialTypeFactory): HydrateData { + return { + key: this.key, + value: addType(this.value), + }; + } - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new ObjPropT( - scope, - originNode, - String(data.key), - getType(data.value), - ); - } + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new ObjPropT(scope, originNode, String(data.key), getType(data.value)); + } - compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { - if (otherType instanceof ObjPropT && otherType.key === this.key) { - return this.utils.checkCompability(this.value, otherType.value); - } else { - return false; - } - } + compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { + if (otherType instanceof ObjPropT && otherType.key === this.key) { + return this.utils.checkCompability(this.value, otherType.value); + } else { + return false; + } + } - humanize(builder: HumanBuilder): string { - return `${this.key}: ${builder.humanize(this.value)}`; - } + humanize(builder: HumanBuilder): string { + return `${this.key}: ${builder.humanize(this.value)}`; + } } diff --git a/packages/@romejs/js-analysis/types/ObjT.ts b/packages/@romejs/js-analysis/types/ObjT.ts index 4e9241b5031..78eb469dc02 100644 --- a/packages/@romejs/js-analysis/types/ObjT.ts +++ b/packages/@romejs/js-analysis/types/ObjT.ts @@ -13,114 +13,114 @@ import {HumanBuilder} from '../Utils'; import ObjPropT from './ObjPropT'; export default class ObjT extends T { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - opts: { - props?: Array; - proto: undefined | T; - calls?: Array; - }, - ) { - super(scope, originNode); - this.calls = opts.calls === undefined ? [] : opts.calls; - this.props = opts.props === undefined ? [] : opts.props; - this.proto = opts.proto; - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + opts: { + props?: Array; + proto: undefined | T; + calls?: Array; + }, + ) { + super(scope, originNode); + this.calls = opts.calls === undefined ? [] : opts.calls; + this.props = opts.props === undefined ? [] : opts.props; + this.proto = opts.proto; + } - static type = 'ObjT'; - calls: Array; - props: Array; - proto: undefined | T; + static type = 'ObjT'; + calls: Array; + props: Array; + proto: undefined | T; - serialize(addType: SerialTypeFactory): HydrateData { - if (this.constructor !== ObjT) { - throw new Error( - 'Expected ObjT to be constructor, youve likely forgot to define this method in the type subclass', - ); - } + serialize(addType: SerialTypeFactory): HydrateData { + if (this.constructor !== ObjT) { + throw new Error( + 'Expected ObjT to be constructor, youve likely forgot to define this method in the type subclass', + ); + } - return { - calls: this.calls.map((type) => addType(type)), - proto: this.proto === undefined ? undefined : addType(this.proto), - props: this.props.map((type) => addType(type)), - }; - } + return { + calls: this.calls.map((type) => addType(type)), + proto: this.proto === undefined ? undefined : addType(this.proto), + props: this.props.map((type) => addType(type)), + }; + } - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new ObjT( - scope, - originNode, - { - props: Array(data.props).map((id) => getType(id)), - proto: data.proto === undefined ? undefined : getType(data.proto), - calls: Array(data.calls).map((id) => getType(id)), - }, - ); - } + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new ObjT( + scope, + originNode, + { + props: Array(data.props).map((id) => getType(id)), + proto: data.proto === undefined ? undefined : getType(data.proto), + calls: Array(data.calls).map((id) => getType(id)), + }, + ); + } - compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { - if (!(otherType instanceof ObjT)) { - return false; - } + compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { + if (!(otherType instanceof ObjT)) { + return false; + } - const ourProps: Array = this.props; - const theirProps: Array = otherType.props; + const ourProps: Array = this.props; + const theirProps: Array = otherType.props; - // check that the other type has all of our props - for (const ourPropRaw of ourProps) { - // reduce and get the key of this prop - const ourProp = this.utils.reduce(ourPropRaw); - let key; - if (ourProp instanceof ObjPropT) { - key = ourProp.key; - } else { - // should probably do something here - continue; - } + // check that the other type has all of our props + for (const ourPropRaw of ourProps) { + // reduce and get the key of this prop + const ourProp = this.utils.reduce(ourPropRaw); + let key; + if (ourProp instanceof ObjPropT) { + key = ourProp.key; + } else { + // should probably do something here + continue; + } - // try and find a prop of the same key in the other object - let theirProp; - for (const theirPropRaw of theirProps) { - const maybeTheirProp = this.utils.reduce(theirPropRaw); - if (maybeTheirProp instanceof ObjPropT && maybeTheirProp.key === key) { - theirProp = maybeTheirProp; - break; - } - } + // try and find a prop of the same key in the other object + let theirProp; + for (const theirPropRaw of theirProps) { + const maybeTheirProp = this.utils.reduce(theirPropRaw); + if (maybeTheirProp instanceof ObjPropT && maybeTheirProp.key === key) { + theirProp = maybeTheirProp; + break; + } + } - if (!ourProp || !theirProp) { - return false; - } + if (!ourProp || !theirProp) { + return false; + } - const compatibility = this.utils.checkCompability(ourProp, theirProp); - if (compatibility.type === 'incompatible') { - return compatibility; - } - } + const compatibility = this.utils.checkCompability(ourProp, theirProp); + if (compatibility.type === 'incompatible') { + return compatibility; + } + } - return true; - } + return true; + } - humanize(builder: HumanBuilder): string { - if (this.props.length === 0) { - return '{}'; - } else { - return [ - '{', - ...this.props.map((prop) => { - const val = builder.humanize(prop); - let lines = val.split('\n'); - lines = lines.map((line) => ` ${line}`); - return `${lines.join('\n')},`; - }), - '}', - ].join('\n'); - } - } + humanize(builder: HumanBuilder): string { + if (this.props.length === 0) { + return '{}'; + } else { + return [ + '{', + ...this.props.map((prop) => { + const val = builder.humanize(prop); + let lines = val.split('\n'); + lines = lines.map((line) => ` ${line}`); + return `${lines.join('\n')},`; + }), + '}', + ].join('\n'); + } + } } diff --git a/packages/@romejs/js-analysis/types/OpaqueT.ts b/packages/@romejs/js-analysis/types/OpaqueT.ts index 8e74f0f4e72..df8d65621ff 100644 --- a/packages/@romejs/js-analysis/types/OpaqueT.ts +++ b/packages/@romejs/js-analysis/types/OpaqueT.ts @@ -11,31 +11,31 @@ import {Scope} from '../scopes'; import T from './T'; export default class OpaqueT extends T { - constructor(scope: Scope, originNode: undefined | AnyNode, name: string) { - super(scope, originNode); - this.name = name; - } + constructor(scope: Scope, originNode: undefined | AnyNode, name: string) { + super(scope, originNode); + this.name = name; + } - static type = 'OpaqueT'; - name: string; + static type = 'OpaqueT'; + name: string; - serialize(): HydrateData { - return {name: this.name}; - } + serialize(): HydrateData { + return {name: this.name}; + } - static hydrate( - scope: Scope, - originNode: undefined | AnyNode, - data: HydrateData, - ): T { - return new OpaqueT(scope, originNode, String(data.name)); - } + static hydrate( + scope: Scope, + originNode: undefined | AnyNode, + data: HydrateData, + ): T { + return new OpaqueT(scope, originNode, String(data.name)); + } - humanize(): string { - return `opaque ${this.name}`; - } + humanize(): string { + return `opaque ${this.name}`; + } - compatibleWith(otherType: T): boolean { - return otherType === this; - } + compatibleWith(otherType: T): boolean { + return otherType === this; + } } diff --git a/packages/@romejs/js-analysis/types/OpenIntrinsicT.ts b/packages/@romejs/js-analysis/types/OpenIntrinsicT.ts index 8ea2eedba28..eec4143fbb2 100644 --- a/packages/@romejs/js-analysis/types/OpenIntrinsicT.ts +++ b/packages/@romejs/js-analysis/types/OpenIntrinsicT.ts @@ -12,30 +12,30 @@ import OpenT from './OpenT'; import {AnyNode} from '@romejs/js-ast'; export default class OpenIntrinsicT extends OpenT { - constructor(scope: Scope, originNode: undefined | AnyNode, name: string) { - super(scope, originNode); - this.name = name; - } + constructor(scope: Scope, originNode: undefined | AnyNode, name: string) { + super(scope, originNode); + this.name = name; + } - static type = 'OpenIntrinsicT'; + static type = 'OpenIntrinsicT'; - name: string; + name: string; - serialize(): HydrateData { - return { - name: this.name, - }; - } + serialize(): HydrateData { + return { + name: this.name, + }; + } - static hydrate( - scope: Scope, - originNode: undefined | AnyNode, - data: HydrateData, - ): T { - return scope.intrinsics.get(String(data.name)); - } + static hydrate( + scope: Scope, + originNode: undefined | AnyNode, + data: HydrateData, + ): T { + return scope.intrinsics.get(String(data.name)); + } - humanize(): string { - return 'open intrinsic'; - } + humanize(): string { + return 'open intrinsic'; + } } diff --git a/packages/@romejs/js-analysis/types/OpenT.ts b/packages/@romejs/js-analysis/types/OpenT.ts index 18c1201479f..234a58dd942 100644 --- a/packages/@romejs/js-analysis/types/OpenT.ts +++ b/packages/@romejs/js-analysis/types/OpenT.ts @@ -10,24 +10,24 @@ import UnknownT from './UnknownT'; import T from './T'; export default class OpenT extends T { - static type = 'OpenT'; + static type = 'OpenT'; - humanize(builder: HumanBuilder): string { - const type = this.utils.reduce(this); - if (type === this) { - return 'open'; - } else { - return builder.humanize(type); - } - } + humanize(builder: HumanBuilder): string { + const type = this.utils.reduce(this); + if (type === this) { + return 'open'; + } else { + return builder.humanize(type); + } + } - reduce(): T { - const node = this.graph.find(this); - if (node === undefined) { - return new UnknownT(this.scope, this.originNode); - } + reduce(): T { + const node = this.graph.find(this); + if (node === undefined) { + return new UnknownT(this.scope, this.originNode); + } - const values = node.lines.map((line) => this.utils.reduce(line.value)); - return this.scope.createUnion(values, this.originNode); - } + const values = node.lines.map((line) => this.utils.reduce(line.value)); + return this.scope.createUnion(values, this.originNode); + } } diff --git a/packages/@romejs/js-analysis/types/RefineTypeofT.ts b/packages/@romejs/js-analysis/types/RefineTypeofT.ts index 4ce246c42a8..b128060a887 100644 --- a/packages/@romejs/js-analysis/types/RefineTypeofT.ts +++ b/packages/@romejs/js-analysis/types/RefineTypeofT.ts @@ -15,69 +15,69 @@ import StringT from './StringT'; import VoidT from './VoidT'; export default class RefineTypeofT extends T { - constructor(scope: Scope, node: AnyNode, str: T, fallback: T) { - super(scope, node); - this.str = str; - this.fallback = fallback; - } + constructor(scope: Scope, node: AnyNode, str: T, fallback: T) { + super(scope, node); + this.str = str; + this.fallback = fallback; + } - static type = 'RefineTypeofT'; - str: T; - fallback: T; + static type = 'RefineTypeofT'; + str: T; + fallback: T; - reduce(): T { - const {fallback, utils} = this; - const str = utils.reduce(this.str); + reduce(): T { + const {fallback, utils} = this; + const str = utils.reduce(this.str); - if (str instanceof StringLiteralT) { - let val; + if (str instanceof StringLiteralT) { + let val; - switch (str.value) { - case 'string': { - val = new StringT(this.scope, undefined); - break; - } + switch (str.value) { + case 'string': { + val = new StringT(this.scope, undefined); + break; + } - case 'number': { - val = new NumericT(this.scope, undefined); - break; - } + case 'number': { + val = new NumericT(this.scope, undefined); + break; + } - case 'undefined': { - val = new VoidT(this.scope, undefined); - break; - } + case 'undefined': { + val = new VoidT(this.scope, undefined); + break; + } - case 'boolean': { - val = new BooleanT(this.scope, undefined); - break; - } + case 'boolean': { + val = new BooleanT(this.scope, undefined); + break; + } - case 'symbol': - case 'function': - case 'object': - // TODO - return utils.reduce(fallback); + case 'symbol': + case 'function': + case 'object': + // TODO + return utils.reduce(fallback); - default: - // TODO complain about unknown value - return utils.reduce(fallback); - } + default: + // TODO complain about unknown value + return utils.reduce(fallback); + } - // make sure our refinement is actually possible and matches a value in `fallback` + // make sure our refinement is actually possible and matches a value in `fallback` - // then pluck the matching type - const types = utils.explodeUnion(fallback); - for (const type of types) { - if (utils.isCompatibleWith(type, val)) { - return utils.reduce(type); - } - } + // then pluck the matching type + const types = utils.explodeUnion(fallback); + for (const type of types) { + if (utils.isCompatibleWith(type, val)) { + return utils.reduce(type); + } + } - // TODO complain of a missing condition - return utils.reduce(fallback); - } + // TODO complain of a missing condition + return utils.reduce(fallback); + } - return utils.reduce(fallback); - } + return utils.reduce(fallback); + } } diff --git a/packages/@romejs/js-analysis/types/RefinedT.ts b/packages/@romejs/js-analysis/types/RefinedT.ts index 508e935b0d3..917400813c9 100644 --- a/packages/@romejs/js-analysis/types/RefinedT.ts +++ b/packages/@romejs/js-analysis/types/RefinedT.ts @@ -11,56 +11,56 @@ import MissingUnionE from './errors/MissingUnionE'; import T from './T'; export default class RefinedT extends T { - constructor(scope: Scope, originNode: undefined | AnyNode, root: T, remove: T) { - super(scope, originNode); - this.root = root; - this.remove = remove; - } + constructor(scope: Scope, originNode: undefined | AnyNode, root: T, remove: T) { + super(scope, originNode); + this.root = root; + this.remove = remove; + } - static type = 'RefinedT'; - root: T; - remove: T; + static type = 'RefinedT'; + root: T; + remove: T; - reduce(): T { - const {root} = this; + reduce(): T { + const {root} = this; - const exploded = this.utils.explodeUnion(root); - const removeTypes = this.utils.explodeUnion(this.remove); + const exploded = this.utils.explodeUnion(root); + const removeTypes = this.utils.explodeUnion(this.remove); - const clean = []; - const removed = []; + const clean = []; + const removed = []; - // remove any possible derived types from the root that are compatible with the removed type - for (const type of exploded) { - let compatible = false; + // remove any possible derived types from the root that are compatible with the removed type + for (const type of exploded) { + let compatible = false; - // check if any of the removed types are compatible, if every removed type is incompatible then + // check if any of the removed types are compatible, if every removed type is incompatible then - // we've refined away the type - for (const remove of removeTypes) { - if (this.utils.isCompatibleWith(type, remove)) { - compatible = true; - } - } + // we've refined away the type + for (const remove of removeTypes) { + if (this.utils.isCompatibleWith(type, remove)) { + compatible = true; + } + } - if (compatible === false) { - clean.push(type); - } else { - removed.push(type); - } - } + if (compatible === false) { + clean.push(type); + } else { + removed.push(type); + } + } - if (removed.length === 0) { - // return an error here because the removed type doesn't exist in the root - return new MissingUnionE( - root.scope, - root.originNode, - root, - this.remove, - removed, - ); - } else { - return root.scope.createUnion(clean, root.originNode); - } - } + if (removed.length === 0) { + // return an error here because the removed type doesn't exist in the root + return new MissingUnionE( + root.scope, + root.originNode, + root, + this.remove, + removed, + ); + } else { + return root.scope.createUnion(clean, root.originNode); + } + } } diff --git a/packages/@romejs/js-analysis/types/SideEffectT.ts b/packages/@romejs/js-analysis/types/SideEffectT.ts index 5cef5693fa3..148284f9454 100644 --- a/packages/@romejs/js-analysis/types/SideEffectT.ts +++ b/packages/@romejs/js-analysis/types/SideEffectT.ts @@ -10,16 +10,16 @@ import {Scope} from '../scopes'; import T from './T'; export default class SideEffectT extends T { - constructor(scope: Scope, originNode: undefined | AnyNode, actualType: T) { - super(scope, originNode); - this.actualType = actualType; - } + constructor(scope: Scope, originNode: undefined | AnyNode, actualType: T) { + super(scope, originNode); + this.actualType = actualType; + } - static type = 'SideEffectT'; + static type = 'SideEffectT'; - actualType: T; + actualType: T; - reduce(): T { - return this.utils.reduce(this.actualType); - } + reduce(): T { + return this.utils.reduce(this.actualType); + } } diff --git a/packages/@romejs/js-analysis/types/StringLiteralT.ts b/packages/@romejs/js-analysis/types/StringLiteralT.ts index f84f703f1fb..b7c88342a08 100644 --- a/packages/@romejs/js-analysis/types/StringLiteralT.ts +++ b/packages/@romejs/js-analysis/types/StringLiteralT.ts @@ -12,44 +12,44 @@ import ObjT from './ObjT'; import T from './T'; export default class StringLiteralT extends ObjT { - constructor(scope: Scope, originNode: undefined | AnyNode, value: string) { - super( - scope, - originNode, - { - props: [], - proto: scope.intrinsics.StringPrototype, - calls: [], - }, - ); - this.value = value; - } + constructor(scope: Scope, originNode: undefined | AnyNode, value: string) { + super( + scope, + originNode, + { + props: [], + proto: scope.intrinsics.StringPrototype, + calls: [], + }, + ); + this.value = value; + } - static type = 'StringLiteralT'; - value: string; + static type = 'StringLiteralT'; + value: string; - serialize(): HydrateData { - return {value: this.value}; - } + serialize(): HydrateData { + return {value: this.value}; + } - static hydrate( - scope: Scope, - originNode: undefined | AnyNode, - data: HydrateData, - ): T { - return new StringLiteralT(scope, originNode, String(data.value)); - } + static hydrate( + scope: Scope, + originNode: undefined | AnyNode, + data: HydrateData, + ): T { + return new StringLiteralT(scope, originNode, String(data.value)); + } - humanize(): string { - let str: string = JSON.stringify(this.value); - if (this.value.includes("'")) { - return str; - } else { - return `'${str.slice(1, -1)}'`; - } - } + humanize(): string { + let str: string = JSON.stringify(this.value); + if (this.value.includes("'")) { + return str; + } else { + return `'${str.slice(1, -1)}'`; + } + } - compatibleWith(type: T): boolean { - return type instanceof StringLiteralT && type.value === this.value; - } + compatibleWith(type: T): boolean { + return type instanceof StringLiteralT && type.value === this.value; + } } diff --git a/packages/@romejs/js-analysis/types/StringT.ts b/packages/@romejs/js-analysis/types/StringT.ts index fe07ee45414..6565810b637 100644 --- a/packages/@romejs/js-analysis/types/StringT.ts +++ b/packages/@romejs/js-analysis/types/StringT.ts @@ -13,34 +13,34 @@ import ObjT from './ObjT'; import T from './T'; export default class StringT extends ObjT { - constructor(scope: Scope, originNode: undefined | AnyNode) { - super( - scope, - originNode, - { - props: [], - proto: scope.intrinsics.StringPrototype, - calls: [], - }, - ); - } + constructor(scope: Scope, originNode: undefined | AnyNode) { + super( + scope, + originNode, + { + props: [], + proto: scope.intrinsics.StringPrototype, + calls: [], + }, + ); + } - static type = 'StringT'; + static type = 'StringT'; - serialize(): HydrateData { - return {}; - } + serialize(): HydrateData { + return {}; + } - static hydrate(scope: Scope, originNode: undefined | AnyNode): T { - return new StringT(scope, originNode); - } + static hydrate(scope: Scope, originNode: undefined | AnyNode): T { + return new StringT(scope, originNode); + } - humanize(): string { - return 'string'; - } + humanize(): string { + return 'string'; + } - compatibleWith(type: T) { - // a string literal can flow into a generic string - return type instanceof StringT || type instanceof StringLiteralT; - } + compatibleWith(type: T) { + // a string literal can flow into a generic string + return type instanceof StringT || type instanceof StringLiteralT; + } } diff --git a/packages/@romejs/js-analysis/types/T.ts b/packages/@romejs/js-analysis/types/T.ts index 8e618382f9a..79c5964a446 100644 --- a/packages/@romejs/js-analysis/types/T.ts +++ b/packages/@romejs/js-analysis/types/T.ts @@ -18,139 +18,139 @@ let counter = 0; export type SerialTypeFactory = (type: T) => string; export type TypeCompatibilityReturn = - | { - type: 'compatible'; - } - | { - type: 'incompatible'; - lower: T; - upper: T; - }; + | { + type: 'compatible'; + } + | { + type: 'incompatible'; + lower: T; + upper: T; + }; export default class T { - constructor(scope: Scope, originNode: undefined | AnyNode) { - this.human = undefined; - this.scope = scope; - - const {hub} = scope; - this.hub = hub; - this.utils = hub.utils; - this.evaluator = hub.evaluator; - this.originEvaluator = scope.evaluator.evaluatingType; - - // setup graph - this.graph = scope.evaluator.graph; - this.graph.addNode(this); - - this.originNode = originNode; - this.originLoc = originNode === undefined ? undefined : originNode.loc; - this.id = `${String(process.pid)}:${String(counter++)}`; - - this.compatibilityCache = new Map(); - } - - static type = 'T'; - utils: Utils; - evaluator: Evaluator; - graph: Graph; - scope: Scope; - hub: Hub; - - compatibilityCache: Map; - - human: undefined | string; - id: string; - - originNode: undefined | AnyNode; - originLoc: undefined | SourceLocation; - originEvaluator: undefined | string; - - getConstructor(): typeof T { - // @ts-ignore - return this.constructor; - } - - setHuman(human: undefined | string) { - this.human = human; - } - - shouldMatch(type: T) { - this.hub.assertOpen(); - this.graph.addLine(this, type); - } - - hasConnections(): boolean { - return this.graph.hasConnections(this); - } - - explodeUnion(): Array { - return [this]; - } - - compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { - return otherType instanceof this.constructor; - } - - clone() { - const idsToType: Map = new Map(); - - const addType: SerialTypeFactory = (type: T) => { - const reduced = this.utils.reduce(type); - idsToType.set(type.id, type); - return reduced.id; - }; - - const data = this.serialize(addType); - - const getType: HydrateTypeFactory = (id: unknown): T => { - if (typeof id !== 'string') { - throw new Error('Expected id to be a string'); - } - - const type = idsToType.get(id); - if (type === undefined) { - throw new Error('Expected type'); - } - return type; - }; - - return this.getConstructor().hydrate( - this.scope, - this.originNode, - data, - getType, - ); - } - - static hydrate( - scope: Scope, - originNode: undefined | AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - throw new Error(`Unimplemented ${this.type}.hydrate`); - } - - serialize(addType: SerialTypeFactory): HydrateData { - throw new Error( - `Unimplemented ${this.getConstructor().type}.prototype.serialize`, - ); - } - - reduce(): T { - return this; - } - - humanize(builder: HumanBuilder): string { - const reduced = this.utils.reduce(this); - if (reduced === this) { - throw new Error('unimplemented'); - } else { - return builder.humanize(reduced); - } - } - - inspect() { - return this.utils.inspect(this); - } + constructor(scope: Scope, originNode: undefined | AnyNode) { + this.human = undefined; + this.scope = scope; + + const {hub} = scope; + this.hub = hub; + this.utils = hub.utils; + this.evaluator = hub.evaluator; + this.originEvaluator = scope.evaluator.evaluatingType; + + // setup graph + this.graph = scope.evaluator.graph; + this.graph.addNode(this); + + this.originNode = originNode; + this.originLoc = originNode === undefined ? undefined : originNode.loc; + this.id = `${String(process.pid)}:${String(counter++)}`; + + this.compatibilityCache = new Map(); + } + + static type = 'T'; + utils: Utils; + evaluator: Evaluator; + graph: Graph; + scope: Scope; + hub: Hub; + + compatibilityCache: Map; + + human: undefined | string; + id: string; + + originNode: undefined | AnyNode; + originLoc: undefined | SourceLocation; + originEvaluator: undefined | string; + + getConstructor(): typeof T { + // @ts-ignore + return this.constructor; + } + + setHuman(human: undefined | string) { + this.human = human; + } + + shouldMatch(type: T) { + this.hub.assertOpen(); + this.graph.addLine(this, type); + } + + hasConnections(): boolean { + return this.graph.hasConnections(this); + } + + explodeUnion(): Array { + return [this]; + } + + compatibleWith(otherType: T): boolean | TypeCompatibilityReturn { + return otherType instanceof this.constructor; + } + + clone() { + const idsToType: Map = new Map(); + + const addType: SerialTypeFactory = (type: T) => { + const reduced = this.utils.reduce(type); + idsToType.set(type.id, type); + return reduced.id; + }; + + const data = this.serialize(addType); + + const getType: HydrateTypeFactory = (id: unknown): T => { + if (typeof id !== 'string') { + throw new Error('Expected id to be a string'); + } + + const type = idsToType.get(id); + if (type === undefined) { + throw new Error('Expected type'); + } + return type; + }; + + return this.getConstructor().hydrate( + this.scope, + this.originNode, + data, + getType, + ); + } + + static hydrate( + scope: Scope, + originNode: undefined | AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + throw new Error(`Unimplemented ${this.type}.hydrate`); + } + + serialize(addType: SerialTypeFactory): HydrateData { + throw new Error( + `Unimplemented ${this.getConstructor().type}.prototype.serialize`, + ); + } + + reduce(): T { + return this; + } + + humanize(builder: HumanBuilder): string { + const reduced = this.utils.reduce(this); + if (reduced === this) { + throw new Error('unimplemented'); + } else { + return builder.humanize(reduced); + } + } + + inspect() { + return this.utils.inspect(this); + } } diff --git a/packages/@romejs/js-analysis/types/TypeofT.ts b/packages/@romejs/js-analysis/types/TypeofT.ts index 2b559dc6fc3..ce6da0e3ede 100644 --- a/packages/@romejs/js-analysis/types/TypeofT.ts +++ b/packages/@romejs/js-analysis/types/TypeofT.ts @@ -19,64 +19,62 @@ import VoidT from './VoidT'; import T from './T'; export default class TypeofT extends T { - constructor(scope: Scope, node: undefined | AnyNode, obj: T) { - super(scope, node); - this.obj = obj; - } + constructor(scope: Scope, node: undefined | AnyNode, obj: T) { + super(scope, node); + this.obj = obj; + } - static type = 'TypeofT'; - obj: T; + static type = 'TypeofT'; + obj: T; - reduce(): T { - const types = this.utils.explodeUnion(this.obj); + reduce(): T { + const types = this.utils.explodeUnion(this.obj); - const possibleTypes = []; - for (const rawType of types) { - const type = this.utils.reduce(rawType); - let typeStr; + const possibleTypes = []; + for (const rawType of types) { + const type = this.utils.reduce(rawType); + let typeStr; - if (type instanceof StringT || type instanceof StringLiteralT) { - typeStr = 'string'; - } + if (type instanceof StringT || type instanceof StringLiteralT) { + typeStr = 'string'; + } - if (type instanceof NumericT || type instanceof NumericLiteralT) { - typeStr = 'number'; - } + if (type instanceof NumericT || type instanceof NumericLiteralT) { + typeStr = 'number'; + } - if (type instanceof BooleanT || type instanceof BooleanLiteralT) { - typeStr = 'boolean'; - } + if (type instanceof BooleanT || type instanceof BooleanLiteralT) { + typeStr = 'boolean'; + } - if (type instanceof VoidT) { - typeStr = 'undefined'; - } + if (type instanceof VoidT) { + typeStr = 'undefined'; + } - if (type instanceof ObjT) { - if (type.calls.length === 0) { - typeStr = 'object'; - } else { - typeStr = 'function'; - } - } + if (type instanceof ObjT) { + if (type.calls.length === 0) { + typeStr = 'object'; + } else { + typeStr = 'function'; + } + } - if (type instanceof NullT) { - typeStr = 'object'; - } + if (type instanceof NullT) { + typeStr = 'object'; + } - // TODO symbol + // TODO symbol - // TODO bigint - if (typeStr !== undefined) { - possibleTypes.push( - new StringLiteralT(this.scope, this.originNode, typeStr), - ); - } - } + // TODO bigint + if (typeStr !== undefined) { + possibleTypes.push(new StringLiteralT(this.scope, this.originNode, typeStr)); + } + } - if (possibleTypes.length === 0) { - return new StringT(this.scope, this.originNode); - } else { - return this.scope.createUnion(possibleTypes, this.originNode); - } - } + if (possibleTypes.length === 0) { + return new StringT(this.scope, this.originNode); + } else { + return this.scope.createUnion(possibleTypes, this.originNode); + } + } } diff --git a/packages/@romejs/js-analysis/types/UnionT.ts b/packages/@romejs/js-analysis/types/UnionT.ts index 9363765b939..28733d218ea 100644 --- a/packages/@romejs/js-analysis/types/UnionT.ts +++ b/packages/@romejs/js-analysis/types/UnionT.ts @@ -12,113 +12,113 @@ import {Scope} from '../scopes'; import {HumanBuilder} from '../Utils'; export default class UnionT extends T { - constructor(scope: Scope, originNode: undefined | AnyNode, types: Array) { - super(scope, originNode); - this.types = [...new Set(types)]; - } - - static type = 'UnionT'; - types: Array; - - serialize(addType: SerialTypeFactory): HydrateData { - return { - types: this.types.map((type) => addType(type)), - }; - } - - static hydrate( - scope: Scope, - originNode: AnyNode, - data: HydrateData, - getType: HydrateTypeFactory, - ): T { - return new UnionT( - scope, - originNode, - Array(data.types).map((id) => getType(id)), - ); - } - - reduce(): T { - const uniqTypes = []; - const types = this.explodeUnion(); - - for (const type of types) { - let foundMatch = false; - for (const compareType of uniqTypes) { - const isCompatible = this.utils.isCompatibleWith(compareType, type); - if (isCompatible) { - foundMatch = true; - break; - } - } - if (foundMatch === false) { - uniqTypes.push(type); - } - } - - if (uniqTypes.length === types.length) { - return this; - } else if (uniqTypes.length === 1) { - return uniqTypes[0]; - } else { - return new UnionT(this.scope, this.originNode, uniqTypes); - } - } - - explodeUnion(): Array { - let types: Array = []; - const visited: Set = new Set([this]); - - for (const type of this.types) { - const reduced = this.utils.reduce(type); - if (visited.has(reduced)) { - continue; - } else { - visited.add(reduced); - } - - types = types.concat(this.utils.explodeUnion(type)); - } - - return types; - } - - compatibleWith(otherType: T) { - const ourTypes = this.utils.explodeUnion(this); - - // fast path to check if a union contains a type - if (ourTypes.includes(otherType)) { - return true; - } - - const otherTypes = this.utils.explodeUnion(otherType); - const missing: Array = []; - - for (const type of ourTypes) { - let compatible = false; - - for (const otherType of otherTypes) { - if (this.utils.isCompatibleWith(type, otherType)) { - compatible = true; - } - } - - if (compatible === false) { - missing.push(type); - } - } - - if (missing.length === 0) { - return true; - } else { - // create custom error with the types that weren't in the opposing one - //return new MissingUnionE(this.scope, otherType.originNode, otherType, this, missing); - return false; - } - } - - humanize(builder: HumanBuilder): string { - return this.types.map((type) => builder.humanize(type)).join(' | '); - } + constructor(scope: Scope, originNode: undefined | AnyNode, types: Array) { + super(scope, originNode); + this.types = [...new Set(types)]; + } + + static type = 'UnionT'; + types: Array; + + serialize(addType: SerialTypeFactory): HydrateData { + return { + types: this.types.map((type) => addType(type)), + }; + } + + static hydrate( + scope: Scope, + originNode: AnyNode, + data: HydrateData, + getType: HydrateTypeFactory, + ): T { + return new UnionT( + scope, + originNode, + Array(data.types).map((id) => getType(id)), + ); + } + + reduce(): T { + const uniqTypes = []; + const types = this.explodeUnion(); + + for (const type of types) { + let foundMatch = false; + for (const compareType of uniqTypes) { + const isCompatible = this.utils.isCompatibleWith(compareType, type); + if (isCompatible) { + foundMatch = true; + break; + } + } + if (foundMatch === false) { + uniqTypes.push(type); + } + } + + if (uniqTypes.length === types.length) { + return this; + } else if (uniqTypes.length === 1) { + return uniqTypes[0]; + } else { + return new UnionT(this.scope, this.originNode, uniqTypes); + } + } + + explodeUnion(): Array { + let types: Array = []; + const visited: Set = new Set([this]); + + for (const type of this.types) { + const reduced = this.utils.reduce(type); + if (visited.has(reduced)) { + continue; + } else { + visited.add(reduced); + } + + types = types.concat(this.utils.explodeUnion(type)); + } + + return types; + } + + compatibleWith(otherType: T) { + const ourTypes = this.utils.explodeUnion(this); + + // fast path to check if a union contains a type + if (ourTypes.includes(otherType)) { + return true; + } + + const otherTypes = this.utils.explodeUnion(otherType); + const missing: Array = []; + + for (const type of ourTypes) { + let compatible = false; + + for (const otherType of otherTypes) { + if (this.utils.isCompatibleWith(type, otherType)) { + compatible = true; + } + } + + if (compatible === false) { + missing.push(type); + } + } + + if (missing.length === 0) { + return true; + } else { + // create custom error with the types that weren't in the opposing one + //return new MissingUnionE(this.scope, otherType.originNode, otherType, this, missing); + return false; + } + } + + humanize(builder: HumanBuilder): string { + return this.types.map((type) => builder.humanize(type)).join(' | '); + } } diff --git a/packages/@romejs/js-analysis/types/UnknownT.ts b/packages/@romejs/js-analysis/types/UnknownT.ts index 42f2a675ebe..12fdd8e3d35 100644 --- a/packages/@romejs/js-analysis/types/UnknownT.ts +++ b/packages/@romejs/js-analysis/types/UnknownT.ts @@ -11,21 +11,21 @@ import {Scope} from '../scopes'; import T from './T'; export default class UnknownT extends T { - static type = 'UnknownT'; + static type = 'UnknownT'; - serialize(): HydrateData { - return {}; - } + serialize(): HydrateData { + return {}; + } - static hydrate(scope: Scope, originNode: undefined | AnyNode): T { - return new UnknownT(scope, originNode); - } + static hydrate(scope: Scope, originNode: undefined | AnyNode): T { + return new UnknownT(scope, originNode); + } - humanize(): string { - return 'unknown'; - } + humanize(): string { + return 'unknown'; + } - compatibleWith(): boolean { - return false; - } + compatibleWith(): boolean { + return false; + } } diff --git a/packages/@romejs/js-analysis/types/VoidT.ts b/packages/@romejs/js-analysis/types/VoidT.ts index 20382d55e7d..58fa583b0c3 100644 --- a/packages/@romejs/js-analysis/types/VoidT.ts +++ b/packages/@romejs/js-analysis/types/VoidT.ts @@ -11,17 +11,17 @@ import {Scope} from '../scopes'; import T from './T'; export default class VoidT extends T { - static type = 'VoidT'; + static type = 'VoidT'; - serialize(): HydrateData { - return {}; - } + serialize(): HydrateData { + return {}; + } - static hydrate(scope: Scope, originNode: undefined | AnyNode): T { - return new VoidT(scope, originNode); - } + static hydrate(scope: Scope, originNode: undefined | AnyNode): T { + return new VoidT(scope, originNode); + } - humanize(): string { - return 'void'; - } + humanize(): string { + return 'void'; + } } diff --git a/packages/@romejs/js-analysis/types/errors/E.ts b/packages/@romejs/js-analysis/types/errors/E.ts index ca90434156a..fe844d32bbe 100644 --- a/packages/@romejs/js-analysis/types/errors/E.ts +++ b/packages/@romejs/js-analysis/types/errors/E.ts @@ -12,27 +12,27 @@ import AnyT from '../AnyT'; import T from '../T'; export type ErrorDefinition = { - description: DiagnosticDescription; - lowerTarget: T; - upperTarget?: T; + description: DiagnosticDescription; + lowerTarget: T; + upperTarget?: T; }; export default class E extends T { - static type = 'E'; + static type = 'E'; - static hydrate(scope: Scope, originNode: undefined | AnyNode): T { - return new AnyT(scope, originNode); - } + static hydrate(scope: Scope, originNode: undefined | AnyNode): T { + return new AnyT(scope, originNode); + } - humanize(): string { - return this.getError().description.message.value; - } + humanize(): string { + return this.getError().description.message.value; + } - getError(): ErrorDefinition { - throw new Error('unimplemented'); - } + getError(): ErrorDefinition { + throw new Error('unimplemented'); + } - compatibleWith(): boolean { - return false; - } + compatibleWith(): boolean { + return false; + } } diff --git a/packages/@romejs/js-analysis/types/errors/MissingUnionE.ts b/packages/@romejs/js-analysis/types/errors/MissingUnionE.ts index 2a233b1d3ba..bf9fc20e86b 100644 --- a/packages/@romejs/js-analysis/types/errors/MissingUnionE.ts +++ b/packages/@romejs/js-analysis/types/errors/MissingUnionE.ts @@ -12,30 +12,30 @@ import {AnyNode} from '@romejs/js-ast'; import {descriptions} from '@romejs/diagnostics'; export default class MissingUnionE extends E { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - target: T, - union: T, - missing: Array, - ) { - super(scope, originNode); - this.target = target; - this.union = union; - this.missing = missing; - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + target: T, + union: T, + missing: Array, + ) { + super(scope, originNode); + this.target = target; + this.union = union; + this.missing = missing; + } - static type = 'MissingUnionE'; - target: T; - union: T; - missing: Array; + static type = 'MissingUnionE'; + target: T; + union: T; + missing: Array; - getError(): ErrorDefinition { - return { - description: descriptions.TYPE_CHECK.MISSING_CONDITION( - this.missing.map((type) => this.utils.humanize(type)), - ), - lowerTarget: this.target, - }; - } + getError(): ErrorDefinition { + return { + description: descriptions.TYPE_CHECK.MISSING_CONDITION( + this.missing.map((type) => this.utils.humanize(type)), + ), + lowerTarget: this.target, + }; + } } diff --git a/packages/@romejs/js-analysis/types/errors/NotCallableE.ts b/packages/@romejs/js-analysis/types/errors/NotCallableE.ts index 01742c1f3fd..79b03109d73 100644 --- a/packages/@romejs/js-analysis/types/errors/NotCallableE.ts +++ b/packages/@romejs/js-analysis/types/errors/NotCallableE.ts @@ -12,18 +12,18 @@ import T from '../T'; import {descriptions} from '@romejs/diagnostics'; export default class NotCallableE extends E { - constructor(scope: Scope, originNode: undefined | AnyNode, callee: T) { - super(scope, originNode); - this.callee = callee; - } + constructor(scope: Scope, originNode: undefined | AnyNode, callee: T) { + super(scope, originNode); + this.callee = callee; + } - static type = 'NotCallableE'; - callee: T; + static type = 'NotCallableE'; + callee: T; - getError(): ErrorDefinition { - return { - description: descriptions.TYPE_CHECK.NOT_CALLABLE, - lowerTarget: this.callee, - }; - } + getError(): ErrorDefinition { + return { + description: descriptions.TYPE_CHECK.NOT_CALLABLE, + lowerTarget: this.callee, + }; + } } diff --git a/packages/@romejs/js-analysis/types/errors/UndeclaredVarE.ts b/packages/@romejs/js-analysis/types/errors/UndeclaredVarE.ts index 083dfd4b614..b3951c97399 100644 --- a/packages/@romejs/js-analysis/types/errors/UndeclaredVarE.ts +++ b/packages/@romejs/js-analysis/types/errors/UndeclaredVarE.ts @@ -11,22 +11,22 @@ import {Scope} from '../../scopes'; import E, {ErrorDefinition} from './E'; export default class UndeclaredVarE extends E { - constructor(scope: Scope, originNode: undefined | AnyNode, name: string) { - super(scope, originNode); - this.name = name; - } + constructor(scope: Scope, originNode: undefined | AnyNode, name: string) { + super(scope, originNode); + this.name = name; + } - static type = 'UndeclaredVarE'; - name: string; + static type = 'UndeclaredVarE'; + name: string; - getError(): ErrorDefinition { - const possibleNames = this.scope.getBindingNames(); - return { - description: descriptions.TYPE_CHECK.UNDECLARED_VARIABLE( - this.name, - possibleNames, - ), - lowerTarget: this, - }; - } + getError(): ErrorDefinition { + const possibleNames = this.scope.getBindingNames(); + return { + description: descriptions.TYPE_CHECK.UNDECLARED_VARIABLE( + this.name, + possibleNames, + ), + lowerTarget: this, + }; + } } diff --git a/packages/@romejs/js-analysis/types/errors/UnknownImportE.ts b/packages/@romejs/js-analysis/types/errors/UnknownImportE.ts index 1c4f186b29c..e7c789f7283 100644 --- a/packages/@romejs/js-analysis/types/errors/UnknownImportE.ts +++ b/packages/@romejs/js-analysis/types/errors/UnknownImportE.ts @@ -11,34 +11,34 @@ import E, {ErrorDefinition} from './E'; import {AnyNode} from '@romejs/js-ast'; export default class UnknownImportE extends E { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - opts: { - possibleNames: Array; - importedName: string; - source: string; - }, - ) { - super(scope, originNode); - this.possibleNames = opts.possibleNames; - this.importedName = opts.importedName; - this.source = opts.source; - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + opts: { + possibleNames: Array; + importedName: string; + source: string; + }, + ) { + super(scope, originNode); + this.possibleNames = opts.possibleNames; + this.importedName = opts.importedName; + this.source = opts.source; + } - static type = 'UnknownImportE'; - importedName: string; - source: string; - possibleNames: Array; + static type = 'UnknownImportE'; + importedName: string; + source: string; + possibleNames: Array; - getError(): ErrorDefinition { - return { - description: descriptions.TYPE_CHECK.UNKNOWN_IMPORT( - this.importedName, - this.source, - this.possibleNames, - ), - lowerTarget: this, - }; - } + getError(): ErrorDefinition { + return { + description: descriptions.TYPE_CHECK.UNKNOWN_IMPORT( + this.importedName, + this.source, + this.possibleNames, + ), + lowerTarget: this, + }; + } } diff --git a/packages/@romejs/js-analysis/types/errors/UnknownPropE.ts b/packages/@romejs/js-analysis/types/errors/UnknownPropE.ts index 86094169ead..d4448cd88f1 100644 --- a/packages/@romejs/js-analysis/types/errors/UnknownPropE.ts +++ b/packages/@romejs/js-analysis/types/errors/UnknownPropE.ts @@ -13,49 +13,49 @@ import E, {ErrorDefinition} from './E'; import {AnyNode} from '@romejs/js-ast'; export default class UnknownPropE extends E { - constructor( - scope: Scope, - originNode: undefined | AnyNode, - opts: { - object: T; - property: T; - key: string; - thisKeys: Array; - protoKeys: Array; - }, - ) { - super(scope, originNode); - this.thisKeys = opts.thisKeys; - this.protoKeys = opts.protoKeys; - this.allProps = [...this.thisKeys, ...this.protoKeys]; - this.key = opts.key; - this.object = opts.object; - this.property = opts.property; - } + constructor( + scope: Scope, + originNode: undefined | AnyNode, + opts: { + object: T; + property: T; + key: string; + thisKeys: Array; + protoKeys: Array; + }, + ) { + super(scope, originNode); + this.thisKeys = opts.thisKeys; + this.protoKeys = opts.protoKeys; + this.allProps = [...this.thisKeys, ...this.protoKeys]; + this.key = opts.key; + this.object = opts.object; + this.property = opts.property; + } - static type = 'UnknownPropE'; - allProps: Array; - thisKeys: Array; - protoKeys: Array; - property: T; - object: T; - key: string; + static type = 'UnknownPropE'; + allProps: Array; + thisKeys: Array; + protoKeys: Array; + property: T; + object: T; + key: string; - sortProps(props: Array): Array { - if (props.length === 0) { - return props; - } + sortProps(props: Array): Array { + if (props.length === 0) { + return props; + } - const ratings = orderBySimilarity(this.key, props); - const sortedProps = ratings.map((prop) => prop.target); - return sortedProps; - } + const ratings = orderBySimilarity(this.key, props); + const sortedProps = ratings.map((prop) => prop.target); + return sortedProps; + } - getError(): ErrorDefinition { - return { - description: descriptions.TYPE_CHECK.UNKNOWN_PROP(this.key, this.allProps), - lowerTarget: this.property, - upperTarget: this.object, - }; - } + getError(): ErrorDefinition { + return { + description: descriptions.TYPE_CHECK.UNKNOWN_PROP(this.key, this.allProps), + lowerTarget: this.property, + upperTarget: this.object, + }; + } } diff --git a/packages/@romejs/js-analysis/utils/executeAtom.ts b/packages/@romejs/js-analysis/utils/executeAtom.ts index b9d121e647f..5c1c1fa3702 100644 --- a/packages/@romejs/js-analysis/utils/executeAtom.ts +++ b/packages/@romejs/js-analysis/utils/executeAtom.ts @@ -13,52 +13,52 @@ import StringLiteralT from '../types/StringLiteralT'; import GetPropT from '../types/GetPropT'; export default function executeAtom( - leftNode: AnyNode, - rightType: T, - scope: Scope, + leftNode: AnyNode, + rightType: T, + scope: Scope, ) { - switch (leftNode.type) { - case 'BindingIdentifier': { - scope.addBinding(leftNode.name, rightType); - break; - } + switch (leftNode.type) { + case 'BindingIdentifier': { + scope.addBinding(leftNode.name, rightType); + break; + } - case 'BindingObjectPattern': { - for (const prop of leftNode.properties) { - executeAtom(prop, rightType, scope); - } - break; - } + case 'BindingObjectPattern': { + for (const prop of leftNode.properties) { + executeAtom(prop, rightType, scope); + } + break; + } - case 'BindingObjectPatternProperty': { - const {key} = leftNode; - if (key.type === 'ComputedPropertyKey' || key.value.type !== 'Identifier') { - throw new Error('unimplemented'); - } + case 'BindingObjectPatternProperty': { + const {key} = leftNode; + if (key.type === 'ComputedPropertyKey' || key.value.type !== 'Identifier') { + throw new Error('unimplemented'); + } - const propKey = new StringLiteralT(scope, key, key.value.name); - const getProp = new GetPropT(scope, leftNode, rightType, propKey); - executeAtom(leftNode.value, getProp, scope); - break; - } + const propKey = new StringLiteralT(scope, key, key.value.name); + const getProp = new GetPropT(scope, leftNode, rightType, propKey); + executeAtom(leftNode.value, getProp, scope); + break; + } - case 'BindingArrayPattern': { - for (let i = 0; i < leftNode.elements.length; i++) { - const elem = leftNode.elements[i]; - if (elem === undefined) { - continue; - } + case 'BindingArrayPattern': { + for (let i = 0; i < leftNode.elements.length; i++) { + const elem = leftNode.elements[i]; + if (elem === undefined) { + continue; + } - const propKey = new NumericLiteralT(scope, elem, i); - const getProp = new GetPropT(scope, leftNode, rightType, propKey); - executeAtom(elem, getProp, scope); - } - break; - } + const propKey = new NumericLiteralT(scope, elem, i); + const getProp = new GetPropT(scope, leftNode, rightType, propKey); + executeAtom(elem, getProp, scope); + } + break; + } - case 'BindingAssignmentPattern': { - executeAtom(leftNode.left, rightType, scope); - break; - } - } + case 'BindingAssignmentPattern': { + executeAtom(leftNode.left, rightType, scope); + break; + } + } } diff --git a/packages/@romejs/js-analysis/utils/executeFunction.ts b/packages/@romejs/js-analysis/utils/executeFunction.ts index 89df105eebf..12f3a8b87e6 100644 --- a/packages/@romejs/js-analysis/utils/executeFunction.ts +++ b/packages/@romejs/js-analysis/utils/executeFunction.ts @@ -15,74 +15,74 @@ import VoidT from '../types/VoidT'; import OpenT from '../types/OpenT'; export default function executeFunction( - node: AnyFunction, - scope: Scope, - bindId: boolean, - thisContext?: T, + node: AnyFunction, + scope: Scope, + bindId: boolean, + thisContext?: T, ): FunctionT { - const {head} = node; + const {head} = node; - // build return type - const returns = new OpenT(scope, head.returnType ? head.returnType : node); + // build return type + const returns = new OpenT(scope, head.returnType ? head.returnType : node); - // type check the body - const bodyScope = new FunctionScope( - { - parentScope: scope, - }, - { - thisContext: thisContext ? thisContext : new VoidT(scope, undefined), - returnType: returns, - }, - ); - if (head.typeParameters) { - bodyScope.evaluate(head.typeParameters); - } + // type check the body + const bodyScope = new FunctionScope( + { + parentScope: scope, + }, + { + thisContext: thisContext ? thisContext : new VoidT(scope, undefined), + returnType: returns, + }, + ); + if (head.typeParameters) { + bodyScope.evaluate(head.typeParameters); + } - // build param types - const params = []; - let rest; - for (let paramNode of head.params) { - let optional = - paramNode.meta !== undefined && paramNode.meta.optional === true; - if (paramNode.type === 'BindingAssignmentPattern') { - optional = false; - paramNode = paramNode.left; - } + // build param types + const params = []; + let rest; + for (let paramNode of head.params) { + let optional = + paramNode.meta !== undefined && paramNode.meta.optional === true; + if (paramNode.type === 'BindingAssignmentPattern') { + optional = false; + paramNode = paramNode.left; + } - let paramType; - if ( - paramNode.meta !== undefined && - paramNode.meta.typeAnnotation !== undefined - ) { - paramType = scope.evaluate(paramNode.meta.typeAnnotation); - } else { - paramType = new OpenT(scope, paramNode); - } + let paramType; + if ( + paramNode.meta !== undefined && + paramNode.meta.typeAnnotation !== undefined + ) { + paramType = scope.evaluate(paramNode.meta.typeAnnotation); + } else { + paramType = new OpenT(scope, paramNode); + } - if (optional) { - paramType = new MaybeT(scope, paramNode, paramType); - } + if (optional) { + paramType = new MaybeT(scope, paramNode, paramType); + } - params.push(paramType); - } + params.push(paramType); + } - for (let i = 0; i < head.params.length; i++) { - executeAtom(head.params[i], params[i], scope); - } - const block = bodyScope.evaluate(node.body); + for (let i = 0; i < head.params.length; i++) { + executeAtom(head.params[i], params[i], scope); + } + const block = bodyScope.evaluate(node.body); - // if no types have flowed into the return type then it'll return undefined - if (returns.hasConnections() === false) { - //const ret = new VoidT(scope, node); - //returns.shouldMatch(ret); - } + // if no types have flowed into the return type then it'll return undefined + if (returns.hasConnections() === false) { + //const ret = new VoidT(scope, node); + //returns.shouldMatch(ret); + } - if (head.returnType) { - returns.shouldMatch(scope.evaluate(head.returnType)); - } + if (head.returnType) { + returns.shouldMatch(scope.evaluate(head.returnType)); + } - // create the function - const func = new FunctionT(scope, node, {params, rest, returns, body: block}); - return func; + // create the function + const func = new FunctionT(scope, node, {params, rest, returns, body: block}); + return func; } diff --git a/packages/@romejs/js-analysis/utils/refine.ts b/packages/@romejs/js-analysis/utils/refine.ts index 08f317635dc..a9f0c410e2a 100644 --- a/packages/@romejs/js-analysis/utils/refine.ts +++ b/packages/@romejs/js-analysis/utils/refine.ts @@ -14,218 +14,218 @@ import UnionT from '../types/UnionT'; import RefineTypeofT from '../types/RefineTypeofT'; type TypeDefinition = { - name: string; - value: T; + name: string; + value: T; }; type TypeDefinitions = Array; function typesToMap(types: TypeDefinitions): Map { - const map: Map = new Map(); - for (const {name, value} of types) { - map.set(name, value); - } - return map; + const map: Map = new Map(); + for (const {name, value} of types) { + map.set(name, value); + } + return map; } function isTypeofNode( - node: AnyNode, + node: AnyNode, ): node is UnaryExpression & { - argument: ReferenceIdentifier; + argument: ReferenceIdentifier; } { - return ( - node.type === 'UnaryExpression' && - node.operator === 'typeof' && - node.argument.type === 'ReferenceIdentifier' - ); + return ( + node.type === 'UnaryExpression' && + node.operator === 'typeof' && + node.argument.type === 'ReferenceIdentifier' + ); } function genTypes(node: AnyNode, scope: Scope): Array { - const evaluator: Evaluator = scope.evaluator; - let types = []; - - switch (node.type) { - case 'BinaryExpression': { - const {left, right} = node; - switch (node.operator) { - case '==': - return []; - - case '!=': - return []; - - case '===': { - // typeof foo === 'string' - if (isTypeofNode(left)) { - const name = left.argument.name; - const binding = scope.getBinding(name); - if (binding !== undefined) { - types.push({ - name, - value: new RefineTypeofT( - scope, - node, - evaluator.getTypeFromEvaluatedNode(right), - binding, - ), - }); - } - } - - // foo === 'bar' - if (left.type === 'ReferenceIdentifier') { - types.push({ - name: left.name, - value: evaluator.getTypeFromEvaluatedNode(right), - }); - } - - // 'string' === typeof foo - if (isTypeofNode(right)) { - const name = right.argument.name; - const binding = scope.getBinding(name); - if (binding !== undefined) { - types.push({ - name, - value: new RefineTypeofT( - scope, - node, - evaluator.getTypeFromEvaluatedNode(left), - binding, - ), - }); - } - } - - // 'bar' === foo - if (right.type === 'ReferenceIdentifier') { - types.push({ - name: right.name, - value: evaluator.getTypeFromEvaluatedNode(left), - }); - } - break; - } - - case '!==': { - // TODO add `typeof` - if (left.type === 'ReferenceIdentifier') { - types.push({ - name: left.name, - value: new RefinedT( - scope, - left, - evaluator.getTypeFromEvaluatedNode(left), - evaluator.getTypeFromEvaluatedNode(right), - ), - }); - } - if (right.type === 'ReferenceIdentifier') { - types.push({ - name: right.name, - value: new RefinedT( - scope, - right, - evaluator.getTypeFromEvaluatedNode(right), - evaluator.getTypeFromEvaluatedNode(left), - ), - }); - } - return types; - } - - case 'instanceof': - return []; - - default: - throw new Error('Unknown BinaryExpression operator'); - } - break; - } - - case 'LogicalExpression': - switch (node.operator) { - case '||': { - const leftMap = typesToMap(genTypes(node.left, scope)); - const rightMap = typesToMap(genTypes(node.right, scope)); - const names = new Set([...leftMap.keys(), ...rightMap.keys()]); - - return Array.from( - names, - (name: string): TypeDefinition => { - const left = leftMap.get(name); - const right = rightMap.get(name); - - let type; - - if (left === undefined) { - type = right; - } else if (right === undefined) { - type = left; - } else { - type = new UnionT(scope, undefined, [left, right]); - } - - if (type === undefined) { - throw new Error('Expected type'); - } - - return { - name, - value: type, - }; - }, - ); - } - - case '&&': - return [...genTypes(node.left, scope), ...genTypes(node.right, scope)]; - } - } - - return types; + const evaluator: Evaluator = scope.evaluator; + let types = []; + + switch (node.type) { + case 'BinaryExpression': { + const {left, right} = node; + switch (node.operator) { + case '==': + return []; + + case '!=': + return []; + + case '===': { + // typeof foo === 'string' + if (isTypeofNode(left)) { + const name = left.argument.name; + const binding = scope.getBinding(name); + if (binding !== undefined) { + types.push({ + name, + value: new RefineTypeofT( + scope, + node, + evaluator.getTypeFromEvaluatedNode(right), + binding, + ), + }); + } + } + + // foo === 'bar' + if (left.type === 'ReferenceIdentifier') { + types.push({ + name: left.name, + value: evaluator.getTypeFromEvaluatedNode(right), + }); + } + + // 'string' === typeof foo + if (isTypeofNode(right)) { + const name = right.argument.name; + const binding = scope.getBinding(name); + if (binding !== undefined) { + types.push({ + name, + value: new RefineTypeofT( + scope, + node, + evaluator.getTypeFromEvaluatedNode(left), + binding, + ), + }); + } + } + + // 'bar' === foo + if (right.type === 'ReferenceIdentifier') { + types.push({ + name: right.name, + value: evaluator.getTypeFromEvaluatedNode(left), + }); + } + break; + } + + case '!==': { + // TODO add `typeof` + if (left.type === 'ReferenceIdentifier') { + types.push({ + name: left.name, + value: new RefinedT( + scope, + left, + evaluator.getTypeFromEvaluatedNode(left), + evaluator.getTypeFromEvaluatedNode(right), + ), + }); + } + if (right.type === 'ReferenceIdentifier') { + types.push({ + name: right.name, + value: new RefinedT( + scope, + right, + evaluator.getTypeFromEvaluatedNode(right), + evaluator.getTypeFromEvaluatedNode(left), + ), + }); + } + return types; + } + + case 'instanceof': + return []; + + default: + throw new Error('Unknown BinaryExpression operator'); + } + break; + } + + case 'LogicalExpression': + switch (node.operator) { + case '||': { + const leftMap = typesToMap(genTypes(node.left, scope)); + const rightMap = typesToMap(genTypes(node.right, scope)); + const names = new Set([...leftMap.keys(), ...rightMap.keys()]); + + return Array.from( + names, + (name: string): TypeDefinition => { + const left = leftMap.get(name); + const right = rightMap.get(name); + + let type; + + if (left === undefined) { + type = right; + } else if (right === undefined) { + type = left; + } else { + type = new UnionT(scope, undefined, [left, right]); + } + + if (type === undefined) { + throw new Error('Expected type'); + } + + return { + name, + value: type, + }; + }, + ); + } + + case '&&': + return [...genTypes(node.left, scope), ...genTypes(node.right, scope)]; + } + } + + return types; } export default function refine( - test: AnyNode, - outerScope: Scope, - hasAlternate: boolean, + test: AnyNode, + outerScope: Scope, + hasAlternate: boolean, ): { - consequent: Scope; - alternate: Scope; + consequent: Scope; + alternate: Scope; } { - const consequent = outerScope.fork(); - const alternate = outerScope.fork(); - - const rawTestTypes = genTypes(test, outerScope); - - const testTypes: Map> = new Map(); - for (const {name, value} of rawTestTypes) { - let types = testTypes.get(name); - if (types === undefined) { - types = []; - testTypes.set(name, types); - } - - types.push(value); - } - - for (const [name, types] of testTypes) { - // Build up the type in the case it's been refined to multiple values - const type = - types.length === 1 ? types[0] : new UnionT(outerScope, undefined, types); - - // Set type on `consequent` - consequent.addBinding(name, type); - - // Remove type from '`alternate` - if (hasAlternate) { - const binding = outerScope.getBindingAssert(name); - const opposite = new RefinedT(outerScope, type.originNode, binding, type); - alternate.addBinding(name, opposite); - } - } - - // TODO, get binding refinements that were made inside - return {consequent, alternate}; + const consequent = outerScope.fork(); + const alternate = outerScope.fork(); + + const rawTestTypes = genTypes(test, outerScope); + + const testTypes: Map> = new Map(); + for (const {name, value} of rawTestTypes) { + let types = testTypes.get(name); + if (types === undefined) { + types = []; + testTypes.set(name, types); + } + + types.push(value); + } + + for (const [name, types] of testTypes) { + // Build up the type in the case it's been refined to multiple values + const type = + types.length === 1 ? types[0] : new UnionT(outerScope, undefined, types); + + // Set type on `consequent` + consequent.addBinding(name, type); + + // Remove type from '`alternate` + if (hasAlternate) { + const binding = outerScope.getBindingAssert(name); + const opposite = new RefinedT(outerScope, type.originNode, binding, type); + alternate.addBinding(name, opposite); + } + } + + // TODO, get binding refinements that were made inside + return {consequent, alternate}; } diff --git a/packages/@romejs/js-ast-utils/assertMultipleNodes.ts b/packages/@romejs/js-ast-utils/assertMultipleNodes.ts index b123eebc209..7b2ccc49997 100644 --- a/packages/@romejs/js-ast-utils/assertMultipleNodes.ts +++ b/packages/@romejs/js-ast-utils/assertMultipleNodes.ts @@ -9,15 +9,15 @@ import {TransformExitResult} from '@romejs/js-compiler'; import {AnyNode} from '@romejs/js-ast'; export default function assertMultipleNodes( - result: TransformExitResult, + result: TransformExitResult, ): Array { - if (Array.isArray(result)) { - return result; - } else if (result === undefined) { - return []; - } else if (typeof result === 'symbol') { - throw new Error('No symbols expected here'); - } else { - return [result]; - } + if (Array.isArray(result)) { + return result; + } else if (result === undefined) { + return []; + } else if (typeof result === 'symbol') { + throw new Error('No symbols expected here'); + } else { + return [result]; + } } diff --git a/packages/@romejs/js-ast-utils/assertSingleNode.ts b/packages/@romejs/js-ast-utils/assertSingleNode.ts index 914ee55cb05..ee9133d914c 100644 --- a/packages/@romejs/js-ast-utils/assertSingleNode.ts +++ b/packages/@romejs/js-ast-utils/assertSingleNode.ts @@ -9,16 +9,16 @@ import {TransformExitResult} from '@romejs/js-compiler'; import {AnyNode} from '@romejs/js-ast'; export default function assertSingleNode(result: TransformExitResult): AnyNode { - if (Array.isArray(result)) { - if (result.length !== 1) { - throw new Error(`Expected node list length of 1 but got ${result.length}`); - } - return result[0]; - } else if (result === undefined) { - throw new Error('Expected node or node list but got null'); - } else if (typeof result === 'symbol') { - throw new Error('No symbols expected here'); - } else { - return result; - } + if (Array.isArray(result)) { + if (result.length !== 1) { + throw new Error(`Expected node list length of 1 but got ${result.length}`); + } + return result[0]; + } else if (result === undefined) { + throw new Error('Expected node or node list but got null'); + } else if (typeof result === 'symbol') { + throw new Error('No symbols expected here'); + } else { + return result; + } } diff --git a/packages/@romejs/js-ast-utils/assertSingleOrMultipleNodes.ts b/packages/@romejs/js-ast-utils/assertSingleOrMultipleNodes.ts index e0f819e4aae..ab15edd1da0 100644 --- a/packages/@romejs/js-ast-utils/assertSingleOrMultipleNodes.ts +++ b/packages/@romejs/js-ast-utils/assertSingleOrMultipleNodes.ts @@ -9,13 +9,13 @@ import {TransformExitResult} from '@romejs/js-compiler'; import {AnyNode} from '@romejs/js-ast'; export default function assertSingleOrMultipleNodes( - result: TransformExitResult, + result: TransformExitResult, ): AnyNode | Array { - if (result === undefined) { - throw new Error('Expected node or node list but got null'); - } else if (typeof result === 'symbol') { - throw new Error('No symbols expected here'); - } else { - return result; - } + if (result === undefined) { + throw new Error('Expected node or node list but got null'); + } else if (typeof result === 'symbol') { + throw new Error('No symbols expected here'); + } else { + return result; + } } diff --git a/packages/@romejs/js-ast-utils/createMemberProperty.ts b/packages/@romejs/js-ast-utils/createMemberProperty.ts index ae0aad6dc32..0b009ab3977 100644 --- a/packages/@romejs/js-ast-utils/createMemberProperty.ts +++ b/packages/@romejs/js-ast-utils/createMemberProperty.ts @@ -6,21 +6,21 @@ */ import { - ComputedMemberProperty, - StaticMemberProperty, - computedMemberProperty, - identifier, - staticMemberProperty, - stringLiteral, + ComputedMemberProperty, + StaticMemberProperty, + computedMemberProperty, + identifier, + staticMemberProperty, + stringLiteral, } from '@romejs/js-ast'; import isValidIdentifierName from './isValidIdentifierName'; export default function createMemberProperty( - name: string, + name: string, ): StaticMemberProperty | ComputedMemberProperty { - if (isValidIdentifierName(name)) { - return staticMemberProperty.quick(identifier.quick(name)); - } else { - return computedMemberProperty.quick(stringLiteral.quick(name)); - } + if (isValidIdentifierName(name)) { + return staticMemberProperty.quick(identifier.quick(name)); + } else { + return computedMemberProperty.quick(stringLiteral.quick(name)); + } } diff --git a/packages/@romejs/js-ast-utils/createPropertyKey.ts b/packages/@romejs/js-ast-utils/createPropertyKey.ts index 039315edd12..7671a9ffb8e 100644 --- a/packages/@romejs/js-ast-utils/createPropertyKey.ts +++ b/packages/@romejs/js-ast-utils/createPropertyKey.ts @@ -7,18 +7,18 @@ import isValidIdentifierName from './isValidIdentifierName'; import { - Identifier, - StringLiteral, - identifier, - stringLiteral, + Identifier, + StringLiteral, + identifier, + stringLiteral, } from '@romejs/js-ast'; export default function createPropertyKey( - name: string, + name: string, ): Identifier | StringLiteral { - if (isValidIdentifierName(name)) { - return identifier.quick(name); - } else { - return stringLiteral.quick(name); - } + if (isValidIdentifierName(name)) { + return identifier.quick(name); + } else { + return stringLiteral.quick(name); + } } diff --git a/packages/@romejs/js-ast-utils/doesNodeMatchPattern.test.ts b/packages/@romejs/js-ast-utils/doesNodeMatchPattern.test.ts index 7986e0f5369..09bf61f146b 100644 --- a/packages/@romejs/js-ast-utils/doesNodeMatchPattern.test.ts +++ b/packages/@romejs/js-ast-utils/doesNodeMatchPattern.test.ts @@ -10,36 +10,33 @@ import doesNodeMatchPattern from './doesNodeMatchPattern'; import template from './template'; test( - 'doesNodeMatchPattern', - (t) => { - t.inlineSnapshot( - doesNodeMatchPattern(template.expression`foo`, 'foo'), - true, - ); + 'doesNodeMatchPattern', + (t) => { + t.inlineSnapshot(doesNodeMatchPattern(template.expression`foo`, 'foo'), true); - t.inlineSnapshot( - doesNodeMatchPattern(template.expression`this.foo`, 'this.foo'), - true, - ); + t.inlineSnapshot( + doesNodeMatchPattern(template.expression`this.foo`, 'this.foo'), + true, + ); - t.inlineSnapshot( - doesNodeMatchPattern(template.expression`exports.foo`, 'exports.**'), - true, - ); + t.inlineSnapshot( + doesNodeMatchPattern(template.expression`exports.foo`, 'exports.**'), + true, + ); - t.inlineSnapshot( - doesNodeMatchPattern(template.expression`this.foo.bar`, 'this.foo.*'), - true, - ); + t.inlineSnapshot( + doesNodeMatchPattern(template.expression`this.foo.bar`, 'this.foo.*'), + true, + ); - t.inlineSnapshot( - doesNodeMatchPattern(template.expression`this.foo.bar.yes`, 'this.foo.*'), - false, - ); + t.inlineSnapshot( + doesNodeMatchPattern(template.expression`this.foo.bar.yes`, 'this.foo.*'), + false, + ); - t.inlineSnapshot( - doesNodeMatchPattern(template.expression`this.foo.bar.yes`, 'this.foo.**'), - true, - ); - }, + t.inlineSnapshot( + doesNodeMatchPattern(template.expression`this.foo.bar.yes`, 'this.foo.**'), + true, + ); + }, ); diff --git a/packages/@romejs/js-ast-utils/doesNodeMatchPattern.ts b/packages/@romejs/js-ast-utils/doesNodeMatchPattern.ts index bc0fec2b4f4..1aef69e58a1 100644 --- a/packages/@romejs/js-ast-utils/doesNodeMatchPattern.ts +++ b/packages/@romejs/js-ast-utils/doesNodeMatchPattern.ts @@ -12,122 +12,122 @@ import isIdentifierish from './isIdentifierish'; const splitCache: Map = new Map(); type SplitResult = { - hasDoubleStar: boolean; - parts: Array; + hasDoubleStar: boolean; + parts: Array; }; function split(str: string): SplitResult { - const cached = splitCache.get(str); - if (cached !== undefined) { - return cached; - } - - const parts = str.split('.'); - - let hasDoubleStar = false; - for (const part of parts) { - if (part === '**') { - hasDoubleStar = true; - break; - } - } - - const result: SplitResult = {parts, hasDoubleStar}; - splitCache.set(str, result); - return result; + const cached = splitCache.get(str); + if (cached !== undefined) { + return cached; + } + + const parts = str.split('.'); + + let hasDoubleStar = false; + for (const part of parts) { + if (part === '**') { + hasDoubleStar = true; + break; + } + } + + const result: SplitResult = {parts, hasDoubleStar}; + splitCache.set(str, result); + return result; } export default function doesNodeMatchPattern( - node: undefined | AnyNode, - match: string, + node: undefined | AnyNode, + match: string, ): boolean { - if (node === undefined) { - return false; - } - - // Not a member expression - if (node.type !== 'MemberExpression' && !isIdentifierish(node)) { - return false; - } - - const {parts: expectedParts, hasDoubleStar} = split(match); - - const {bailed, parts: actualParts} = getNodeReferenceParts(node); - - // Bailed will be true if we were unable to derive a name for one of the parts - if (bailed && !hasDoubleStar) { - return false; - } - - // If there's less parts than the amount we expect then it's never going to match - if (actualParts.length < expectedParts.length) { - return false; - } - - // I there's more parts than we expect then it's never going to match either - if (!hasDoubleStar && actualParts.length > expectedParts.length) { - return false; - } - - let nextActualIndex = 0; - let nextExpectedIndex = 0; - - // Loop over the parts we received and match them - while (nextActualIndex < actualParts.length) { - // If we have no more expected parts then we can't possibly match it - if (nextActualIndex >= expectedParts.length) { - return false; - } - - const actual = actualParts[nextActualIndex].value; - nextActualIndex++; - - const expected = expectedParts[nextExpectedIndex]; - nextExpectedIndex++; - - // A star part can accept anything - if (expected === '*') { - continue; - } - - if (expected === '**') { - // Ran out of matches but we've accepted the current part - if (nextExpectedIndex >= expectedParts.length) { - return true; - } - - const next = expectedParts[nextExpectedIndex]; - nextExpectedIndex++; - - if (next === '*' || next === '**') { - throw new Error( - `The next expected part was ${next} but this isn't allowed since we're processing a double star`, - ); - } - - let found = false; - - // Eat as many parts until we find the next expected part - while (nextActualIndex < actualParts.length) { - const actual = actualParts[nextActualIndex].value; - nextActualIndex++; - if (actual === next) { - found = true; - break; - } - } - - if (found) { - continue; - } else { - return false; - } - } - - if (expected !== actual) { - return false; - } - } - - return true; + if (node === undefined) { + return false; + } + + // Not a member expression + if (node.type !== 'MemberExpression' && !isIdentifierish(node)) { + return false; + } + + const {parts: expectedParts, hasDoubleStar} = split(match); + + const {bailed, parts: actualParts} = getNodeReferenceParts(node); + + // Bailed will be true if we were unable to derive a name for one of the parts + if (bailed && !hasDoubleStar) { + return false; + } + + // If there's less parts than the amount we expect then it's never going to match + if (actualParts.length < expectedParts.length) { + return false; + } + + // I there's more parts than we expect then it's never going to match either + if (!hasDoubleStar && actualParts.length > expectedParts.length) { + return false; + } + + let nextActualIndex = 0; + let nextExpectedIndex = 0; + + // Loop over the parts we received and match them + while (nextActualIndex < actualParts.length) { + // If we have no more expected parts then we can't possibly match it + if (nextActualIndex >= expectedParts.length) { + return false; + } + + const actual = actualParts[nextActualIndex].value; + nextActualIndex++; + + const expected = expectedParts[nextExpectedIndex]; + nextExpectedIndex++; + + // A star part can accept anything + if (expected === '*') { + continue; + } + + if (expected === '**') { + // Ran out of matches but we've accepted the current part + if (nextExpectedIndex >= expectedParts.length) { + return true; + } + + const next = expectedParts[nextExpectedIndex]; + nextExpectedIndex++; + + if (next === '*' || next === '**') { + throw new Error( + `The next expected part was ${next} but this isn't allowed since we're processing a double star`, + ); + } + + let found = false; + + // Eat as many parts until we find the next expected part + while (nextActualIndex < actualParts.length) { + const actual = actualParts[nextActualIndex].value; + nextActualIndex++; + if (actual === next) { + found = true; + break; + } + } + + if (found) { + continue; + } else { + return false; + } + } + + if (expected !== actual) { + return false; + } + } + + return true; } diff --git a/packages/@romejs/js-ast-utils/getBindingIdentifiers.ts b/packages/@romejs/js-ast-utils/getBindingIdentifiers.ts index fa900194e53..af1339ce67c 100644 --- a/packages/@romejs/js-ast-utils/getBindingIdentifiers.ts +++ b/packages/@romejs/js-ast-utils/getBindingIdentifiers.ts @@ -8,41 +8,41 @@ import {AnyNode, BindingIdentifier, bindingKeys} from '@romejs/js-ast'; export default function getBindingIdentifiers( - node: AnyNode | Array, + node: AnyNode | Array, ): Array { - const ids: Array = []; - let queue: Array = Array.isArray(node) - ? [...node] - : [node]; + const ids: Array = []; + let queue: Array = Array.isArray(node) + ? [...node] + : [node]; - while (queue.length) { - const node = queue.pop(); - if (node === undefined) { - continue; - } + while (queue.length) { + const node = queue.pop(); + if (node === undefined) { + continue; + } - if (node.type === 'BindingIdentifier') { - ids.push(node); - continue; - } + if (node.type === 'BindingIdentifier') { + ids.push(node); + continue; + } - const keys: undefined | Array = bindingKeys.get(node.type); - if (keys === undefined) { - continue; - } + const keys: undefined | Array = bindingKeys.get(node.type); + if (keys === undefined) { + continue; + } - for (const key of keys) { - // rome-ignore lint/noExplicitAny - const val = (node as any)[key]; - if (val === undefined) { - continue; - } else if (Array.isArray(val)) { - queue = queue.concat(val); - } else { - queue.push(val); - } - } - } + for (const key of keys) { + // rome-ignore lint/noExplicitAny + const val = (node as any)[key]; + if (val === undefined) { + continue; + } else if (Array.isArray(val)) { + queue = queue.concat(val); + } else { + queue.push(val); + } + } + } - return ids; + return ids; } diff --git a/packages/@romejs/js-ast-utils/getCompletionRecords.test.md b/packages/@romejs/js-ast-utils/getCompletionRecords.test.md index 15f29e26cff..4cf48afb8b8 100644 --- a/packages/@romejs/js-ast-utils/getCompletionRecords.test.md +++ b/packages/@romejs/js-ast-utils/getCompletionRecords.test.md @@ -8,39 +8,39 @@ ```javascript Array [ - COMPLETION { - node: ReturnStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 28 - index: 28 - line: 1 - } - start: Object { - column: 15 - index: 15 - line: 1 - } - } - argument: BooleanLiteral { - value: false - loc: Object { - filename: 'unknown' - end: Object { - column: 27 - index: 27 - line: 1 - } - start: Object { - column: 22 - index: 22 - line: 1 - } - } - } - } - } + COMPLETION { + node: ReturnStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 28 + index: 28 + line: 1 + } + start: Object { + column: 15 + index: 15 + line: 1 + } + } + argument: BooleanLiteral { + value: false + loc: Object { + filename: 'unknown' + end: Object { + column: 27 + index: 27 + line: 1 + } + start: Object { + column: 22 + index: 22 + line: 1 + } + } + } + } + } ] ``` @@ -48,24 +48,24 @@ Array [ ```javascript Array [ - COMPLETION { - node: ReturnStatement { - argument: undefined - loc: Object { - filename: 'unknown' - end: Object { - column: 22 - index: 22 - line: 1 - } - start: Object { - column: 15 - index: 15 - line: 1 - } - } - } - } + COMPLETION { + node: ReturnStatement { + argument: undefined + loc: Object { + filename: 'unknown' + end: Object { + column: 22 + index: 22 + line: 1 + } + start: Object { + column: 15 + index: 15 + line: 1 + } + } + } + } ] ``` @@ -73,124 +73,124 @@ Array [ ```javascript Array [ - COMPLETION { - node: ReturnStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 38 - index: 38 - line: 1 - } - start: Object { - column: 25 - index: 25 - line: 1 - } - } - argument: BooleanLiteral { - value: false - loc: Object { - filename: 'unknown' - end: Object { - column: 37 - index: 37 - line: 1 - } - start: Object { - column: 32 - index: 32 - line: 1 - } - } - } - } - } - INVALID { - description: 'empty alternate' - node: IfStatement { - alternate: undefined - loc: Object { - filename: 'unknown' - end: Object { - column: 39 - index: 39 - line: 1 - } - start: Object { - column: 15 - index: 15 - line: 1 - } - } - test: ReferenceIdentifier { - name: 'bar' - loc: Object { - filename: 'unknown' - identifierName: 'bar' - end: Object { - column: 22 - index: 22 - line: 1 - } - start: Object { - column: 19 - index: 19 - line: 1 - } - } - } - consequent: BlockStatement { - directives: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 39 - index: 39 - line: 1 - } - start: Object { - column: 24 - index: 24 - line: 1 - } - } - body: Array [ - ReturnStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 38 - index: 38 - line: 1 - } - start: Object { - column: 25 - index: 25 - line: 1 - } - } - argument: BooleanLiteral { - value: false - loc: Object { - filename: 'unknown' - end: Object { - column: 37 - index: 37 - line: 1 - } - start: Object { - column: 32 - index: 32 - line: 1 - } - } - } - } - ] - } - } - } + COMPLETION { + node: ReturnStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 38 + index: 38 + line: 1 + } + start: Object { + column: 25 + index: 25 + line: 1 + } + } + argument: BooleanLiteral { + value: false + loc: Object { + filename: 'unknown' + end: Object { + column: 37 + index: 37 + line: 1 + } + start: Object { + column: 32 + index: 32 + line: 1 + } + } + } + } + } + INVALID { + description: 'empty alternate' + node: IfStatement { + alternate: undefined + loc: Object { + filename: 'unknown' + end: Object { + column: 39 + index: 39 + line: 1 + } + start: Object { + column: 15 + index: 15 + line: 1 + } + } + test: ReferenceIdentifier { + name: 'bar' + loc: Object { + filename: 'unknown' + identifierName: 'bar' + end: Object { + column: 22 + index: 22 + line: 1 + } + start: Object { + column: 19 + index: 19 + line: 1 + } + } + } + consequent: BlockStatement { + directives: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 39 + index: 39 + line: 1 + } + start: Object { + column: 24 + index: 24 + line: 1 + } + } + body: Array [ + ReturnStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 38 + index: 38 + line: 1 + } + start: Object { + column: 25 + index: 25 + line: 1 + } + } + argument: BooleanLiteral { + value: false + loc: Object { + filename: 'unknown' + end: Object { + column: 37 + index: 37 + line: 1 + } + start: Object { + column: 32 + index: 32 + line: 1 + } + } + } + } + ] + } + } + } ] ``` @@ -198,72 +198,72 @@ Array [ ```javascript Array [ - COMPLETION { - node: ReturnStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 38 - index: 38 - line: 1 - } - start: Object { - column: 25 - index: 25 - line: 1 - } - } - argument: BooleanLiteral { - value: false - loc: Object { - filename: 'unknown' - end: Object { - column: 37 - index: 37 - line: 1 - } - start: Object { - column: 32 - index: 32 - line: 1 - } - } - } - } - } - COMPLETION { - node: ReturnStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 58 - index: 58 - line: 1 - } - start: Object { - column: 46 - index: 46 - line: 1 - } - } - argument: BooleanLiteral { - value: true - loc: Object { - filename: 'unknown' - end: Object { - column: 57 - index: 57 - line: 1 - } - start: Object { - column: 53 - index: 53 - line: 1 - } - } - } - } - } + COMPLETION { + node: ReturnStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 38 + index: 38 + line: 1 + } + start: Object { + column: 25 + index: 25 + line: 1 + } + } + argument: BooleanLiteral { + value: false + loc: Object { + filename: 'unknown' + end: Object { + column: 37 + index: 37 + line: 1 + } + start: Object { + column: 32 + index: 32 + line: 1 + } + } + } + } + } + COMPLETION { + node: ReturnStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 58 + index: 58 + line: 1 + } + start: Object { + column: 46 + index: 46 + line: 1 + } + } + argument: BooleanLiteral { + value: true + loc: Object { + filename: 'unknown' + end: Object { + column: 57 + index: 57 + line: 1 + } + start: Object { + column: 53 + index: 53 + line: 1 + } + } + } + } + } ] ``` @@ -271,39 +271,39 @@ Array [ ```javascript Array [ - COMPLETION { - node: ReturnStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 52 - index: 52 - line: 1 - } - start: Object { - column: 39 - index: 39 - line: 1 - } - } - argument: BooleanLiteral { - value: false - loc: Object { - filename: 'unknown' - end: Object { - column: 51 - index: 51 - line: 1 - } - start: Object { - column: 46 - index: 46 - line: 1 - } - } - } - } - } + COMPLETION { + node: ReturnStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 52 + index: 52 + line: 1 + } + start: Object { + column: 39 + index: 39 + line: 1 + } + } + argument: BooleanLiteral { + value: false + loc: Object { + filename: 'unknown' + end: Object { + column: 51 + index: 51 + line: 1 + } + start: Object { + column: 46 + index: 46 + line: 1 + } + } + } + } + } ] ``` @@ -311,39 +311,39 @@ Array [ ```javascript Array [ - COMPLETION { - node: ReturnStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 52 - index: 52 - line: 1 - } - start: Object { - column: 39 - index: 39 - line: 1 - } - } - argument: BooleanLiteral { - value: false - loc: Object { - filename: 'unknown' - end: Object { - column: 51 - index: 51 - line: 1 - } - start: Object { - column: 46 - index: 46 - line: 1 - } - } - } - } - } + COMPLETION { + node: ReturnStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 52 + index: 52 + line: 1 + } + start: Object { + column: 39 + index: 39 + line: 1 + } + } + argument: BooleanLiteral { + value: false + loc: Object { + filename: 'unknown' + end: Object { + column: 51 + index: 51 + line: 1 + } + start: Object { + column: 46 + index: 46 + line: 1 + } + } + } + } + } ] ``` @@ -353,26 +353,26 @@ Array [ ```javascript Array [ - INVALID { - description: 'empty block' - node: BlockStatement { - body: Array [] - directives: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 17 - index: 17 - line: 1 - } - start: Object { - column: 15 - index: 15 - line: 1 - } - } - } - } + INVALID { + description: 'empty block' + node: BlockStatement { + body: Array [] + directives: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 17 + index: 17 + line: 1 + } + start: Object { + column: 15 + index: 15 + line: 1 + } + } + } + } ] ``` @@ -380,43 +380,43 @@ Array [ ```javascript Array [ - INVALID { - description: 'empty block' - node: BlockStatement { - body: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 25 - index: 25 - line: 1 - } - start: Object { - column: 14 - index: 14 - line: 1 - } - } - directives: Array [ - Directive { - value: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 24 - index: 24 - line: 1 - } - start: Object { - column: 15 - index: 15 - line: 1 - } - } - } - ] - } - } + INVALID { + description: 'empty block' + node: BlockStatement { + body: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 25 + index: 25 + line: 1 + } + start: Object { + column: 14 + index: 14 + line: 1 + } + } + directives: Array [ + Directive { + value: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 24 + index: 24 + line: 1 + } + start: Object { + column: 15 + index: 15 + line: 1 + } + } + } + ] + } + } ] ``` @@ -424,143 +424,143 @@ Array [ ```javascript Array [ - INVALID { - description: 'empty block' - node: BlockStatement { - directives: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 35 - index: 35 - line: 1 - } - start: Object { - column: 24 - index: 24 - line: 1 - } - } - body: Array [ - ExpressionStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 34 - index: 34 - line: 1 - } - start: Object { - column: 25 - index: 25 - line: 1 - } - } - expression: StringLiteral { - value: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 33 - index: 33 - line: 1 - } - start: Object { - column: 25 - index: 25 - line: 1 - } - } - } - } - ] - } - } - INVALID { - description: 'empty alternate' - node: IfStatement { - alternate: undefined - loc: Object { - filename: 'unknown' - end: Object { - column: 35 - index: 35 - line: 1 - } - start: Object { - column: 15 - index: 15 - line: 1 - } - } - test: ReferenceIdentifier { - name: 'bar' - loc: Object { - filename: 'unknown' - identifierName: 'bar' - end: Object { - column: 22 - index: 22 - line: 1 - } - start: Object { - column: 19 - index: 19 - line: 1 - } - } - } - consequent: BlockStatement { - directives: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 35 - index: 35 - line: 1 - } - start: Object { - column: 24 - index: 24 - line: 1 - } - } - body: Array [ - ExpressionStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 34 - index: 34 - line: 1 - } - start: Object { - column: 25 - index: 25 - line: 1 - } - } - expression: StringLiteral { - value: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 33 - index: 33 - line: 1 - } - start: Object { - column: 25 - index: 25 - line: 1 - } - } - } - } - ] - } - } - } + INVALID { + description: 'empty block' + node: BlockStatement { + directives: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 35 + index: 35 + line: 1 + } + start: Object { + column: 24 + index: 24 + line: 1 + } + } + body: Array [ + ExpressionStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 34 + index: 34 + line: 1 + } + start: Object { + column: 25 + index: 25 + line: 1 + } + } + expression: StringLiteral { + value: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 33 + index: 33 + line: 1 + } + start: Object { + column: 25 + index: 25 + line: 1 + } + } + } + } + ] + } + } + INVALID { + description: 'empty alternate' + node: IfStatement { + alternate: undefined + loc: Object { + filename: 'unknown' + end: Object { + column: 35 + index: 35 + line: 1 + } + start: Object { + column: 15 + index: 15 + line: 1 + } + } + test: ReferenceIdentifier { + name: 'bar' + loc: Object { + filename: 'unknown' + identifierName: 'bar' + end: Object { + column: 22 + index: 22 + line: 1 + } + start: Object { + column: 19 + index: 19 + line: 1 + } + } + } + consequent: BlockStatement { + directives: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 35 + index: 35 + line: 1 + } + start: Object { + column: 24 + index: 24 + line: 1 + } + } + body: Array [ + ExpressionStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 34 + index: 34 + line: 1 + } + start: Object { + column: 25 + index: 25 + line: 1 + } + } + expression: StringLiteral { + value: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 33 + index: 33 + line: 1 + } + start: Object { + column: 25 + index: 25 + line: 1 + } + } + } + } + ] + } + } + } ] ``` @@ -568,78 +568,78 @@ Array [ ```javascript Array [ - INVALID { - description: 'empty block' - node: BlockStatement { - directives: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 35 - index: 35 - line: 1 - } - start: Object { - column: 24 - index: 24 - line: 1 - } - } - body: Array [ - ExpressionStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 34 - index: 34 - line: 1 - } - start: Object { - column: 25 - index: 25 - line: 1 - } - } - expression: StringLiteral { - value: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 33 - index: 33 - line: 1 - } - start: Object { - column: 25 - index: 25 - line: 1 - } - } - } - } - ] - } - } - INVALID { - description: 'empty block' - node: BlockStatement { - body: Array [] - directives: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 43 - index: 43 - line: 1 - } - start: Object { - column: 41 - index: 41 - line: 1 - } - } - } - } + INVALID { + description: 'empty block' + node: BlockStatement { + directives: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 35 + index: 35 + line: 1 + } + start: Object { + column: 24 + index: 24 + line: 1 + } + } + body: Array [ + ExpressionStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 34 + index: 34 + line: 1 + } + start: Object { + column: 25 + index: 25 + line: 1 + } + } + expression: StringLiteral { + value: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 33 + index: 33 + line: 1 + } + start: Object { + column: 25 + index: 25 + line: 1 + } + } + } + } + ] + } + } + INVALID { + description: 'empty block' + node: BlockStatement { + body: Array [] + directives: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 43 + index: 43 + line: 1 + } + start: Object { + column: 41 + index: 41 + line: 1 + } + } + } + } ] ``` @@ -647,42 +647,42 @@ Array [ ```javascript Array [ - INVALID { - description: 'switch with no default clause' - node: SwitchStatement { - cases: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 30 - index: 30 - line: 1 - } - start: Object { - column: 15 - index: 15 - line: 1 - } - } - discriminant: ReferenceIdentifier { - name: 'foo' - loc: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 26 - index: 26 - line: 1 - } - start: Object { - column: 23 - index: 23 - line: 1 - } - } - } - } - } + INVALID { + description: 'switch with no default clause' + node: SwitchStatement { + cases: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 30 + index: 30 + line: 1 + } + start: Object { + column: 15 + index: 15 + line: 1 + } + } + discriminant: ReferenceIdentifier { + name: 'foo' + loc: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 26 + index: 26 + line: 1 + } + start: Object { + column: 23 + index: 23 + line: 1 + } + } + } + } + } ] ``` @@ -690,93 +690,93 @@ Array [ ```javascript Array [ - INVALID { - description: 'switch with no default clause' - node: SwitchStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 44 - index: 44 - line: 1 - } - start: Object { - column: 15 - index: 15 - line: 1 - } - } - discriminant: ReferenceIdentifier { - name: 'foo' - loc: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 26 - index: 26 - line: 1 - } - start: Object { - column: 23 - index: 23 - line: 1 - } - } - } - cases: Array [ - SwitchCase { - loc: Object { - filename: 'unknown' - end: Object { - column: 43 - index: 43 - line: 1 - } - start: Object { - column: 39 - index: 39 - line: 1 - } - } - test: StringLiteral { - value: 'bar' - loc: Object { - filename: 'unknown' - end: Object { - column: 39 - index: 39 - line: 1 - } - start: Object { - column: 34 - index: 34 - line: 1 - } - } - } - consequent: Array [ - BlockStatement { - body: Array [] - directives: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 43 - index: 43 - line: 1 - } - start: Object { - column: 41 - index: 41 - line: 1 - } - } - } - ] - } - ] - } - } + INVALID { + description: 'switch with no default clause' + node: SwitchStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 44 + index: 44 + line: 1 + } + start: Object { + column: 15 + index: 15 + line: 1 + } + } + discriminant: ReferenceIdentifier { + name: 'foo' + loc: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 26 + index: 26 + line: 1 + } + start: Object { + column: 23 + index: 23 + line: 1 + } + } + } + cases: Array [ + SwitchCase { + loc: Object { + filename: 'unknown' + end: Object { + column: 43 + index: 43 + line: 1 + } + start: Object { + column: 39 + index: 39 + line: 1 + } + } + test: StringLiteral { + value: 'bar' + loc: Object { + filename: 'unknown' + end: Object { + column: 39 + index: 39 + line: 1 + } + start: Object { + column: 34 + index: 34 + line: 1 + } + } + } + consequent: Array [ + BlockStatement { + body: Array [] + directives: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 43 + index: 43 + line: 1 + } + start: Object { + column: 41 + index: 41 + line: 1 + } + } + } + ] + } + ] + } + } ] ``` @@ -784,26 +784,26 @@ Array [ ```javascript Array [ - INVALID { - description: 'empty block' - node: BlockStatement { - body: Array [] - directives: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 40 - index: 40 - line: 1 - } - start: Object { - column: 38 - index: 38 - line: 1 - } - } - } - } + INVALID { + description: 'empty block' + node: BlockStatement { + body: Array [] + directives: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 40 + index: 40 + line: 1 + } + start: Object { + column: 38 + index: 38 + line: 1 + } + } + } + } ] ``` @@ -811,122 +811,122 @@ Array [ ```javascript Array [ - COMPLETION { - node: ReturnStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 63 - index: 63 - line: 1 - } - start: Object { - column: 50 - index: 50 - line: 1 - } - } - argument: BooleanLiteral { - value: false - loc: Object { - filename: 'unknown' - end: Object { - column: 62 - index: 62 - line: 1 - } - start: Object { - column: 57 - index: 57 - line: 1 - } - } - } - } - } - INVALID { - description: 'empty alternate' - node: IfStatement { - alternate: undefined - loc: Object { - filename: 'unknown' - end: Object { - column: 64 - index: 64 - line: 1 - } - start: Object { - column: 39 - index: 39 - line: 1 - } - } - test: BooleanLiteral { - value: true - loc: Object { - filename: 'unknown' - end: Object { - column: 47 - index: 47 - line: 1 - } - start: Object { - column: 43 - index: 43 - line: 1 - } - } - } - consequent: BlockStatement { - directives: Array [] - loc: Object { - filename: 'unknown' - end: Object { - column: 64 - index: 64 - line: 1 - } - start: Object { - column: 49 - index: 49 - line: 1 - } - } - body: Array [ - ReturnStatement { - loc: Object { - filename: 'unknown' - end: Object { - column: 63 - index: 63 - line: 1 - } - start: Object { - column: 50 - index: 50 - line: 1 - } - } - argument: BooleanLiteral { - value: false - loc: Object { - filename: 'unknown' - end: Object { - column: 62 - index: 62 - line: 1 - } - start: Object { - column: 57 - index: 57 - line: 1 - } - } - } - } - ] - } - } - } + COMPLETION { + node: ReturnStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 63 + index: 63 + line: 1 + } + start: Object { + column: 50 + index: 50 + line: 1 + } + } + argument: BooleanLiteral { + value: false + loc: Object { + filename: 'unknown' + end: Object { + column: 62 + index: 62 + line: 1 + } + start: Object { + column: 57 + index: 57 + line: 1 + } + } + } + } + } + INVALID { + description: 'empty alternate' + node: IfStatement { + alternate: undefined + loc: Object { + filename: 'unknown' + end: Object { + column: 64 + index: 64 + line: 1 + } + start: Object { + column: 39 + index: 39 + line: 1 + } + } + test: BooleanLiteral { + value: true + loc: Object { + filename: 'unknown' + end: Object { + column: 47 + index: 47 + line: 1 + } + start: Object { + column: 43 + index: 43 + line: 1 + } + } + } + consequent: BlockStatement { + directives: Array [] + loc: Object { + filename: 'unknown' + end: Object { + column: 64 + index: 64 + line: 1 + } + start: Object { + column: 49 + index: 49 + line: 1 + } + } + body: Array [ + ReturnStatement { + loc: Object { + filename: 'unknown' + end: Object { + column: 63 + index: 63 + line: 1 + } + start: Object { + column: 50 + index: 50 + line: 1 + } + } + argument: BooleanLiteral { + value: false + loc: Object { + filename: 'unknown' + end: Object { + column: 62 + index: 62 + line: 1 + } + start: Object { + column: 57 + index: 57 + line: 1 + } + } + } + } + ] + } + } + } ] ``` diff --git a/packages/@romejs/js-ast-utils/getCompletionRecords.test.ts b/packages/@romejs/js-ast-utils/getCompletionRecords.test.ts index b0aa2683978..751b56f6235 100644 --- a/packages/@romejs/js-ast-utils/getCompletionRecords.test.ts +++ b/packages/@romejs/js-ast-utils/getCompletionRecords.test.ts @@ -11,44 +11,44 @@ import {parseJS} from '@romejs/js-parser'; import {functionDeclaration} from '@romejs/js-ast'; function helper(input: string) { - return getCompletionRecords( - functionDeclaration.assert( - parseJS({ - path: 'unknown', - input: `function foo(){${input}}`, - }).body[0], - ).body, - ); + return getCompletionRecords( + functionDeclaration.assert( + parseJS({ + path: 'unknown', + input: `function foo(){${input}}`, + }).body[0], + ).body, + ); } test( - 'invalid', - async (t) => { - t.snapshot(helper(`{}`)); - t.snapshot(helper(`'foobar';`)); - t.snapshot(helper(`if (bar) {'foobar';}`)); - t.snapshot(helper(`if (bar) {'foobar';} else {}`)); - t.snapshot(helper(`switch (foo) {}`)); - t.snapshot(helper(`switch (foo) {case 'bar': {}}`)); - t.snapshot(helper(`switch (foo) {default: {}}`)); - }, + 'invalid', + async (t) => { + t.snapshot(helper(`{}`)); + t.snapshot(helper(`'foobar';`)); + t.snapshot(helper(`if (bar) {'foobar';}`)); + t.snapshot(helper(`if (bar) {'foobar';} else {}`)); + t.snapshot(helper(`switch (foo) {}`)); + t.snapshot(helper(`switch (foo) {case 'bar': {}}`)); + t.snapshot(helper(`switch (foo) {default: {}}`)); + }, ); test( - 'completions', - async (t) => { - t.snapshot(helper(`return false;`)); - t.snapshot(helper(`return; invalid;`)); - t.snapshot(helper(`if (bar) {return false;}`)); - t.snapshot(helper(`if (bar) {return false;} else {return true;}`)); - t.snapshot(helper(`switch (foo) {default: {return false;}}`)); - t.snapshot(helper(`switch (foo) {default: {return false;}}`)); - }, + 'completions', + async (t) => { + t.snapshot(helper(`return false;`)); + t.snapshot(helper(`return; invalid;`)); + t.snapshot(helper(`if (bar) {return false;}`)); + t.snapshot(helper(`if (bar) {return false;} else {return true;}`)); + t.snapshot(helper(`switch (foo) {default: {return false;}}`)); + t.snapshot(helper(`switch (foo) {default: {return false;}}`)); + }, ); test( - 'mix', - async (t) => { - t.snapshot(helper(`switch (foo) {default: {if (true) {return false;}}}`)); - }, + 'mix', + async (t) => { + t.snapshot(helper(`switch (foo) {default: {if (true) {return false;}}}`)); + }, ); diff --git a/packages/@romejs/js-ast-utils/getCompletionRecords.ts b/packages/@romejs/js-ast-utils/getCompletionRecords.ts index bd857857152..1a80ba68bfa 100644 --- a/packages/@romejs/js-ast-utils/getCompletionRecords.ts +++ b/packages/@romejs/js-ast-utils/getCompletionRecords.ts @@ -6,137 +6,137 @@ */ import { - AnyNode, - AnyStatement, - BreakStatement, - ContinueStatement, - ReturnStatement, - ThrowStatement, + AnyNode, + AnyStatement, + BreakStatement, + ContinueStatement, + ReturnStatement, + ThrowStatement, } from '@romejs/js-ast'; type CompletionRecord = { - type: 'COMPLETION'; - node: ReturnStatement | ContinueStatement | BreakStatement | ThrowStatement; + type: 'COMPLETION'; + node: ReturnStatement | ContinueStatement | BreakStatement | ThrowStatement; }; type InvalidRecord = { - type: 'INVALID'; - description: string; - node: AnyNode; + type: 'INVALID'; + description: string; + node: AnyNode; }; type Records = Array; function getIfCompletionRecords( - node: undefined | AnyNode, - parent: AnyNode, - key: string, + node: undefined | AnyNode, + parent: AnyNode, + key: string, ): Records { - if (node === undefined) { - return [ - { - type: 'INVALID', - description: `empty ${key}`, - node: parent, - }, - ]; - } else { - return getCompletionRecords(node); - } + if (node === undefined) { + return [ + { + type: 'INVALID', + description: `empty ${key}`, + node: parent, + }, + ]; + } else { + return getCompletionRecords(node); + } } function getLastCompletionRecordFromNodes( - nodes: Array, + nodes: Array, ): undefined | Records { - // Get the last node to produce records - for (let i = nodes.length - 1; i >= 0; i--) { - const node = nodes[i]; - const records = _getCompletionRecords(node); - if (records !== undefined) { - return records; - } - } - return undefined; + // Get the last node to produce records + for (let i = nodes.length - 1; i >= 0; i--) { + const node = nodes[i]; + const records = _getCompletionRecords(node); + if (records !== undefined) { + return records; + } + } + return undefined; } function _getCompletionRecords(node: AnyNode): undefined | Records { - if (node.type === 'BlockStatement') { - const records = getLastCompletionRecordFromNodes(node.body); - if (records !== undefined) { - return records; - } + if (node.type === 'BlockStatement') { + const records = getLastCompletionRecordFromNodes(node.body); + if (records !== undefined) { + return records; + } - return [ - { - type: 'INVALID', - description: 'empty block', - node, - }, - ]; - } + return [ + { + type: 'INVALID', + description: 'empty block', + node, + }, + ]; + } - if (node.type === 'SwitchStatement') { - for (const caseNode of node.cases) { - if (caseNode.test === undefined) { - const records = getLastCompletionRecordFromNodes(caseNode.consequent); - if (records === undefined) { - return [ - { - type: 'INVALID', - description: 'default switch clause with no completions', - node: caseNode, - }, - ]; - } else { - return records; - } - } - } + if (node.type === 'SwitchStatement') { + for (const caseNode of node.cases) { + if (caseNode.test === undefined) { + const records = getLastCompletionRecordFromNodes(caseNode.consequent); + if (records === undefined) { + return [ + { + type: 'INVALID', + description: 'default switch clause with no completions', + node: caseNode, + }, + ]; + } else { + return records; + } + } + } - return [ - { - type: 'INVALID', - description: 'switch with no default clause', - node, - }, - ]; - } + return [ + { + type: 'INVALID', + description: 'switch with no default clause', + node, + }, + ]; + } - if (node.type === 'IfStatement') { - return [ - ...getIfCompletionRecords(node.consequent, node, 'consequent'), - ...getIfCompletionRecords(node.alternate, node, 'alternate'), - ]; - } + if (node.type === 'IfStatement') { + return [ + ...getIfCompletionRecords(node.consequent, node, 'consequent'), + ...getIfCompletionRecords(node.alternate, node, 'alternate'), + ]; + } - if ( - node.type === 'ReturnStatement' || - node.type === 'ContinueStatement' || - node.type === 'BreakStatement' || - node.type === 'ThrowStatement' - ) { - return [ - { - type: 'COMPLETION', - node, - }, - ]; - } + if ( + node.type === 'ReturnStatement' || + node.type === 'ContinueStatement' || + node.type === 'BreakStatement' || + node.type === 'ThrowStatement' + ) { + return [ + { + type: 'COMPLETION', + node, + }, + ]; + } - return undefined; + return undefined; } export default function getCompletionRecords(node: AnyNode): Records { - const records = _getCompletionRecords(node); - if (records === undefined) { - return [ - { - type: 'INVALID', - description: 'invalid node', - node, - }, - ]; - } else { - return records; - } + const records = _getCompletionRecords(node); + if (records === undefined) { + return [ + { + type: 'INVALID', + description: 'invalid node', + node, + }, + ]; + } else { + return records; + } } diff --git a/packages/@romejs/js-ast-utils/getImportSpecifiers.ts b/packages/@romejs/js-ast-utils/getImportSpecifiers.ts index bddbdc849be..59835b72f0c 100644 --- a/packages/@romejs/js-ast-utils/getImportSpecifiers.ts +++ b/packages/@romejs/js-ast-utils/getImportSpecifiers.ts @@ -8,19 +8,19 @@ import {AnyImportSpecifier, ImportDeclaration} from '@romejs/js-ast'; export default function getImportSpecifiers( - node: ImportDeclaration, + node: ImportDeclaration, ): Array { - let specifiers: Array = []; + let specifiers: Array = []; - if (node.defaultSpecifier !== undefined) { - specifiers.push(node.defaultSpecifier); - } + if (node.defaultSpecifier !== undefined) { + specifiers.push(node.defaultSpecifier); + } - if (node.namespaceSpecifier !== undefined) { - specifiers.push(node.namespaceSpecifier); - } + if (node.namespaceSpecifier !== undefined) { + specifiers.push(node.namespaceSpecifier); + } - specifiers = specifiers.concat(node.namedSpecifiers); + specifiers = specifiers.concat(node.namedSpecifiers); - return specifiers; + return specifiers; } diff --git a/packages/@romejs/js-ast-utils/getNodeReferenceParts.test.ts b/packages/@romejs/js-ast-utils/getNodeReferenceParts.test.ts index 7963e0c5d0a..7bc9fb2dfaa 100644 --- a/packages/@romejs/js-ast-utils/getNodeReferenceParts.test.ts +++ b/packages/@romejs/js-ast-utils/getNodeReferenceParts.test.ts @@ -10,35 +10,35 @@ import getNodeReferenceParts from './getNodeReferenceParts'; import template from './template'; test( - 'getNodeReferenceParts', - (t) => { - t.inlineSnapshot( - getNodeReferenceParts(template.expression`foo`), - "Object {\n bailed: false\n parts: Array [\n Object {\n value: 'foo'\n node: ReferenceIdentifier {name: 'foo'}\n }\n ]\n}", - ); - t.inlineSnapshot( - getNodeReferenceParts(template.expression`foo.bar`), - "Object {\n bailed: false\n parts: Array [\n Object {\n value: 'foo'\n node: ReferenceIdentifier {name: 'foo'}\n }\n Object {\n value: 'bar'\n node: Identifier {name: 'bar'}\n }\n ]\n}", - ); - t.inlineSnapshot( - getNodeReferenceParts(template.expression`this.bar`), - "Object {\n bailed: false\n parts: Array [\n Object {\n value: 'this'\n node: ThisExpression {}\n }\n Object {\n value: 'bar'\n node: Identifier {name: 'bar'}\n }\n ]\n}", - ); - t.inlineSnapshot( - getNodeReferenceParts(template.expression`this.bar[bar]`), - "Object {\n bailed: true\n parts: Array [\n Object {\n value: 'this'\n node: ThisExpression {}\n }\n Object {\n value: 'bar'\n node: Identifier {name: 'bar'}\n }\n ]\n}", - ); - t.inlineSnapshot( - getNodeReferenceParts(template.expression`import.meta`), - "Object {\n bailed: false\n parts: Array [\n Object {\n value: 'import'\n node: MetaProperty {\n meta: Identifier {name: 'import'}\n property: Identifier {name: 'meta'}\n }\n }\n Object {\n value: 'meta'\n node: MetaProperty {\n meta: Identifier {name: 'import'}\n property: Identifier {name: 'meta'}\n }\n }\n ]\n}", - ); - t.inlineSnapshot( - getNodeReferenceParts(template.expression`foo['bar']`), - "Object {\n bailed: false\n parts: Array [\n Object {\n value: 'foo'\n node: ReferenceIdentifier {name: 'foo'}\n }\n Object {\n value: 'bar'\n node: StringLiteral {value: 'bar'}\n }\n ]\n}", - ); - t.inlineSnapshot( - getNodeReferenceParts(template.expression`foo[bar]`), - "Object {\n bailed: true\n parts: Array [\n Object {\n value: 'foo'\n node: ReferenceIdentifier {name: 'foo'}\n }\n ]\n}", - ); - }, + 'getNodeReferenceParts', + (t) => { + t.inlineSnapshot( + getNodeReferenceParts(template.expression`foo`), + "Object {\n\tbailed: false\n\tparts: Array [\n\t\tObject {\n\t\t\tvalue: 'foo'\n\t\t\tnode: ReferenceIdentifier {name: 'foo'}\n\t\t}\n\t]\n}", + ); + t.inlineSnapshot( + getNodeReferenceParts(template.expression`foo.bar`), + "Object {\n\tbailed: false\n\tparts: Array [\n\t\tObject {\n\t\t\tvalue: 'foo'\n\t\t\tnode: ReferenceIdentifier {name: 'foo'}\n\t\t}\n\t\tObject {\n\t\t\tvalue: 'bar'\n\t\t\tnode: Identifier {name: 'bar'}\n\t\t}\n\t]\n}", + ); + t.inlineSnapshot( + getNodeReferenceParts(template.expression`this.bar`), + "Object {\n\tbailed: false\n\tparts: Array [\n\t\tObject {\n\t\t\tvalue: 'this'\n\t\t\tnode: ThisExpression {}\n\t\t}\n\t\tObject {\n\t\t\tvalue: 'bar'\n\t\t\tnode: Identifier {name: 'bar'}\n\t\t}\n\t]\n}", + ); + t.inlineSnapshot( + getNodeReferenceParts(template.expression`this.bar[bar]`), + "Object {\n\tbailed: true\n\tparts: Array [\n\t\tObject {\n\t\t\tvalue: 'this'\n\t\t\tnode: ThisExpression {}\n\t\t}\n\t\tObject {\n\t\t\tvalue: 'bar'\n\t\t\tnode: Identifier {name: 'bar'}\n\t\t}\n\t]\n}", + ); + t.inlineSnapshot( + getNodeReferenceParts(template.expression`import.meta`), + "Object {\n\tbailed: false\n\tparts: Array [\n\t\tObject {\n\t\t\tvalue: 'import'\n\t\t\tnode: MetaProperty {\n\t\t\t\tmeta: Identifier {name: 'import'}\n\t\t\t\tproperty: Identifier {name: 'meta'}\n\t\t\t}\n\t\t}\n\t\tObject {\n\t\t\tvalue: 'meta'\n\t\t\tnode: MetaProperty {\n\t\t\t\tmeta: Identifier {name: 'import'}\n\t\t\t\tproperty: Identifier {name: 'meta'}\n\t\t\t}\n\t\t}\n\t]\n}", + ); + t.inlineSnapshot( + getNodeReferenceParts(template.expression`foo['bar']`), + "Object {\n\tbailed: false\n\tparts: Array [\n\t\tObject {\n\t\t\tvalue: 'foo'\n\t\t\tnode: ReferenceIdentifier {name: 'foo'}\n\t\t}\n\t\tObject {\n\t\t\tvalue: 'bar'\n\t\t\tnode: StringLiteral {value: 'bar'}\n\t\t}\n\t]\n}", + ); + t.inlineSnapshot( + getNodeReferenceParts(template.expression`foo[bar]`), + "Object {\n\tbailed: true\n\tparts: Array [\n\t\tObject {\n\t\t\tvalue: 'foo'\n\t\t\tnode: ReferenceIdentifier {name: 'foo'}\n\t\t}\n\t]\n}", + ); + }, ); diff --git a/packages/@romejs/js-ast-utils/getNodeReferenceParts.ts b/packages/@romejs/js-ast-utils/getNodeReferenceParts.ts index 904f3a04960..31bbcbad703 100644 --- a/packages/@romejs/js-ast-utils/getNodeReferenceParts.ts +++ b/packages/@romejs/js-ast-utils/getNodeReferenceParts.ts @@ -9,69 +9,69 @@ import {AnyNode} from '@romejs/js-ast'; import isIdentifierish from './isIdentifierish'; type Parts = Array<{ - value: string; - node: AnyNode; + value: string; + node: AnyNode; }>; type Result = { - bailed: boolean; - parts: Parts; + bailed: boolean; + parts: Parts; }; const cache: WeakMap = new WeakMap(); const EMPTY: Result = { - bailed: true, - parts: [], + bailed: true, + parts: [], }; export default function getNodeReferenceParts(node: undefined | AnyNode): Result { - if (node === undefined) { - return EMPTY; - } + if (node === undefined) { + return EMPTY; + } - const cached = cache.get(node); - if (cached !== undefined) { - return cached; - } + const cached = cache.get(node); + if (cached !== undefined) { + return cached; + } - const parts: Parts = []; + const parts: Parts = []; - function add(node: AnyNode): boolean { - if (isIdentifierish(node)) { - parts.push({node, value: node.name}); - return false; - } else if (node.type === 'ThisExpression') { - parts.push({node, value: 'this'}); - return false; - } else if (node.type === 'StringLiteral') { - parts.push({node, value: node.value}); - return false; - } else if (node.type === 'MetaProperty') { - parts.push({node, value: node.meta.name}); - parts.push({node, value: node.property.name}); - return false; - } else if (node.type === 'MemberExpression') { - const stop = add(node.object); - if (stop) { - return true; - } else { - return add(node.property); - } - } else if ( - node.type === 'ComputedMemberProperty' && - node.value.type === 'StringLiteral' - ) { - return add(node.value); - } else if (node.type === 'StaticMemberProperty') { - return add(node.value); - } else { - return true; - } - } + function add(node: AnyNode): boolean { + if (isIdentifierish(node)) { + parts.push({node, value: node.name}); + return false; + } else if (node.type === 'ThisExpression') { + parts.push({node, value: 'this'}); + return false; + } else if (node.type === 'StringLiteral') { + parts.push({node, value: node.value}); + return false; + } else if (node.type === 'MetaProperty') { + parts.push({node, value: node.meta.name}); + parts.push({node, value: node.property.name}); + return false; + } else if (node.type === 'MemberExpression') { + const stop = add(node.object); + if (stop) { + return true; + } else { + return add(node.property); + } + } else if ( + node.type === 'ComputedMemberProperty' && + node.value.type === 'StringLiteral' + ) { + return add(node.value); + } else if (node.type === 'StaticMemberProperty') { + return add(node.value); + } else { + return true; + } + } - const bailed = add(node); - const result: Result = {bailed, parts}; - cache.set(node, result); - return result; + const bailed = add(node); + const result: Result = {bailed, parts}; + cache.set(node, result); + return result; } diff --git a/packages/@romejs/js-ast-utils/getPrecedence.ts b/packages/@romejs/js-ast-utils/getPrecedence.ts index ba44fb23713..ef4b7fe5261 100644 --- a/packages/@romejs/js-ast-utils/getPrecedence.ts +++ b/packages/@romejs/js-ast-utils/getPrecedence.ts @@ -1,35 +1,35 @@ import {BinaryOperator, LogicalOperator} from '@romejs/js-ast'; const PRECEDENCE = { - '||': 0, - '&&': 1, - '??': 1, - '|': 2, - '^': 3, - '&': 4, - '==': 5, - '===': 5, - '!=': 5, - '!==': 5, - '<': 6, - '>': 6, - '<=': 6, - '>=': 6, - in: 6, - instanceof: 6, - '>>': 7, - '<<': 7, - '>>>': 7, - '+': 8, - '-': 8, - '*': 9, - '/': 9, - '%': 9, - '**': 10, + '||': 0, + '&&': 1, + '??': 1, + '|': 2, + '^': 3, + '&': 4, + '==': 5, + '===': 5, + '!=': 5, + '!==': 5, + '<': 6, + '>': 6, + '<=': 6, + '>=': 6, + in: 6, + instanceof: 6, + '>>': 7, + '<<': 7, + '>>>': 7, + '+': 8, + '-': 8, + '*': 9, + '/': 9, + '%': 9, + '**': 10, }; export default function getPrecedence( - operator: BinaryOperator | LogicalOperator, + operator: BinaryOperator | LogicalOperator, ): number { - return PRECEDENCE[operator]; + return PRECEDENCE[operator]; } diff --git a/packages/@romejs/js-ast-utils/getRequireSource.ts b/packages/@romejs/js-ast-utils/getRequireSource.ts index 5d8ca4b0fab..403b57f08cb 100644 --- a/packages/@romejs/js-ast-utils/getRequireSource.ts +++ b/packages/@romejs/js-ast-utils/getRequireSource.ts @@ -10,46 +10,46 @@ import {AnyNode} from '@romejs/js-ast'; import doesNodeMatchPattern from './doesNodeMatchPattern'; export default function getRequireSource( - node: undefined | AnyNode, - scope: Scope, - allowStaticMember: boolean = false, + node: undefined | AnyNode, + scope: Scope, + allowStaticMember: boolean = false, ): undefined | string { - if (node === undefined) { - return undefined; - } - - if ( - allowStaticMember && - node.type === 'MemberExpression' && - node.property.type === 'StaticMemberProperty' - ) { - node = node.object; - } - - if (node.type !== 'CallExpression') { - return undefined; - } - - const {arguments: args, callee} = node; - - const [firstArg] = args; - if (args.length !== 1 || firstArg.type !== 'StringLiteral') { - return undefined; - } - - const validRequireCallee = - callee.type === 'ReferenceIdentifier' && - callee.name === 'require' && - scope.getBinding('require') === undefined; - - const validRomeRequreCallee = - (doesNodeMatchPattern(callee, 'Rome.requireDefault') || - doesNodeMatchPattern(callee, 'Rome.requireNamespace')) && - scope.getBinding('Rome') === undefined; - - if (validRequireCallee || validRomeRequreCallee) { - return firstArg.value; - } - - return undefined; + if (node === undefined) { + return undefined; + } + + if ( + allowStaticMember && + node.type === 'MemberExpression' && + node.property.type === 'StaticMemberProperty' + ) { + node = node.object; + } + + if (node.type !== 'CallExpression') { + return undefined; + } + + const {arguments: args, callee} = node; + + const [firstArg] = args; + if (args.length !== 1 || firstArg.type !== 'StringLiteral') { + return undefined; + } + + const validRequireCallee = + callee.type === 'ReferenceIdentifier' && + callee.name === 'require' && + scope.getBinding('require') === undefined; + + const validRomeRequreCallee = + (doesNodeMatchPattern(callee, 'Rome.requireDefault') || + doesNodeMatchPattern(callee, 'Rome.requireNamespace')) && + scope.getBinding('Rome') === undefined; + + if (validRequireCallee || validRomeRequreCallee) { + return firstArg.value; + } + + return undefined; } diff --git a/packages/@romejs/js-ast-utils/hasPotentialSideEffects.ts b/packages/@romejs/js-ast-utils/hasPotentialSideEffects.ts index e6d4aa913f9..2be3e9451f0 100644 --- a/packages/@romejs/js-ast-utils/hasPotentialSideEffects.ts +++ b/packages/@romejs/js-ast-utils/hasPotentialSideEffects.ts @@ -9,102 +9,102 @@ import {AnyNode} from '@romejs/js-ast'; import {Scope} from '@romejs/js-compiler'; export default function hasPotentialSideEffects( - node: undefined | AnyNode, - scope: Scope, + node: undefined | AnyNode, + scope: Scope, ): boolean { - if (node === undefined) { - return false; - } - - switch (node.type) { - case 'ExportLocalDeclaration': - if (node.declaration === undefined) { - return false; - } else { - return hasPotentialSideEffects(node.declaration, scope); - } - - case 'ExportExternalDeclaration': - return true; - - case 'FunctionExpression': - case 'FunctionDeclaration': - return false; - - case 'ClassDeclaration': - return ( - node.meta.superClass !== undefined || - !hasPotentialSideEffects(node.meta.superClass, scope) - ); - - case 'ReferenceIdentifier': - // Variables that aren't in scope and aren't registered globals could trigger a getter - // Unlikely but let's aim for 100% correctness - return ( - scope.getRootScope().isGlobal(node.name) || scope.hasBinding(node.name) - ); - - case 'VariableDeclaration': { - for (const declarator of node.declarations) { - if (hasPotentialSideEffects(declarator, scope)) { - return true; - } - } - return false; - } - - case 'VariableDeclarator': - return ( - hasPotentialSideEffects(node.id, scope) || - hasPotentialSideEffects(node.init, scope) - ); - - case 'SpreadProperty': - case 'SpreadElement': - return hasPotentialSideEffects(node.argument, scope); - - case 'BindingAssignmentPattern': - return hasPotentialSideEffects(node.right, scope); - - case 'ObjectExpression': - case 'BindingObjectPattern': { - for (const prop of node.properties) { - if (hasPotentialSideEffects(prop, scope)) { - return true; - } - } - return false; - } - - case 'StaticPropertyKey': - return false; - - case 'ComputedPropertyKey': - return hasPotentialSideEffects(node.value, scope); - - case 'BindingObjectPatternProperty': - case 'ObjectProperty': - return ( - hasPotentialSideEffects(node.key, scope) || - hasPotentialSideEffects(node.value, scope) - ); - - case 'BindingArrayPattern': - case 'ArrayExpression': { - for (const elem of node.elements) { - if (hasPotentialSideEffects(elem, scope)) { - return true; - } - } - return false; - } - - case 'StringLiteral': - case 'NumericLiteral': - case 'BooleanLiteral': - case 'NullLiteral': - return false; - } - - return true; + if (node === undefined) { + return false; + } + + switch (node.type) { + case 'ExportLocalDeclaration': + if (node.declaration === undefined) { + return false; + } else { + return hasPotentialSideEffects(node.declaration, scope); + } + + case 'ExportExternalDeclaration': + return true; + + case 'FunctionExpression': + case 'FunctionDeclaration': + return false; + + case 'ClassDeclaration': + return ( + node.meta.superClass !== undefined || + !hasPotentialSideEffects(node.meta.superClass, scope) + ); + + case 'ReferenceIdentifier': + // Variables that aren't in scope and aren't registered globals could trigger a getter + // Unlikely but let's aim for 100% correctness + return ( + scope.getRootScope().isGlobal(node.name) || scope.hasBinding(node.name) + ); + + case 'VariableDeclaration': { + for (const declarator of node.declarations) { + if (hasPotentialSideEffects(declarator, scope)) { + return true; + } + } + return false; + } + + case 'VariableDeclarator': + return ( + hasPotentialSideEffects(node.id, scope) || + hasPotentialSideEffects(node.init, scope) + ); + + case 'SpreadProperty': + case 'SpreadElement': + return hasPotentialSideEffects(node.argument, scope); + + case 'BindingAssignmentPattern': + return hasPotentialSideEffects(node.right, scope); + + case 'ObjectExpression': + case 'BindingObjectPattern': { + for (const prop of node.properties) { + if (hasPotentialSideEffects(prop, scope)) { + return true; + } + } + return false; + } + + case 'StaticPropertyKey': + return false; + + case 'ComputedPropertyKey': + return hasPotentialSideEffects(node.value, scope); + + case 'BindingObjectPatternProperty': + case 'ObjectProperty': + return ( + hasPotentialSideEffects(node.key, scope) || + hasPotentialSideEffects(node.value, scope) + ); + + case 'BindingArrayPattern': + case 'ArrayExpression': { + for (const elem of node.elements) { + if (hasPotentialSideEffects(elem, scope)) { + return true; + } + } + return false; + } + + case 'StringLiteral': + case 'NumericLiteral': + case 'BooleanLiteral': + case 'NullLiteral': + return false; + } + + return true; } diff --git a/packages/@romejs/js-ast-utils/inheritLoc.ts b/packages/@romejs/js-ast-utils/inheritLoc.ts index e47fbed6cd0..eeff11dbbb8 100644 --- a/packages/@romejs/js-ast-utils/inheritLoc.ts +++ b/packages/@romejs/js-ast-utils/inheritLoc.ts @@ -9,36 +9,36 @@ import {AnyNode} from '@romejs/js-ast'; import {SourceLocation} from '@romejs/parser-core'; export default function inheritLoc( - node: AnyNode, - name?: string, + node: AnyNode, + name?: string, ): undefined | SourceLocation { - const {loc} = node; - if (loc === undefined) { - return undefined; - } + const {loc} = node; + if (loc === undefined) { + return undefined; + } - // Inherit new name if specified - if (name !== undefined) { - return { - ...loc, - identifierName: name, - }; - } + // Inherit new name if specified + if (name !== undefined) { + return { + ...loc, + identifierName: name, + }; + } - // Don't infer a name if it already has one - if (loc.identifierName !== undefined) { - return loc; - } + // Don't infer a name if it already has one + if (loc.identifierName !== undefined) { + return loc; + } - // If this location has no identifierName and we're an Identifier then inherit it + // If this location has no identifierName and we're an Identifier then inherit it - // TODO maybe handle other identifier types? JSXIdentifier etc? - if (node.type === 'Identifier') { - return { - ...loc, - identifierName: node.name, - }; - } + // TODO maybe handle other identifier types? JSXIdentifier etc? + if (node.type === 'Identifier') { + return { + ...loc, + identifierName: node.name, + }; + } - return loc; + return loc; } diff --git a/packages/@romejs/js-ast-utils/isBinary.ts b/packages/@romejs/js-ast-utils/isBinary.ts index 538e770f2e8..8cd98703966 100644 --- a/packages/@romejs/js-ast-utils/isBinary.ts +++ b/packages/@romejs/js-ast-utils/isBinary.ts @@ -8,18 +8,18 @@ import {AnyNode, BinaryExpression, LogicalExpression} from '@romejs/js-ast'; export default function isBinary( - node: undefined | AnyNode, + node: undefined | AnyNode, ): node is BinaryExpression | LogicalExpression { - if (node === undefined) { - return false; - } + if (node === undefined) { + return false; + } - switch (node.type) { - case 'BinaryExpression': - case 'LogicalExpression': - return true; + switch (node.type) { + case 'BinaryExpression': + case 'LogicalExpression': + return true; - default: - return false; - } + default: + return false; + } } diff --git a/packages/@romejs/js-ast-utils/isConditional.ts b/packages/@romejs/js-ast-utils/isConditional.ts index f363c67c4d4..09c818fba93 100644 --- a/packages/@romejs/js-ast-utils/isConditional.ts +++ b/packages/@romejs/js-ast-utils/isConditional.ts @@ -8,18 +8,18 @@ import {AnyNode, ConditionalExpression, IfStatement} from '@romejs/js-ast'; export default function isConditional( - node: undefined | AnyNode, + node: undefined | AnyNode, ): node is ConditionalExpression | IfStatement { - if (node === undefined) { - return false; - } + if (node === undefined) { + return false; + } - switch (node.type) { - case 'ConditionalExpression': - case 'IfStatement': - return true; + switch (node.type) { + case 'ConditionalExpression': + case 'IfStatement': + return true; - default: - return false; - } + default: + return false; + } } diff --git a/packages/@romejs/js-ast-utils/isDeclaration.ts b/packages/@romejs/js-ast-utils/isDeclaration.ts index 5b839835704..517c2decf0f 100644 --- a/packages/@romejs/js-ast-utils/isDeclaration.ts +++ b/packages/@romejs/js-ast-utils/isDeclaration.ts @@ -8,38 +8,38 @@ import {AnyDeclaration, AnyNode} from '@romejs/js-ast'; export default function isDeclaration( - node: undefined | AnyNode, + node: undefined | AnyNode, ): node is AnyDeclaration { - if (node === undefined) { - return false; - } + if (node === undefined) { + return false; + } - switch (node.type) { - case 'FunctionDeclaration': - case 'ClassDeclaration': - case 'ExportAllDeclaration': - case 'ExportDefaultDeclaration': - case 'ExportLocalDeclaration': - case 'ImportDeclaration': - case 'TypeAliasTypeAnnotation': - case 'VariableDeclarationStatement': - case 'ExportExternalDeclaration': - case 'TSDeclareFunction': - case 'TSEnumDeclaration': - case 'TSExportAssignment': - case 'TSImportEqualsDeclaration': - case 'TSInterfaceDeclaration': - case 'TSModuleDeclaration': - case 'TSNamespaceExportDeclaration': { - const declaration: AnyDeclaration = node; - declaration; - return true; - } + switch (node.type) { + case 'FunctionDeclaration': + case 'ClassDeclaration': + case 'ExportAllDeclaration': + case 'ExportDefaultDeclaration': + case 'ExportLocalDeclaration': + case 'ImportDeclaration': + case 'TypeAliasTypeAnnotation': + case 'VariableDeclarationStatement': + case 'ExportExternalDeclaration': + case 'TSDeclareFunction': + case 'TSEnumDeclaration': + case 'TSExportAssignment': + case 'TSImportEqualsDeclaration': + case 'TSInterfaceDeclaration': + case 'TSModuleDeclaration': + case 'TSNamespaceExportDeclaration': { + const declaration: AnyDeclaration = node; + declaration; + return true; + } - default: { - const notDeclaration: Exclude = node; - notDeclaration; - return false; - } - } + default: { + const notDeclaration: Exclude = node; + notDeclaration; + return false; + } + } } diff --git a/packages/@romejs/js-ast-utils/isFor.ts b/packages/@romejs/js-ast-utils/isFor.ts index f4e83208d85..0698ea4f4ef 100644 --- a/packages/@romejs/js-ast-utils/isFor.ts +++ b/packages/@romejs/js-ast-utils/isFor.ts @@ -6,26 +6,26 @@ */ import { - AnyNode, - ForInStatement, - ForOfStatement, - ForStatement, + AnyNode, + ForInStatement, + ForOfStatement, + ForStatement, } from '@romejs/js-ast'; export default function isFor( - node: undefined | AnyNode, + node: undefined | AnyNode, ): node is ForStatement | ForInStatement | ForOfStatement { - if (node === undefined) { - return false; - } + if (node === undefined) { + return false; + } - switch (node.type) { - case 'ForStatement': - case 'ForInStatement': - case 'ForOfStatement': - return true; + switch (node.type) { + case 'ForStatement': + case 'ForInStatement': + case 'ForOfStatement': + return true; - default: - return false; - } + default: + return false; + } } diff --git a/packages/@romejs/js-ast-utils/isFunctionNode.ts b/packages/@romejs/js-ast-utils/isFunctionNode.ts index 9106d4d0a80..a8200839b1d 100644 --- a/packages/@romejs/js-ast-utils/isFunctionNode.ts +++ b/packages/@romejs/js-ast-utils/isFunctionNode.ts @@ -8,11 +8,11 @@ import {AnyFunction, AnyNode} from '@romejs/js-ast'; export default function isFunctionNode(node: AnyNode): node is AnyFunction { - return ( - node.type === 'FunctionDeclaration' || - node.type === 'FunctionExpression' || - node.type === 'ObjectMethod' || - node.type === 'ArrowFunctionExpression' || - node.type === 'ClassMethod' - ); + return ( + node.type === 'FunctionDeclaration' || + node.type === 'FunctionExpression' || + node.type === 'ObjectMethod' || + node.type === 'ArrowFunctionExpression' || + node.type === 'ClassMethod' + ); } diff --git a/packages/@romejs/js-ast-utils/isIdentifierish.ts b/packages/@romejs/js-ast-utils/isIdentifierish.ts index e2ed97e1c55..d5511ecb9ac 100644 --- a/packages/@romejs/js-ast-utils/isIdentifierish.ts +++ b/packages/@romejs/js-ast-utils/isIdentifierish.ts @@ -8,12 +8,12 @@ import {AnyIdentifier, AnyNode} from '@romejs/js-ast'; export default function isIdentifierish(node: AnyNode): node is AnyIdentifier { - return ( - node.type === 'Identifier' || - node.type === 'JSXIdentifier' || - node.type === 'JSXReferenceIdentifier' || - node.type === 'BindingIdentifier' || - node.type === 'AssignmentIdentifier' || - node.type === 'ReferenceIdentifier' - ); + return ( + node.type === 'Identifier' || + node.type === 'JSXIdentifier' || + node.type === 'JSXReferenceIdentifier' || + node.type === 'BindingIdentifier' || + node.type === 'AssignmentIdentifier' || + node.type === 'ReferenceIdentifier' + ); } diff --git a/packages/@romejs/js-ast-utils/isInTypeAnnotation.ts b/packages/@romejs/js-ast-utils/isInTypeAnnotation.ts index b81252ae701..a347be4013e 100644 --- a/packages/@romejs/js-ast-utils/isInTypeAnnotation.ts +++ b/packages/@romejs/js-ast-utils/isInTypeAnnotation.ts @@ -12,31 +12,31 @@ import isTypeExpressionWrapperNode from './isTypeExpressionWrapperNode'; // Is this honestly the best heuristics? function getTypeNode(path: Path): undefined | AnyNode { - const {parent, parentPath} = path; - if (parent === undefined || parentPath === undefined) { - return undefined; - } + const {parent, parentPath} = path; + if (parent === undefined || parentPath === undefined) { + return undefined; + } - if (isTypeNode(parent)) { - return parent; - } + if (isTypeNode(parent)) { + return parent; + } - if (isTypeNode(parentPath.parent)) { - return parentPath.parent; - } + if (isTypeNode(parentPath.parent)) { + return parentPath.parent; + } - return undefined; + return undefined; } export default function isInTypeAnnotation(path: Path): boolean { - const match = getTypeNode(path); - if (match === undefined) { - return false; - } + const match = getTypeNode(path); + if (match === undefined) { + return false; + } - if (isTypeExpressionWrapperNode(match)) { - return false; - } else { - return true; - } + if (isTypeExpressionWrapperNode(match)) { + return false; + } else { + return true; + } } diff --git a/packages/@romejs/js-ast-utils/isNodeLike.ts b/packages/@romejs/js-ast-utils/isNodeLike.ts index 87c210b738b..f8ff0baae5a 100644 --- a/packages/@romejs/js-ast-utils/isNodeLike.ts +++ b/packages/@romejs/js-ast-utils/isNodeLike.ts @@ -8,9 +8,9 @@ import {isPlainObject} from '@romejs/typescript-helpers'; export default function isNodeLike(node: unknown): boolean { - if (node == null) { - return false; - } else { - return isPlainObject(node) && typeof node.type === 'string'; - } + if (node == null) { + return false; + } else { + return isPlainObject(node) && typeof node.type === 'string'; + } } diff --git a/packages/@romejs/js-ast-utils/isStatement.ts b/packages/@romejs/js-ast-utils/isStatement.ts index 337e8d8291c..ed9d0dba782 100644 --- a/packages/@romejs/js-ast-utils/isStatement.ts +++ b/packages/@romejs/js-ast-utils/isStatement.ts @@ -9,45 +9,45 @@ import {AnyNode, AnyStatement} from '@romejs/js-ast'; import isDeclaration from './isDeclaration'; export default function isStatement( - node: undefined | AnyNode, + node: undefined | AnyNode, ): node is AnyStatement { - if (node === undefined) { - return false; - } + if (node === undefined) { + return false; + } - if (isDeclaration(node)) { - return true; - } + if (isDeclaration(node)) { + return true; + } - switch (node.type) { - case 'BlockStatement': - case 'BreakStatement': - case 'ContinueStatement': - case 'DebuggerStatement': - case 'DoWhileStatement': - case 'EmptyStatement': - case 'ExpressionStatement': - case 'ForInStatement': - case 'ForStatement': - case 'IfStatement': - case 'LabeledStatement': - case 'ReturnStatement': - case 'SwitchStatement': - case 'ThrowStatement': - case 'TryStatement': - case 'WhileStatement': - case 'WithStatement': - case 'ForOfStatement': { - const statement: AnyStatement = node; - statement; - return true; - } + switch (node.type) { + case 'BlockStatement': + case 'BreakStatement': + case 'ContinueStatement': + case 'DebuggerStatement': + case 'DoWhileStatement': + case 'EmptyStatement': + case 'ExpressionStatement': + case 'ForInStatement': + case 'ForStatement': + case 'IfStatement': + case 'LabeledStatement': + case 'ReturnStatement': + case 'SwitchStatement': + case 'ThrowStatement': + case 'TryStatement': + case 'WhileStatement': + case 'WithStatement': + case 'ForOfStatement': { + const statement: AnyStatement = node; + statement; + return true; + } - default: { - // Assert that all statements were handled - const notStatement: Exclude = node; - notStatement; - return false; - } - } + default: { + // Assert that all statements were handled + const notStatement: Exclude = node; + notStatement; + return false; + } + } } diff --git a/packages/@romejs/js-ast-utils/isTypeExpressionWrapperNode.ts b/packages/@romejs/js-ast-utils/isTypeExpressionWrapperNode.ts index ab594d6ef9b..854f9425878 100644 --- a/packages/@romejs/js-ast-utils/isTypeExpressionWrapperNode.ts +++ b/packages/@romejs/js-ast-utils/isTypeExpressionWrapperNode.ts @@ -6,18 +6,18 @@ */ import { - AnyNode, - TSAsExpression, - TSNonNullExpression, - TSTypeAssertion, + AnyNode, + TSAsExpression, + TSNonNullExpression, + TSTypeAssertion, } from '@romejs/js-ast'; export default function isTypeExpressionWrapperNode( - node: AnyNode, + node: AnyNode, ): node is TSAsExpression | TSTypeAssertion | TSNonNullExpression { - return ( - node.type === 'TSAsExpression' || - node.type === 'TSTypeAssertion' || - node.type === 'TSNonNullExpression' - ); + return ( + node.type === 'TSAsExpression' || + node.type === 'TSTypeAssertion' || + node.type === 'TSNonNullExpression' + ); } diff --git a/packages/@romejs/js-ast-utils/isTypeNode.ts b/packages/@romejs/js-ast-utils/isTypeNode.ts index 52aec8161db..2f16183248a 100644 --- a/packages/@romejs/js-ast-utils/isTypeNode.ts +++ b/packages/@romejs/js-ast-utils/isTypeNode.ts @@ -8,21 +8,21 @@ import {AnyNode} from '@romejs/js-ast'; export default function isTypeNode(node: AnyNode): boolean { - if ( - node.type.startsWith('Flow') || - node.type.startsWith('TS') || - node.type.endsWith('TypeAnnotation') - ) { - return true; - } else if (node.type === 'ImportDeclaration') { - return node.importKind === 'type' || node.importKind === 'typeof'; - } else if ( - node.type === 'ExportDefaultDeclaration' || - node.type === 'ExportLocalDeclaration' || - node.type === 'ExportAllDeclaration' - ) { - return node.exportKind === 'type'; - } else { - return false; - } + if ( + node.type.startsWith('Flow') || + node.type.startsWith('TS') || + node.type.endsWith('TypeAnnotation') + ) { + return true; + } else if (node.type === 'ImportDeclaration') { + return node.importKind === 'type' || node.importKind === 'typeof'; + } else if ( + node.type === 'ExportDefaultDeclaration' || + node.type === 'ExportLocalDeclaration' || + node.type === 'ExportAllDeclaration' + ) { + return node.exportKind === 'type'; + } else { + return false; + } } diff --git a/packages/@romejs/js-ast-utils/isUnaryLike.ts b/packages/@romejs/js-ast-utils/isUnaryLike.ts index 52ea8ecceba..8b40bf12f3c 100644 --- a/packages/@romejs/js-ast-utils/isUnaryLike.ts +++ b/packages/@romejs/js-ast-utils/isUnaryLike.ts @@ -8,17 +8,17 @@ import {AnyNode} from '@romejs/js-ast'; export default function isUnaryLike(node: undefined | AnyNode): boolean { - if (node === undefined) { - return false; - } + if (node === undefined) { + return false; + } - switch (node.type) { - case 'UnaryExpression': - case 'SpreadElement': - case 'SpreadProperty': - return true; + switch (node.type) { + case 'UnaryExpression': + case 'SpreadElement': + case 'SpreadProperty': + return true; - default: - return false; - } + default: + return false; + } } diff --git a/packages/@romejs/js-ast-utils/isValidIdentifierName.ts b/packages/@romejs/js-ast-utils/isValidIdentifierName.ts index f22a627b2b2..9d3791893b2 100644 --- a/packages/@romejs/js-ast-utils/isValidIdentifierName.ts +++ b/packages/@romejs/js-ast-utils/isValidIdentifierName.ts @@ -6,13 +6,13 @@ */ import { - getFullCharCodeAt, - isES2015ReservedWord, - isIdentifierChar, - isIdentifierStart, - isKeyword, - isStrictBindReservedWord, - isStrictReservedWord, + getFullCharCodeAt, + isES2015ReservedWord, + isIdentifierChar, + isIdentifierStart, + isKeyword, + isStrictBindReservedWord, + isStrictReservedWord, } from '@romejs/js-parser-utils'; /** @@ -25,39 +25,39 @@ import { * unintentional semantics. */ export default function isValidIdentifierName(name: string): boolean { - if (name.length === 0) { - return false; - } - - if (isStrictReservedWord(name, true)) { - return false; - } - - if (isStrictBindReservedWord(name, true)) { - return false; - } - - if (isES2015ReservedWord(name)) { - return false; - } - - if (isKeyword(name)) { - return false; - } - - if (isIdentifierStart(getFullCharCodeAt(name, 0)) === false) { - return false; - } - - let i = 1; - while (i < name.length) { - const code = getFullCharCodeAt(name, i); - if (isIdentifierChar(code)) { - i += code <= 65_535 ? 1 : 2; - } else { - return false; - } - } - - return true; + if (name.length === 0) { + return false; + } + + if (isStrictReservedWord(name, true)) { + return false; + } + + if (isStrictBindReservedWord(name, true)) { + return false; + } + + if (isES2015ReservedWord(name)) { + return false; + } + + if (isKeyword(name)) { + return false; + } + + if (isIdentifierStart(getFullCharCodeAt(name, 0)) === false) { + return false; + } + + let i = 1; + while (i < name.length) { + const code = getFullCharCodeAt(name, i); + if (isIdentifierChar(code)) { + i += code <= 65_535 ? 1 : 2; + } else { + return false; + } + } + + return true; } diff --git a/packages/@romejs/js-ast-utils/isVariableIdentifier.ts b/packages/@romejs/js-ast-utils/isVariableIdentifier.ts index 14a32cd7641..56edc1b7834 100644 --- a/packages/@romejs/js-ast-utils/isVariableIdentifier.ts +++ b/packages/@romejs/js-ast-utils/isVariableIdentifier.ts @@ -8,12 +8,12 @@ import {AnyNode, AnyVariableIdentifier} from '@romejs/js-ast'; export default function isVariableIdentifier( - node: AnyNode, + node: AnyNode, ): node is AnyVariableIdentifier { - return ( - node.type === 'BindingIdentifier' || - node.type === 'AssignmentIdentifier' || - node.type === 'ReferenceIdentifier' || - node.type === 'JSXReferenceIdentifier' - ); + return ( + node.type === 'BindingIdentifier' || + node.type === 'AssignmentIdentifier' || + node.type === 'ReferenceIdentifier' || + node.type === 'JSXReferenceIdentifier' + ); } diff --git a/packages/@romejs/js-ast-utils/removeLoc.ts b/packages/@romejs/js-ast-utils/removeLoc.ts index c5c6f284b9f..ef617c462ac 100644 --- a/packages/@romejs/js-ast-utils/removeLoc.ts +++ b/packages/@romejs/js-ast-utils/removeLoc.ts @@ -12,47 +12,47 @@ import {SourceLocation} from '@romejs/parser-core'; import {JSNodeBase} from '@romejs/js-ast/base'; function removeProp(obj: T): Omit { - const {loc, ...locless} = obj; - loc; - return locless; + const {loc, ...locless} = obj; + loc; + return locless; } const removeLocTransform: TransformVisitors = [ - { - name: 'removeLocTransform', - enter(path: Path) { - const {node} = path; - if (node.loc === undefined) { - return node; - } else { - const newNode: JSNodeBase = removeProp(node); + { + name: 'removeLocTransform', + enter(path: Path) { + const {node} = path; + if (node.loc === undefined) { + return node; + } else { + const newNode: JSNodeBase = removeProp(node); - // Also remove any `undefined` properties - // rome-ignore lint/noExplicitAny - const escaped: any = newNode; - for (const key in newNode) { - if (escaped[key] === undefined) { - // rome-ignore lint/noDelete - delete escaped[key]; - } - } + // Also remove any `undefined` properties + // rome-ignore lint/noExplicitAny + const escaped: any = newNode; + for (const key in newNode) { + if (escaped[key] === undefined) { + // rome-ignore lint/noDelete + delete escaped[key]; + } + } - return (newNode as AnyNode); - } - }, - }, + return (newNode as AnyNode); + } + }, + }, ]; export default function removeLoc(ast: AnyNode) { - const context = new CompilerContext({ - sourceText: '', - ast: MOCK_PROGRAM, - project: { - folder: undefined, - config: DEFAULT_PROJECT_CONFIG, - }, - }); - return context.reduce(ast, removeLocTransform); + const context = new CompilerContext({ + sourceText: '', + ast: MOCK_PROGRAM, + project: { + folder: undefined, + config: DEFAULT_PROJECT_CONFIG, + }, + }); + return context.reduce(ast, removeLocTransform); } diff --git a/packages/@romejs/js-ast-utils/removeShallowLoc.ts b/packages/@romejs/js-ast-utils/removeShallowLoc.ts index 2e39c623243..002d53583c9 100644 --- a/packages/@romejs/js-ast-utils/removeShallowLoc.ts +++ b/packages/@romejs/js-ast-utils/removeShallowLoc.ts @@ -8,8 +8,8 @@ import {AnyNode} from '@romejs/js-ast'; export default function removeShallowLoc(node: T): T { - return { - ...node, - loc: undefined, - }; + return { + ...node, + loc: undefined, + }; } diff --git a/packages/@romejs/js-ast-utils/renameBindings.ts b/packages/@romejs/js-ast-utils/renameBindings.ts index ca0be58ce80..3e4a15ecced 100644 --- a/packages/@romejs/js-ast-utils/renameBindings.ts +++ b/packages/@romejs/js-ast-utils/renameBindings.ts @@ -8,11 +8,11 @@ import {Binding, Path} from '@romejs/js-compiler'; import inheritLoc from './inheritLoc'; import { - AnyNode, - exportLocalDeclaration, - exportLocalSpecifier, - identifier, - referenceIdentifier, + AnyNode, + exportLocalDeclaration, + exportLocalSpecifier, + identifier, + referenceIdentifier, } from '@romejs/js-ast'; import getBindingIdentifiers from './getBindingIdentifiers'; import isVariableIdentifier from './isVariableIdentifier'; @@ -22,166 +22,163 @@ import {AnyVariableIdentifier} from '@romejs/js-ast/unions'; // This methods allows either passing in Bindings that could be present within deep scopes, // or local names for the scope in the passed Path export default function renameBindings( - path: Path, - oldToNewMapping: Map, + path: Path, + oldToNewMapping: Map, ): AnyNode | Array { - if (oldToNewMapping.size === 0) { - return path.node; - } - - const oldBindingToNewName: Map = new Map(); - - // get a list of the current bindings for this scope - const oldNameToBinding: Map = new Map(); - for (const [oldName, newName] of oldToNewMapping) { - if (typeof oldName === 'string') { - const binding = path.scope.getBinding(oldName); - oldNameToBinding.set(oldName, binding); - } else { - oldBindingToNewName.set(oldName, newName); - } - } - - // discover nodes to replace first without manipulating the AST as that will change the scope and binding objects - const replaceNodesWithName: Map = new Map(); - path.traverse( - 'renameBindingsCollector', - (path) => { - const {node, scope} = path; - if (!isVariableIdentifier(node)) { - return; - } - - const binding = scope.getBinding(node.name); - - // oldName -> newName - if ( - oldToNewMapping.has(node.name) && - binding === oldNameToBinding.get(node.name) - ) { - const newName = oldToNewMapping.get(node.name); - if (newName === undefined) { - throw new Error('Should exist'); - } - replaceNodesWithName.set(node, newName); - } - - // Binding -> newName - if (binding !== undefined && oldBindingToNewName.has(binding)) { - const newName = oldBindingToNewName.get(binding); - if (newName === undefined) { - throw new Error('Should exist'); - } - replaceNodesWithName.set(node, newName); - } - }, - ); - if (replaceNodesWithName.size === 0) { - return path.node; - } - - // - const replaced: Set = new Set(); - - // replace the nodes - const renamedNode = path.reduce( - { - name: 'renameBindings', - enter(path): AnyNode | Array { - const {node} = path; - - // Retain the correct exported name for `export function` and `export class` - if ( - node.type === 'ExportLocalDeclaration' && - node.declaration !== undefined && - (node.declaration.type === 'FunctionDeclaration' || - node.declaration.type === 'ClassDeclaration') - ) { - const newName = replaceNodesWithName.get(node.declaration.id); - - if (newName !== undefined) { - replaced.add(node.declaration.id); - - const oldName = node.declaration.id.name; - - return ([ - node.declaration, - exportLocalDeclaration.create({ - specifiers: [ - exportLocalSpecifier.create({ - loc: node.declaration.id.loc, - local: referenceIdentifier.quick(newName), - exported: identifier.quick(oldName), - }), - ], - }), - ] as Array); - } - } - - // Retain the correct exported names for `export const` - if ( - node.type === 'ExportLocalDeclaration' && - node.declaration !== undefined - ) { - const bindings = getBindingIdentifiers(node.declaration); - let includesAny = false; - for (const node of bindings) { - if (replaceNodesWithName.has(node)) { - includesAny = true; - break; - } - } - - if (includesAny) { - return ([ - node.declaration, - exportLocalDeclaration.create({ - specifiers: bindings.map((node) => { - let local: string = node.name; - - const newName = replaceNodesWithName.get(node); - if (newName !== undefined) { - local = newName; - replaced.add(node); - } - - return exportLocalSpecifier.create({ - loc: node.loc, - local: referenceIdentifier.quick(local), - exported: identifier.quick(node.name), - }); - }), - }), - ] as Array); - } - } - - if (isVariableIdentifier(node)) { - const newName = replaceNodesWithName.get(node); - if (newName !== undefined) { - replaced.add(node); - return { - ...node, - name: newName, - loc: inheritLoc(node, node.name), - }; - } - } - - return node; - }, - }, - { - noScopeCreation: true, - }, - ); - - // - if (replaced.size !== replaceNodesWithName.size) { - console.log({replaced, replaceNodesWithName}); - throw new Error('Missed some bindings'); - } - - return assertSingleOrMultipleNodes(renamedNode); + if (oldToNewMapping.size === 0) { + return path.node; + } + + const oldBindingToNewName: Map = new Map(); + + // get a list of the current bindings for this scope + const oldNameToBinding: Map = new Map(); + for (const [oldName, newName] of oldToNewMapping) { + if (typeof oldName === 'string') { + const binding = path.scope.getBinding(oldName); + oldNameToBinding.set(oldName, binding); + } else { + oldBindingToNewName.set(oldName, newName); + } + } + + // discover nodes to replace first without manipulating the AST as that will change the scope and binding objects + const replaceNodesWithName: Map = new Map(); + path.traverse( + 'renameBindingsCollector', + (path) => { + const {node, scope} = path; + if (!isVariableIdentifier(node)) { + return; + } + + const binding = scope.getBinding(node.name); + + // oldName -> newName + if ( + oldToNewMapping.has(node.name) && + binding === oldNameToBinding.get(node.name) + ) { + const newName = oldToNewMapping.get(node.name); + if (newName === undefined) { + throw new Error('Should exist'); + } + replaceNodesWithName.set(node, newName); + } + + // Binding -> newName + if (binding !== undefined && oldBindingToNewName.has(binding)) { + const newName = oldBindingToNewName.get(binding); + if (newName === undefined) { + throw new Error('Should exist'); + } + replaceNodesWithName.set(node, newName); + } + }, + ); + if (replaceNodesWithName.size === 0) { + return path.node; + } + + // + const replaced: Set = new Set(); + + // replace the nodes + const renamedNode = path.reduce( + { + name: 'renameBindings', + enter(path): AnyNode | Array { + const {node} = path; + + // Retain the correct exported name for `export function` and `export class` + if ( + node.type === 'ExportLocalDeclaration' && + node.declaration !== undefined && + (node.declaration.type === 'FunctionDeclaration' || + node.declaration.type === 'ClassDeclaration') + ) { + const newName = replaceNodesWithName.get(node.declaration.id); + + if (newName !== undefined) { + replaced.add(node.declaration.id); + + const oldName = node.declaration.id.name; + + return ([ + node.declaration, + exportLocalDeclaration.create({ + specifiers: [ + exportLocalSpecifier.create({ + loc: node.declaration.id.loc, + local: referenceIdentifier.quick(newName), + exported: identifier.quick(oldName), + }), + ], + }), + ] as Array); + } + } + + // Retain the correct exported names for `export const` + if (node.type === 'ExportLocalDeclaration' && node.declaration !== undefined) { + const bindings = getBindingIdentifiers(node.declaration); + let includesAny = false; + for (const node of bindings) { + if (replaceNodesWithName.has(node)) { + includesAny = true; + break; + } + } + + if (includesAny) { + return ([ + node.declaration, + exportLocalDeclaration.create({ + specifiers: bindings.map((node) => { + let local: string = node.name; + + const newName = replaceNodesWithName.get(node); + if (newName !== undefined) { + local = newName; + replaced.add(node); + } + + return exportLocalSpecifier.create({ + loc: node.loc, + local: referenceIdentifier.quick(local), + exported: identifier.quick(node.name), + }); + }), + }), + ] as Array); + } + } + + if (isVariableIdentifier(node)) { + const newName = replaceNodesWithName.get(node); + if (newName !== undefined) { + replaced.add(node); + return { + ...node, + name: newName, + loc: inheritLoc(node, node.name), + }; + } + } + + return node; + }, + }, + { + noScopeCreation: true, + }, + ); + + // + if (replaced.size !== replaceNodesWithName.size) { + console.log({replaced, replaceNodesWithName}); + throw new Error('Missed some bindings'); + } + + return assertSingleOrMultipleNodes(renamedNode); } diff --git a/packages/@romejs/js-ast-utils/template.ts b/packages/@romejs/js-ast-utils/template.ts index 04f52a8c58c..08aac2b190a 100644 --- a/packages/@romejs/js-ast-utils/template.ts +++ b/packages/@romejs/js-ast-utils/template.ts @@ -6,11 +6,11 @@ */ import { - AnyExpression, - AnyIdentifier, - AnyNode, - AnyStatement, - program, + AnyExpression, + AnyIdentifier, + AnyNode, + AnyStatement, + program, } from '@romejs/js-ast'; import {CompilerContext, Path} from '@romejs/js-compiler'; import removeLoc from './removeLoc'; @@ -20,13 +20,13 @@ import isIdentifierish from './isIdentifierish'; import {Dict} from '@romejs/typescript-helpers'; type Placeholder = { - type: AnyIdentifier['type']; - path: Array; + type: AnyIdentifier['type']; + path: Array; }; type BuiltTemplate = { - ast: AnyNode; - placeholderPaths: Array; + ast: AnyNode; + placeholderPaths: Array; }; type TemplatePlaceholders = Dict; @@ -34,168 +34,168 @@ type TemplatePlaceholders = Dict; const templateCache: Map = new Map(); function getTemplate(strs: TemplateStringsArray): BuiltTemplate { - const cached = templateCache.get(strs); - if (cached) { - return cached; - } - - // calculate amount of placeholders to insert - const pathCount = strs.length - 1; - - // create path ids - let placeholders: TemplatePlaceholders = {}; - const placeholderIds: Array = []; - for (let i = 0; i < pathCount; i++) { - const id = `__${String(i)}__`; - placeholderIds.push(id); - placeholders[id] = undefined; - } - - // interpolate placeholders and original code - let code = ''; - for (let i = 0; i < strs.length; i++) { - // add original part of code - code += strs[i]; - - // add in placeholder - const placeholder = placeholderIds[i]; - if (placeholder) { - code += placeholder; - } - } - - // parse the interpolated code - let ast = parseJS({ - input: code, - sourceType: 'template', - path: createUnknownFilePath('template'), - }); - - // remove `loc` properties - ast = program.assert(removeLoc(ast)); - - // traverse and find placeholders paths - function collectPlaceholderPaths(path: Path) { - const {node} = path; - if (isIdentifierish(node) && node.name in placeholders) { - placeholders[node.name] = { - type: node.type, - path: path.getPathKeys(), - }; - } - return node; - } - - const context = new CompilerContext({ - ast, - }); - context.reduce( - ast, - [{name: 'collectPlaceholderPaths', enter: collectPlaceholderPaths}], - ); - - const placeholderPaths: BuiltTemplate['placeholderPaths'] = []; - for (const id in placeholders) { - const path = placeholders[id]; - if (path === undefined) { - throw new Error(`Failed to find placeholder path for ${id}`); - } else { - placeholderPaths.push(path); - } - } - - return {ast, placeholderPaths}; + const cached = templateCache.get(strs); + if (cached) { + return cached; + } + + // calculate amount of placeholders to insert + const pathCount = strs.length - 1; + + // create path ids + let placeholders: TemplatePlaceholders = {}; + const placeholderIds: Array = []; + for (let i = 0; i < pathCount; i++) { + const id = `__${String(i)}__`; + placeholderIds.push(id); + placeholders[id] = undefined; + } + + // interpolate placeholders and original code + let code = ''; + for (let i = 0; i < strs.length; i++) { + // add original part of code + code += strs[i]; + + // add in placeholder + const placeholder = placeholderIds[i]; + if (placeholder) { + code += placeholder; + } + } + + // parse the interpolated code + let ast = parseJS({ + input: code, + sourceType: 'template', + path: createUnknownFilePath('template'), + }); + + // remove `loc` properties + ast = program.assert(removeLoc(ast)); + + // traverse and find placeholders paths + function collectPlaceholderPaths(path: Path) { + const {node} = path; + if (isIdentifierish(node) && node.name in placeholders) { + placeholders[node.name] = { + type: node.type, + path: path.getPathKeys(), + }; + } + return node; + } + + const context = new CompilerContext({ + ast, + }); + context.reduce( + ast, + [{name: 'collectPlaceholderPaths', enter: collectPlaceholderPaths}], + ); + + const placeholderPaths: BuiltTemplate['placeholderPaths'] = []; + for (const id in placeholders) { + const path = placeholders[id]; + if (path === undefined) { + throw new Error(`Failed to find placeholder path for ${id}`); + } else { + placeholderPaths.push(path); + } + } + + return {ast, placeholderPaths}; } type TemplateSubstitions = Array; function createIdentifier( - substitute: AnyNode | string, - expectedIdType: Placeholder['type'], + substitute: AnyNode | string, + expectedIdType: Placeholder['type'], ): AnyNode { - if (typeof substitute === 'string') { - // @ts-ignore: No idea why this error exists - return { - type: expectedIdType, - name: substitute, - }; - } else { - return substitute; - } + if (typeof substitute === 'string') { + // @ts-ignore: No idea why this error exists + return { + type: expectedIdType, + name: substitute, + }; + } else { + return substitute; + } } export default function template( - strs: TemplateStringsArray, - ...substitutions: TemplateSubstitions + strs: TemplateStringsArray, + ...substitutions: TemplateSubstitions ): AnyNode { - const {ast, placeholderPaths} = getTemplate(strs); - - // no substitutions so we can just return the ast! - if (!substitutions.length) { - return ast; - } - - // this case should never be hit - if (placeholderPaths.length !== substitutions.length) { - throw new Error('Expected subtituions to be the same length as paths'); - } - - const newAst = {...ast}; - - for (let i = 0; i < placeholderPaths.length; i++) { - const {type, path} = placeholderPaths[i]; - - const substitute: AnyNode = createIdentifier(substitutions[i], type); - // rome-ignore lint/noExplicitAny - let target: any = newAst; - - for (let i = 0; i < path.length; i++) { - const key = path[i]; - const isLast = i === path.length - 1; - - if (isLast) { - target[key] = substitute; - } else { - let currTarget = target[key]; - if (Array.isArray(currTarget)) { - currTarget = currTarget.slice(); - } else { - currTarget = {...currTarget}; - } - target[key] = currTarget; - target = currTarget; - } - } - } - - return newAst; + const {ast, placeholderPaths} = getTemplate(strs); + + // no substitutions so we can just return the ast! + if (!substitutions.length) { + return ast; + } + + // this case should never be hit + if (placeholderPaths.length !== substitutions.length) { + throw new Error('Expected subtituions to be the same length as paths'); + } + + const newAst = {...ast}; + + for (let i = 0; i < placeholderPaths.length; i++) { + const {type, path} = placeholderPaths[i]; + + const substitute: AnyNode = createIdentifier(substitutions[i], type); + // rome-ignore lint/noExplicitAny + let target: any = newAst; + + for (let i = 0; i < path.length; i++) { + const key = path[i]; + const isLast = i === path.length - 1; + + if (isLast) { + target[key] = substitute; + } else { + let currTarget = target[key]; + if (Array.isArray(currTarget)) { + currTarget = currTarget.slice(); + } else { + currTarget = {...currTarget}; + } + target[key] = currTarget; + target = currTarget; + } + } + } + + return newAst; } template.expression = ( - strs: TemplateStringsArray, - ...substitutions: TemplateSubstitions + strs: TemplateStringsArray, + ...substitutions: TemplateSubstitions ): AnyExpression => { - const first = template.statement(strs, ...substitutions); + const first = template.statement(strs, ...substitutions); - // Ensure that the single statement is an ExpressionStatement - if (first.type !== 'ExpressionStatement') { - throw new Error('Single statement should be an ExpressionStatement'); - } + // Ensure that the single statement is an ExpressionStatement + if (first.type !== 'ExpressionStatement') { + throw new Error('Single statement should be an ExpressionStatement'); + } - return first.expression; + return first.expression; }; template.statement = ( - strs: TemplateStringsArray, - ...substitutions: TemplateSubstitions + strs: TemplateStringsArray, + ...substitutions: TemplateSubstitions ): AnyStatement => { - // Parse the template, with caching - const ast = program.assert(template(strs, ...substitutions)); - - // Ensure that there's only a single statement in the Program body - const body = ast.body; - if (body.length !== 1) { - throw new Error("More than one statement isn't allowed for a template."); - } - return body[0]; + // Parse the template, with caching + const ast = program.assert(template(strs, ...substitutions)); + + // Ensure that there's only a single statement in the Program body + const body = ast.body; + if (body.length !== 1) { + throw new Error("More than one statement isn't allowed for a template."); + } + return body[0]; }; diff --git a/packages/@romejs/js-ast-utils/valueToNode.ts b/packages/@romejs/js-ast-utils/valueToNode.ts index 565499f486d..330badb52d4 100644 --- a/packages/@romejs/js-ast-utils/valueToNode.ts +++ b/packages/@romejs/js-ast-utils/valueToNode.ts @@ -6,86 +6,86 @@ */ import { - ArrayExpression, - BooleanLiteral, - NullLiteral, - NumericLiteral, - ObjectExpression, - ObjectProperty, - ReferenceIdentifier, - StringLiteral, - arrayExpression, - booleanLiteral, - nullLiteral, - numericLiteral, - objectExpression, - objectProperty, - referenceIdentifier, - staticPropertyKey, - stringLiteral, + ArrayExpression, + BooleanLiteral, + NullLiteral, + NumericLiteral, + ObjectExpression, + ObjectProperty, + ReferenceIdentifier, + StringLiteral, + arrayExpression, + booleanLiteral, + nullLiteral, + numericLiteral, + objectExpression, + objectProperty, + referenceIdentifier, + staticPropertyKey, + stringLiteral, } from '@romejs/js-ast'; import createPropertyKey from './createPropertyKey'; import {UnknownObject} from '@romejs/typescript-helpers'; export default function valueToNode( - value: unknown, - ancestry: Array = [], + value: unknown, + ancestry: Array = [], ): - | StringLiteral - | BooleanLiteral - | NumericLiteral - | ObjectExpression - | NullLiteral - | ReferenceIdentifier - | ArrayExpression { - if (ancestry.includes(value)) { - throw new Error('Recursion detected'); - } + | StringLiteral + | BooleanLiteral + | NumericLiteral + | ObjectExpression + | NullLiteral + | ReferenceIdentifier + | ArrayExpression { + if (ancestry.includes(value)) { + throw new Error('Recursion detected'); + } - switch (typeof value) { - case 'string': - return stringLiteral.quick(value); + switch (typeof value) { + case 'string': + return stringLiteral.quick(value); - case 'boolean': - return booleanLiteral.quick(value); + case 'boolean': + return booleanLiteral.quick(value); - case 'number': - return numericLiteral.quick(value); + case 'number': + return numericLiteral.quick(value); - case 'undefined': - return referenceIdentifier.quick('undefined'); + case 'undefined': + return referenceIdentifier.quick('undefined'); - case 'object': { - if (value === null) { - return nullLiteral.create({}); - } + case 'object': { + if (value === null) { + return nullLiteral.create({}); + } - const subAncestry = [...ancestry, value]; + const subAncestry = [...ancestry, value]; - if (Array.isArray(value)) { - return arrayExpression.quick( - value.map((elem) => valueToNode(elem, subAncestry)), - ); - } + if (Array.isArray(value)) { + return arrayExpression.quick( + value.map((elem) => valueToNode(elem, subAncestry)), + ); + } - const obj = (value as UnknownObject); - const props: Array = []; + const obj = (value as UnknownObject); + const props: Array = []; - for (let key in obj) { - props.push( - objectProperty.create({ - key: staticPropertyKey.create({ - value: createPropertyKey(key), - }), - value: valueToNode(obj[key], subAncestry), - }), - ); - } + for (let key in obj) { + props.push( + objectProperty.create({ + key: staticPropertyKey.create({ + value: createPropertyKey(key), + }), + value: valueToNode(obj[key], subAncestry), + }), + ); + } - return objectExpression.quick(props); - } + return objectExpression.quick(props); + } - default: - throw new Error('Do not know how to turn this value into a literal'); - } + default: + throw new Error('Do not know how to turn this value into a literal'); + } } diff --git a/packages/@romejs/js-ast/auxiliary/ArrayHole.ts b/packages/@romejs/js-ast/auxiliary/ArrayHole.ts index 3a3227f1ff8..f3b237e52e8 100644 --- a/packages/@romejs/js-ast/auxiliary/ArrayHole.ts +++ b/packages/@romejs/js-ast/auxiliary/ArrayHole.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ArrayHole = JSNodeBase & { - type: 'ArrayHole'; + type: 'ArrayHole'; }; export const arrayHole = createBuilder( - 'ArrayHole', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'ArrayHole', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/auxiliary/CatchClause.ts b/packages/@romejs/js-ast/auxiliary/CatchClause.ts index 1d0735e4633..c736422ea39 100644 --- a/packages/@romejs/js-ast/auxiliary/CatchClause.ts +++ b/packages/@romejs/js-ast/auxiliary/CatchClause.ts @@ -9,20 +9,20 @@ import {AnyBindingPattern, BlockStatement, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type CatchClause = JSNodeBase & { - type: 'CatchClause'; - param?: AnyBindingPattern; - body: BlockStatement; + type: 'CatchClause'; + param?: AnyBindingPattern; + body: BlockStatement; }; export const catchClause = createBuilder( - 'CatchClause', - { - bindingKeys: { - param: true, - }, - visitorKeys: { - param: true, - body: true, - }, - }, + 'CatchClause', + { + bindingKeys: { + param: true, + }, + visitorKeys: { + param: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/auxiliary/ComputedMemberProperty.ts b/packages/@romejs/js-ast/auxiliary/ComputedMemberProperty.ts index 7386e0f1ffe..799e9f43348 100644 --- a/packages/@romejs/js-ast/auxiliary/ComputedMemberProperty.ts +++ b/packages/@romejs/js-ast/auxiliary/ComputedMemberProperty.ts @@ -9,21 +9,21 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type ComputedMemberProperty = JSNodeBase & { - type: 'ComputedMemberProperty'; - value: AnyExpression; - optional?: boolean; + type: 'ComputedMemberProperty'; + value: AnyExpression; + optional?: boolean; }; export const computedMemberProperty = createQuickBuilder< - ComputedMemberProperty, - 'value' + ComputedMemberProperty, + 'value' >( - 'ComputedMemberProperty', - 'value', - { - bindingKeys: {}, - visitorKeys: { - value: true, - }, - }, + 'ComputedMemberProperty', + 'value', + { + bindingKeys: {}, + visitorKeys: { + value: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/auxiliary/FunctionHead.ts b/packages/@romejs/js-ast/auxiliary/FunctionHead.ts index bee82983b3c..27380055808 100644 --- a/packages/@romejs/js-ast/auxiliary/FunctionHead.ts +++ b/packages/@romejs/js-ast/auxiliary/FunctionHead.ts @@ -6,41 +6,41 @@ */ import { - AnyParamBindingPattern, - AnyPrimaryType, - AnyTargetBindingPattern, - BindingIdentifier, - JSNodeBase, - TSTypeParameterDeclaration, + AnyParamBindingPattern, + AnyPrimaryType, + AnyTargetBindingPattern, + BindingIdentifier, + JSNodeBase, + TSTypeParameterDeclaration, } from '../index'; import {createQuickBuilder} from '../utils'; export type FunctionHead = JSNodeBase & { - type: 'FunctionHead'; - params: Array; - rest?: AnyTargetBindingPattern; - thisType?: BindingIdentifier; - hasHoistedVars?: boolean; - generator?: boolean; - async?: boolean; - typeParameters?: TSTypeParameterDeclaration; - returnType?: AnyPrimaryType; + type: 'FunctionHead'; + params: Array; + rest?: AnyTargetBindingPattern; + thisType?: BindingIdentifier; + hasHoistedVars?: boolean; + generator?: boolean; + async?: boolean; + typeParameters?: TSTypeParameterDeclaration; + returnType?: AnyPrimaryType; }; export const functionHead = createQuickBuilder( - 'FunctionHead', - 'params', - { - bindingKeys: { - params: true, - rest: true, - }, - visitorKeys: { - params: true, - thisType: true, - rest: true, - returnType: true, - typeParameters: true, - }, - }, + 'FunctionHead', + 'params', + { + bindingKeys: { + params: true, + rest: true, + }, + visitorKeys: { + params: true, + thisType: true, + rest: true, + returnType: true, + typeParameters: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/auxiliary/Identifier.ts b/packages/@romejs/js-ast/auxiliary/Identifier.ts index 8dd9c729446..a127408fd8d 100644 --- a/packages/@romejs/js-ast/auxiliary/Identifier.ts +++ b/packages/@romejs/js-ast/auxiliary/Identifier.ts @@ -9,16 +9,16 @@ import {JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type Identifier = JSNodeBase & { - type: 'Identifier'; - name: string; - definite?: boolean; + type: 'Identifier'; + name: string; + definite?: boolean; }; export const identifier = createQuickBuilder( - 'Identifier', - 'name', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'Identifier', + 'name', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/auxiliary/SpreadElement.ts b/packages/@romejs/js-ast/auxiliary/SpreadElement.ts index 559ce364d58..e6fa0dbed35 100644 --- a/packages/@romejs/js-ast/auxiliary/SpreadElement.ts +++ b/packages/@romejs/js-ast/auxiliary/SpreadElement.ts @@ -9,17 +9,17 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type SpreadElement = JSNodeBase & { - type: 'SpreadElement'; - argument: AnyExpression; + type: 'SpreadElement'; + argument: AnyExpression; }; export const spreadElement = createQuickBuilder( - 'SpreadElement', - 'argument', - { - bindingKeys: {}, - visitorKeys: { - argument: true, - }, - }, + 'SpreadElement', + 'argument', + { + bindingKeys: {}, + visitorKeys: { + argument: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/auxiliary/StaticMemberProperty.ts b/packages/@romejs/js-ast/auxiliary/StaticMemberProperty.ts index ecc17fc4881..6d20f8996d7 100644 --- a/packages/@romejs/js-ast/auxiliary/StaticMemberProperty.ts +++ b/packages/@romejs/js-ast/auxiliary/StaticMemberProperty.ts @@ -9,21 +9,21 @@ import {Identifier, JSNodeBase, PrivateName} from '../index'; import {createQuickBuilder} from '../utils'; export type StaticMemberProperty = JSNodeBase & { - type: 'StaticMemberProperty'; - value: Identifier | PrivateName; - optional?: boolean; + type: 'StaticMemberProperty'; + value: Identifier | PrivateName; + optional?: boolean; }; export const staticMemberProperty = createQuickBuilder< - StaticMemberProperty, - 'value' + StaticMemberProperty, + 'value' >( - 'StaticMemberProperty', - 'value', - { - bindingKeys: {}, - visitorKeys: { - value: true, - }, - }, + 'StaticMemberProperty', + 'value', + { + bindingKeys: {}, + visitorKeys: { + value: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/auxiliary/SwitchCase.ts b/packages/@romejs/js-ast/auxiliary/SwitchCase.ts index d24394dc28b..6916b9e85a0 100644 --- a/packages/@romejs/js-ast/auxiliary/SwitchCase.ts +++ b/packages/@romejs/js-ast/auxiliary/SwitchCase.ts @@ -9,18 +9,18 @@ import {AnyExpression, AnyStatement, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type SwitchCase = JSNodeBase & { - type: 'SwitchCase'; - test?: AnyExpression; - consequent: Array; + type: 'SwitchCase'; + test?: AnyExpression; + consequent: Array; }; export const switchCase = createBuilder( - 'SwitchCase', - { - bindingKeys: {}, - visitorKeys: { - test: true, - consequent: true, - }, - }, + 'SwitchCase', + { + bindingKeys: {}, + visitorKeys: { + test: true, + consequent: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/auxiliary/TemplateElement.ts b/packages/@romejs/js-ast/auxiliary/TemplateElement.ts index b17443c2294..e0080c31b62 100644 --- a/packages/@romejs/js-ast/auxiliary/TemplateElement.ts +++ b/packages/@romejs/js-ast/auxiliary/TemplateElement.ts @@ -9,16 +9,16 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TemplateElement = JSNodeBase & { - type: 'TemplateElement'; - tail?: boolean; - cooked: string; - raw: string; + type: 'TemplateElement'; + tail?: boolean; + cooked: string; + raw: string; }; export const templateElement = createBuilder( - 'TemplateElement', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'TemplateElement', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/auxiliary/VariableDeclaration.ts b/packages/@romejs/js-ast/auxiliary/VariableDeclaration.ts index 5faaecc70be..9deef72dd72 100644 --- a/packages/@romejs/js-ast/auxiliary/VariableDeclaration.ts +++ b/packages/@romejs/js-ast/auxiliary/VariableDeclaration.ts @@ -11,19 +11,19 @@ import {createBuilder} from '../utils'; export type VariableDeclarationKind = 'var' | 'let' | 'const'; export type VariableDeclaration = JSNodeBase & { - type: 'VariableDeclaration'; - kind: VariableDeclarationKind; - declarations: Array; + type: 'VariableDeclaration'; + kind: VariableDeclarationKind; + declarations: Array; }; export const variableDeclaration = createBuilder( - 'VariableDeclaration', - { - bindingKeys: { - declarations: true, - }, - visitorKeys: { - declarations: true, - }, - }, + 'VariableDeclaration', + { + bindingKeys: { + declarations: true, + }, + visitorKeys: { + declarations: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/auxiliary/VariableDeclarator.ts b/packages/@romejs/js-ast/auxiliary/VariableDeclarator.ts index 7fc1b8396a3..cc94ef7038d 100644 --- a/packages/@romejs/js-ast/auxiliary/VariableDeclarator.ts +++ b/packages/@romejs/js-ast/auxiliary/VariableDeclarator.ts @@ -9,20 +9,20 @@ import {AnyExpression, AnyTargetBindingPattern, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type VariableDeclarator = JSNodeBase & { - type: 'VariableDeclarator'; - id: AnyTargetBindingPattern; - init?: AnyExpression; + type: 'VariableDeclarator'; + id: AnyTargetBindingPattern; + init?: AnyExpression; }; export const variableDeclarator = createBuilder( - 'VariableDeclarator', - { - bindingKeys: { - id: true, - }, - visitorKeys: { - id: true, - init: true, - }, - }, + 'VariableDeclarator', + { + bindingKeys: { + id: true, + }, + visitorKeys: { + id: true, + init: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/base.ts b/packages/@romejs/js-ast/base.ts index 2823080c562..fdd5f2b6487 100644 --- a/packages/@romejs/js-ast/base.ts +++ b/packages/@romejs/js-ast/base.ts @@ -8,7 +8,7 @@ import {NodeBase} from '@romejs/parser-core'; export type JSNodeBase = NodeBase & { - leadingComments?: Array; - trailingComments?: Array; - innerComments?: Array; + leadingComments?: Array; + trailingComments?: Array; + innerComments?: Array; }; diff --git a/packages/@romejs/js-ast/classes/ClassDeclaration.ts b/packages/@romejs/js-ast/classes/ClassDeclaration.ts index 17975046597..7e11a14d315 100644 --- a/packages/@romejs/js-ast/classes/ClassDeclaration.ts +++ b/packages/@romejs/js-ast/classes/ClassDeclaration.ts @@ -9,22 +9,22 @@ import {BindingIdentifier, ClassHead, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ClassDeclaration = JSNodeBase & { - type: 'ClassDeclaration'; - id: BindingIdentifier; - meta: ClassHead; - abstract?: boolean; - declare?: boolean; + type: 'ClassDeclaration'; + id: BindingIdentifier; + meta: ClassHead; + abstract?: boolean; + declare?: boolean; }; export const classDeclaration = createBuilder( - 'ClassDeclaration', - { - bindingKeys: { - id: true, - }, - visitorKeys: { - id: true, - meta: true, - }, - }, + 'ClassDeclaration', + { + bindingKeys: { + id: true, + }, + visitorKeys: { + id: true, + meta: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/classes/ClassExpression.ts b/packages/@romejs/js-ast/classes/ClassExpression.ts index 2a2e2af7263..dc2186ee1fc 100644 --- a/packages/@romejs/js-ast/classes/ClassExpression.ts +++ b/packages/@romejs/js-ast/classes/ClassExpression.ts @@ -9,20 +9,20 @@ import {BindingIdentifier, ClassHead, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ClassExpression = JSNodeBase & { - type: 'ClassExpression'; - id?: BindingIdentifier; - meta: ClassHead; + type: 'ClassExpression'; + id?: BindingIdentifier; + meta: ClassHead; }; export const classExpression = createBuilder( - 'ClassExpression', - { - bindingKeys: { - id: true, - }, - visitorKeys: { - id: true, - meta: true, - }, - }, + 'ClassExpression', + { + bindingKeys: { + id: true, + }, + visitorKeys: { + id: true, + meta: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/classes/ClassHead.ts b/packages/@romejs/js-ast/classes/ClassHead.ts index 9968e836965..6ae75847b1b 100644 --- a/packages/@romejs/js-ast/classes/ClassHead.ts +++ b/packages/@romejs/js-ast/classes/ClassHead.ts @@ -6,35 +6,35 @@ */ import { - AnyClassMember, - AnyExpression, - JSNodeBase, - TSExpressionWithTypeArguments, - TSTypeParameterDeclaration, - TSTypeParameterInstantiation, + AnyClassMember, + AnyExpression, + JSNodeBase, + TSExpressionWithTypeArguments, + TSTypeParameterDeclaration, + TSTypeParameterInstantiation, } from '../index'; import {createQuickBuilder} from '../utils'; export type ClassHead = JSNodeBase & { - type: 'ClassHead'; - superClass?: AnyExpression; - body: Array; - typeParameters?: TSTypeParameterDeclaration; - superTypeParameters?: TSTypeParameterInstantiation; - implements?: undefined | Array; + type: 'ClassHead'; + superClass?: AnyExpression; + body: Array; + typeParameters?: TSTypeParameterDeclaration; + superTypeParameters?: TSTypeParameterInstantiation; + implements?: undefined | Array; }; export const classHead = createQuickBuilder( - 'ClassHead', - 'body', - { - bindingKeys: {}, - visitorKeys: { - superClass: true, - body: true, - typeParameters: true, - superTypeParameters: true, - implements: true, - }, - }, + 'ClassHead', + 'body', + { + bindingKeys: {}, + visitorKeys: { + superClass: true, + body: true, + typeParameters: true, + superTypeParameters: true, + implements: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/classes/ClassMethod.ts b/packages/@romejs/js-ast/classes/ClassMethod.ts index b71ef4d4951..c216bfe5eb2 100644 --- a/packages/@romejs/js-ast/classes/ClassMethod.ts +++ b/packages/@romejs/js-ast/classes/ClassMethod.ts @@ -6,34 +6,34 @@ */ import { - BlockStatement, - ClassPropertyMeta, - FunctionHead, - JSNodeBase, + BlockStatement, + ClassPropertyMeta, + FunctionHead, + JSNodeBase, } from '../index'; import {createBuilder} from '../utils'; import {AnyObjectPropertyKey} from '../unions'; export type ClassMethod = JSNodeBase & { - type: 'ClassMethod'; - meta: ClassPropertyMeta; - key: AnyObjectPropertyKey; - kind: ClassMethodKind; - head: FunctionHead; - body: BlockStatement; + type: 'ClassMethod'; + meta: ClassPropertyMeta; + key: AnyObjectPropertyKey; + kind: ClassMethodKind; + head: FunctionHead; + body: BlockStatement; }; export type ClassMethodKind = 'constructor' | 'method' | 'get' | 'set'; export const classMethod = createBuilder( - 'ClassMethod', - { - bindingKeys: {}, - visitorKeys: { - key: true, - meta: true, - head: true, - body: true, - }, - }, + 'ClassMethod', + { + bindingKeys: {}, + visitorKeys: { + key: true, + meta: true, + head: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/classes/ClassPrivateMethod.ts b/packages/@romejs/js-ast/classes/ClassPrivateMethod.ts index 7ee5db284f8..34f95bc18e4 100644 --- a/packages/@romejs/js-ast/classes/ClassPrivateMethod.ts +++ b/packages/@romejs/js-ast/classes/ClassPrivateMethod.ts @@ -6,33 +6,33 @@ */ import { - BlockStatement, - ClassMethodKind, - ClassPropertyMeta, - FunctionHead, - JSNodeBase, - PrivateName, + BlockStatement, + ClassMethodKind, + ClassPropertyMeta, + FunctionHead, + JSNodeBase, + PrivateName, } from '../index'; import {createBuilder} from '../utils'; export type ClassPrivateMethod = JSNodeBase & { - type: 'ClassPrivateMethod'; - kind: ClassMethodKind; - key: PrivateName; - head: FunctionHead; - body: BlockStatement; - meta: ClassPropertyMeta; + type: 'ClassPrivateMethod'; + kind: ClassMethodKind; + key: PrivateName; + head: FunctionHead; + body: BlockStatement; + meta: ClassPropertyMeta; }; export const classPrivateMethod = createBuilder( - 'ClassPrivateMethod', - { - bindingKeys: {}, - visitorKeys: { - key: true, - meta: true, - head: true, - body: true, - }, - }, + 'ClassPrivateMethod', + { + bindingKeys: {}, + visitorKeys: { + key: true, + meta: true, + head: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/classes/ClassPrivateProperty.ts b/packages/@romejs/js-ast/classes/ClassPrivateProperty.ts index 92f410d82f2..561414e3b16 100644 --- a/packages/@romejs/js-ast/classes/ClassPrivateProperty.ts +++ b/packages/@romejs/js-ast/classes/ClassPrivateProperty.ts @@ -10,22 +10,22 @@ import {createBuilder} from '../utils'; import {ClassPropertyMeta} from './ClassPropertyMeta'; export type ClassPrivateProperty = JSNodeBase & { - type: 'ClassPrivateProperty'; - key: PrivateName; - meta: ClassPropertyMeta; - value: undefined | AnyExpression; - typeAnnotation?: AnyPrimaryType; + type: 'ClassPrivateProperty'; + key: PrivateName; + meta: ClassPropertyMeta; + value: undefined | AnyExpression; + typeAnnotation?: AnyPrimaryType; }; export const classPrivateProperty = createBuilder( - 'ClassPrivateProperty', - { - bindingKeys: {}, - visitorKeys: { - key: true, - meta: true, - value: true, - typeAnnotation: true, - }, - }, + 'ClassPrivateProperty', + { + bindingKeys: {}, + visitorKeys: { + key: true, + meta: true, + value: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/classes/ClassProperty.ts b/packages/@romejs/js-ast/classes/ClassProperty.ts index c5cd9fa6a36..296c63bc58b 100644 --- a/packages/@romejs/js-ast/classes/ClassProperty.ts +++ b/packages/@romejs/js-ast/classes/ClassProperty.ts @@ -6,32 +6,32 @@ */ import { - AnyExpression, - AnyObjectPropertyKey, - AnyPrimaryType, - ClassPropertyMeta, - JSNodeBase, + AnyExpression, + AnyObjectPropertyKey, + AnyPrimaryType, + ClassPropertyMeta, + JSNodeBase, } from '../index'; import {createBuilder} from '../utils'; export type ClassProperty = JSNodeBase & { - type: 'ClassProperty'; - key: AnyObjectPropertyKey; - meta: ClassPropertyMeta; - value?: AnyExpression; - typeAnnotation?: AnyPrimaryType; - definite?: boolean; + type: 'ClassProperty'; + key: AnyObjectPropertyKey; + meta: ClassPropertyMeta; + value?: AnyExpression; + typeAnnotation?: AnyPrimaryType; + definite?: boolean; }; export const classProperty = createBuilder( - 'ClassProperty', - { - bindingKeys: {}, - visitorKeys: { - key: true, - meta: true, - value: true, - typeAnnotation: true, - }, - }, + 'ClassProperty', + { + bindingKeys: {}, + visitorKeys: { + key: true, + meta: true, + value: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/classes/ClassPropertyMeta.ts b/packages/@romejs/js-ast/classes/ClassPropertyMeta.ts index b3e16791458..b05b7de3651 100644 --- a/packages/@romejs/js-ast/classes/ClassPropertyMeta.ts +++ b/packages/@romejs/js-ast/classes/ClassPropertyMeta.ts @@ -9,18 +9,18 @@ import {ConstTSAccessibility, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ClassPropertyMeta = JSNodeBase & { - type: 'ClassPropertyMeta'; - static?: boolean; - accessibility?: ConstTSAccessibility; - optional?: boolean; - readonly?: boolean; - abstract?: boolean; + type: 'ClassPropertyMeta'; + static?: boolean; + accessibility?: ConstTSAccessibility; + optional?: boolean; + readonly?: boolean; + abstract?: boolean; }; export const classPropertyMeta = createBuilder( - 'ClassPropertyMeta', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'ClassPropertyMeta', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/classes/PrivateName.ts b/packages/@romejs/js-ast/classes/PrivateName.ts index 6c08fc04778..341c26001d1 100644 --- a/packages/@romejs/js-ast/classes/PrivateName.ts +++ b/packages/@romejs/js-ast/classes/PrivateName.ts @@ -9,16 +9,16 @@ import {Identifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type PrivateName = JSNodeBase & { - type: 'PrivateName'; - id: Identifier; + type: 'PrivateName'; + id: Identifier; }; export const privateName = createBuilder( - 'PrivateName', - { - bindingKeys: {}, - visitorKeys: { - id: true, - }, - }, + 'PrivateName', + { + bindingKeys: {}, + visitorKeys: { + id: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/constants.ts b/packages/@romejs/js-ast/constants.ts index 3fdf031005f..ca40ad672e7 100644 --- a/packages/@romejs/js-ast/constants.ts +++ b/packages/@romejs/js-ast/constants.ts @@ -12,12 +12,12 @@ export type ConstExportModuleKind = 'type' | 'value'; export type ConstImportModuleKind = 'typeof' | ConstExportModuleKind; export type ConstTSModifier = - | 'readonly' - | 'abstract' - | 'static' - | 'public' - | 'private' - | 'protected'; + | 'readonly' + | 'abstract' + | 'static' + | 'public' + | 'private' + | 'protected'; export type ConstTSAccessibility = 'public' | 'protected' | 'private'; diff --git a/packages/@romejs/js-ast/core/CommentBlock.ts b/packages/@romejs/js-ast/core/CommentBlock.ts index f128c8de4d4..94db82a20f9 100644 --- a/packages/@romejs/js-ast/core/CommentBlock.ts +++ b/packages/@romejs/js-ast/core/CommentBlock.ts @@ -9,15 +9,15 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type CommentBlock = JSNodeBase & { - type: 'CommentBlock'; - value: string; - id: string; + type: 'CommentBlock'; + value: string; + id: string; }; export const commentBlock = createBuilder( - 'CommentBlock', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'CommentBlock', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/core/CommentLine.ts b/packages/@romejs/js-ast/core/CommentLine.ts index c9e4c41586c..a8d7358432d 100644 --- a/packages/@romejs/js-ast/core/CommentLine.ts +++ b/packages/@romejs/js-ast/core/CommentLine.ts @@ -9,15 +9,15 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type CommentLine = JSNodeBase & { - type: 'CommentLine'; - value: string; - id: string; + type: 'CommentLine'; + value: string; + id: string; }; export const commentLine = createBuilder( - 'CommentLine', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'CommentLine', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/core/Directive.ts b/packages/@romejs/js-ast/core/Directive.ts index 92be768beae..c02c5fa3841 100644 --- a/packages/@romejs/js-ast/core/Directive.ts +++ b/packages/@romejs/js-ast/core/Directive.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type Directive = JSNodeBase & { - type: 'Directive'; - value: string; + type: 'Directive'; + value: string; }; export const directive = createBuilder( - 'Directive', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'Directive', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/core/InterpreterDirective.ts b/packages/@romejs/js-ast/core/InterpreterDirective.ts index 07084f4f4b6..a228d99a0f8 100644 --- a/packages/@romejs/js-ast/core/InterpreterDirective.ts +++ b/packages/@romejs/js-ast/core/InterpreterDirective.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type InterpreterDirective = JSNodeBase & { - type: 'InterpreterDirective'; - value: string; + type: 'InterpreterDirective'; + value: string; }; export const interpreterDirective = createBuilder( - 'InterpreterDirective', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'InterpreterDirective', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/core/Program.ts b/packages/@romejs/js-ast/core/Program.ts index 1a3e6d76cd5..8d50bb9c907 100644 --- a/packages/@romejs/js-ast/core/Program.ts +++ b/packages/@romejs/js-ast/core/Program.ts @@ -6,56 +6,56 @@ */ import { - AnyComment, - AnyStatement, - ConstProgramSyntax, - ConstSourceType, - Directive, - InterpreterDirective, - JSNodeBase, + AnyComment, + AnyStatement, + ConstProgramSyntax, + ConstSourceType, + Directive, + InterpreterDirective, + JSNodeBase, } from '../index'; import {Diagnostics} from '@romejs/diagnostics'; import {createBuilder} from '../utils'; export type Program = JSNodeBase & { - type: 'Program'; - directives: Array; - body: Array; - filename: string; - interpreter: undefined | InterpreterDirective; - mtime: undefined | number; - corrupt: boolean; - sourceType: ConstSourceType; - diagnostics: Diagnostics; - comments: Array; - syntax: Array; - hasHoistedVars: boolean; + type: 'Program'; + directives: Array; + body: Array; + filename: string; + interpreter: undefined | InterpreterDirective; + mtime: undefined | number; + corrupt: boolean; + sourceType: ConstSourceType; + diagnostics: Diagnostics; + comments: Array; + syntax: Array; + hasHoistedVars: boolean; }; export const MOCK_PROGRAM: Program = { - type: 'Program', - directives: [], - body: [], - filename: 'unknown', - mtime: undefined, - interpreter: undefined, - corrupt: false, - sourceType: 'module', - diagnostics: [], - comments: [], - syntax: [], - hasHoistedVars: false, + type: 'Program', + directives: [], + body: [], + filename: 'unknown', + mtime: undefined, + interpreter: undefined, + corrupt: false, + sourceType: 'module', + diagnostics: [], + comments: [], + syntax: [], + hasHoistedVars: false, }; export const program = createBuilder( - 'Program', - { - bindingKeys: {}, - visitorKeys: { - interpreter: true, - directives: true, - body: true, - comments: true, - }, - }, + 'Program', + { + bindingKeys: {}, + visitorKeys: { + interpreter: true, + directives: true, + body: true, + comments: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/ArrayExpression.ts b/packages/@romejs/js-ast/expressions/ArrayExpression.ts index 70d2d625c1a..45ed49bafbd 100644 --- a/packages/@romejs/js-ast/expressions/ArrayExpression.ts +++ b/packages/@romejs/js-ast/expressions/ArrayExpression.ts @@ -9,17 +9,17 @@ import {AnyExpression, ArrayHole, JSNodeBase, SpreadElement} from '../index'; import {createQuickBuilder} from '../utils'; export type ArrayExpression = JSNodeBase & { - type: 'ArrayExpression'; - elements: Array; + type: 'ArrayExpression'; + elements: Array; }; export const arrayExpression = createQuickBuilder( - 'ArrayExpression', - 'elements', - { - bindingKeys: {}, - visitorKeys: { - elements: true, - }, - }, + 'ArrayExpression', + 'elements', + { + bindingKeys: {}, + visitorKeys: { + elements: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/ArrowFunctionExpression.ts b/packages/@romejs/js-ast/expressions/ArrowFunctionExpression.ts index dec964a34f5..518af50946c 100644 --- a/packages/@romejs/js-ast/expressions/ArrowFunctionExpression.ts +++ b/packages/@romejs/js-ast/expressions/ArrowFunctionExpression.ts @@ -11,19 +11,19 @@ import {BlockStatement} from '../statements/BlockStatement'; import {AnyExpression} from '../unions'; export type ArrowFunctionExpression = JSNodeBase & { - type: 'ArrowFunctionExpression'; - head: FunctionHead; - body: BlockStatement | AnyExpression; - generator?: void; + type: 'ArrowFunctionExpression'; + head: FunctionHead; + body: BlockStatement | AnyExpression; + generator?: void; }; export const arrowFunctionExpression = createBuilder( - 'ArrowFunctionExpression', - { - bindingKeys: {}, - visitorKeys: { - head: true, - body: true, - }, - }, + 'ArrowFunctionExpression', + { + bindingKeys: {}, + visitorKeys: { + head: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/AssignmentExpression.ts b/packages/@romejs/js-ast/expressions/AssignmentExpression.ts index 5a4f368c496..5d13383d87b 100644 --- a/packages/@romejs/js-ast/expressions/AssignmentExpression.ts +++ b/packages/@romejs/js-ast/expressions/AssignmentExpression.ts @@ -9,34 +9,34 @@ import {AnyAssignmentPattern, AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type AssignmentExpression = JSNodeBase & { - type: 'AssignmentExpression'; - operator: AssignmentOperator; - left: AnyAssignmentPattern; - right: AnyExpression; + type: 'AssignmentExpression'; + operator: AssignmentOperator; + left: AnyAssignmentPattern; + right: AnyExpression; }; export type AssignmentOperator = - | '=' - | '+=' - | '-=' - | '*=' - | '/=' - | '%=' - | '<<=' - | '>>=' - | '>>>=' - | '|=' - | '^=' - | '&=' - | '??='; + | '=' + | '+=' + | '-=' + | '*=' + | '/=' + | '%=' + | '<<=' + | '>>=' + | '>>>=' + | '|=' + | '^=' + | '&=' + | '??='; export const assignmentExpression = createBuilder( - 'AssignmentExpression', - { - bindingKeys: {}, - visitorKeys: { - left: true, - right: true, - }, - }, + 'AssignmentExpression', + { + bindingKeys: {}, + visitorKeys: { + left: true, + right: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/AwaitExpression.ts b/packages/@romejs/js-ast/expressions/AwaitExpression.ts index 7239a6f39a1..d6c2ae85072 100644 --- a/packages/@romejs/js-ast/expressions/AwaitExpression.ts +++ b/packages/@romejs/js-ast/expressions/AwaitExpression.ts @@ -9,16 +9,16 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type AwaitExpression = JSNodeBase & { - type: 'AwaitExpression'; - argument?: AnyExpression; + type: 'AwaitExpression'; + argument?: AnyExpression; }; export const awaitExpression = createBuilder( - 'AwaitExpression', - { - bindingKeys: {}, - visitorKeys: { - argument: true, - }, - }, + 'AwaitExpression', + { + bindingKeys: {}, + visitorKeys: { + argument: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/BinaryExpression.ts b/packages/@romejs/js-ast/expressions/BinaryExpression.ts index c18a2d1f5fb..9c46f4f5da0 100644 --- a/packages/@romejs/js-ast/expressions/BinaryExpression.ts +++ b/packages/@romejs/js-ast/expressions/BinaryExpression.ts @@ -9,43 +9,43 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type BinaryExpression = JSNodeBase & { - type: 'BinaryExpression'; - operator: BinaryOperator; - left: AnyExpression; - right: AnyExpression; + type: 'BinaryExpression'; + operator: BinaryOperator; + left: AnyExpression; + right: AnyExpression; }; export type BinaryOperator = - | '==' - | '!=' - | '===' - | '**' - | '!==' - | '<' - | '<=' - | '>' - | '>=' - | '<<' - | '>>' - | '>>>' - | '+' - | '-' - | '*' - | '/' - | '%' - | '|' - | '^' - | '&' - | 'in' - | 'instanceof'; + | '==' + | '!=' + | '===' + | '**' + | '!==' + | '<' + | '<=' + | '>' + | '>=' + | '<<' + | '>>' + | '>>>' + | '+' + | '-' + | '*' + | '/' + | '%' + | '|' + | '^' + | '&' + | 'in' + | 'instanceof'; export const binaryExpression = createBuilder( - 'BinaryExpression', - { - bindingKeys: {}, - visitorKeys: { - left: true, - right: true, - }, - }, + 'BinaryExpression', + { + bindingKeys: {}, + visitorKeys: { + left: true, + right: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/CallExpression.ts b/packages/@romejs/js-ast/expressions/CallExpression.ts index 7a26fd4ce66..fe76e419cf3 100644 --- a/packages/@romejs/js-ast/expressions/CallExpression.ts +++ b/packages/@romejs/js-ast/expressions/CallExpression.ts @@ -6,29 +6,29 @@ */ import { - AnyExpression, - JSNodeBase, - SpreadElement, - Super, - TSTypeParameterInstantiation, + AnyExpression, + JSNodeBase, + SpreadElement, + Super, + TSTypeParameterInstantiation, } from '../index'; import {createBuilder} from '../utils'; export type CallExpression = JSNodeBase & { - type: 'CallExpression'; - callee: AnyExpression | Super; - arguments: Array; - typeArguments?: TSTypeParameterInstantiation; + type: 'CallExpression'; + callee: AnyExpression | Super; + arguments: Array; + typeArguments?: TSTypeParameterInstantiation; }; export const callExpression = createBuilder( - 'CallExpression', - { - bindingKeys: {}, - visitorKeys: { - callee: true, - arguments: true, - typeArguments: true, - }, - }, + 'CallExpression', + { + bindingKeys: {}, + visitorKeys: { + callee: true, + arguments: true, + typeArguments: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/ConditionalExpression.ts b/packages/@romejs/js-ast/expressions/ConditionalExpression.ts index d569a74e8c9..996eddcdef0 100644 --- a/packages/@romejs/js-ast/expressions/ConditionalExpression.ts +++ b/packages/@romejs/js-ast/expressions/ConditionalExpression.ts @@ -9,20 +9,20 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ConditionalExpression = JSNodeBase & { - type: 'ConditionalExpression'; - test: AnyExpression; - alternate: AnyExpression; - consequent: AnyExpression; + type: 'ConditionalExpression'; + test: AnyExpression; + alternate: AnyExpression; + consequent: AnyExpression; }; export const conditionalExpression = createBuilder( - 'ConditionalExpression', - { - bindingKeys: {}, - visitorKeys: { - test: true, - consequent: true, - alternate: true, - }, - }, + 'ConditionalExpression', + { + bindingKeys: {}, + visitorKeys: { + test: true, + consequent: true, + alternate: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/DoExpression.ts b/packages/@romejs/js-ast/expressions/DoExpression.ts index a0b3a2d615d..d969275bf79 100644 --- a/packages/@romejs/js-ast/expressions/DoExpression.ts +++ b/packages/@romejs/js-ast/expressions/DoExpression.ts @@ -9,16 +9,16 @@ import {BlockStatement, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type DoExpression = JSNodeBase & { - type: 'DoExpression'; - body: BlockStatement; + type: 'DoExpression'; + body: BlockStatement; }; export const doExpression = createBuilder( - 'DoExpression', - { - bindingKeys: {}, - visitorKeys: { - body: true, - }, - }, + 'DoExpression', + { + bindingKeys: {}, + visitorKeys: { + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/FunctionExpression.ts b/packages/@romejs/js-ast/expressions/FunctionExpression.ts index 7525a2385d0..6eb78ebcce5 100644 --- a/packages/@romejs/js-ast/expressions/FunctionExpression.ts +++ b/packages/@romejs/js-ast/expressions/FunctionExpression.ts @@ -10,22 +10,22 @@ import {createBuilder} from '../utils'; import {BlockStatement} from '../statements/BlockStatement'; export type FunctionExpression = JSNodeBase & { - type: 'FunctionExpression'; - id?: BindingIdentifier; - head: FunctionHead; - body: BlockStatement; + type: 'FunctionExpression'; + id?: BindingIdentifier; + head: FunctionHead; + body: BlockStatement; }; export const functionExpression = createBuilder( - 'FunctionExpression', - { - bindingKeys: { - id: true, - }, - visitorKeys: { - head: true, - id: true, - body: true, - }, - }, + 'FunctionExpression', + { + bindingKeys: { + id: true, + }, + visitorKeys: { + head: true, + id: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/LogicalExpression.ts b/packages/@romejs/js-ast/expressions/LogicalExpression.ts index 019fdee27f2..b8e64c3a943 100644 --- a/packages/@romejs/js-ast/expressions/LogicalExpression.ts +++ b/packages/@romejs/js-ast/expressions/LogicalExpression.ts @@ -9,21 +9,21 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type LogicalExpression = JSNodeBase & { - type: 'LogicalExpression'; - operator: LogicalOperator; - left: AnyExpression; - right: AnyExpression; + type: 'LogicalExpression'; + operator: LogicalOperator; + left: AnyExpression; + right: AnyExpression; }; export type LogicalOperator = '||' | '&&' | '??'; export const logicalExpression = createBuilder( - 'LogicalExpression', - { - bindingKeys: {}, - visitorKeys: { - left: true, - right: true, - }, - }, + 'LogicalExpression', + { + bindingKeys: {}, + visitorKeys: { + left: true, + right: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/MemberExpression.ts b/packages/@romejs/js-ast/expressions/MemberExpression.ts index 64a3e752461..4371aeb498d 100644 --- a/packages/@romejs/js-ast/expressions/MemberExpression.ts +++ b/packages/@romejs/js-ast/expressions/MemberExpression.ts @@ -6,27 +6,27 @@ */ import { - AnyExpression, - ComputedMemberProperty, - JSNodeBase, - StaticMemberProperty, - Super, + AnyExpression, + ComputedMemberProperty, + JSNodeBase, + StaticMemberProperty, + Super, } from '../index'; import {createBuilder} from '../utils'; export type MemberExpression = JSNodeBase & { - type: 'MemberExpression'; - object: AnyExpression | Super; - property: StaticMemberProperty | ComputedMemberProperty; + type: 'MemberExpression'; + object: AnyExpression | Super; + property: StaticMemberProperty | ComputedMemberProperty; }; export const memberExpression = createBuilder( - 'MemberExpression', - { - bindingKeys: {}, - visitorKeys: { - object: true, - property: true, - }, - }, + 'MemberExpression', + { + bindingKeys: {}, + visitorKeys: { + object: true, + property: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/MetaProperty.ts b/packages/@romejs/js-ast/expressions/MetaProperty.ts index 24e4d5fe713..de87781cd80 100644 --- a/packages/@romejs/js-ast/expressions/MetaProperty.ts +++ b/packages/@romejs/js-ast/expressions/MetaProperty.ts @@ -9,18 +9,18 @@ import {Identifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type MetaProperty = JSNodeBase & { - type: 'MetaProperty'; - meta: Identifier; - property: Identifier; + type: 'MetaProperty'; + meta: Identifier; + property: Identifier; }; export const metaProperty = createBuilder( - 'MetaProperty', - { - bindingKeys: {}, - visitorKeys: { - meta: true, - property: true, - }, - }, + 'MetaProperty', + { + bindingKeys: {}, + visitorKeys: { + meta: true, + property: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/NewExpression.ts b/packages/@romejs/js-ast/expressions/NewExpression.ts index 03da6b42586..65ba7a2ae45 100644 --- a/packages/@romejs/js-ast/expressions/NewExpression.ts +++ b/packages/@romejs/js-ast/expressions/NewExpression.ts @@ -6,30 +6,30 @@ */ import { - AnyExpression, - JSNodeBase, - SpreadElement, - Super, - TSTypeParameterInstantiation, + AnyExpression, + JSNodeBase, + SpreadElement, + Super, + TSTypeParameterInstantiation, } from '../index'; import {createBuilder} from '../utils'; export type NewExpression = JSNodeBase & { - type: 'NewExpression'; - callee: AnyExpression | Super; - arguments: Array; - typeArguments?: undefined | TSTypeParameterInstantiation; - optional?: boolean; + type: 'NewExpression'; + callee: AnyExpression | Super; + arguments: Array; + typeArguments?: undefined | TSTypeParameterInstantiation; + optional?: boolean; }; export const newExpression = createBuilder( - 'NewExpression', - { - bindingKeys: {}, - visitorKeys: { - callee: true, - arguments: true, - typeArguments: true, - }, - }, + 'NewExpression', + { + bindingKeys: {}, + visitorKeys: { + callee: true, + arguments: true, + typeArguments: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/OptionalCallExpression.ts b/packages/@romejs/js-ast/expressions/OptionalCallExpression.ts index de23628673d..d5cf68392dc 100644 --- a/packages/@romejs/js-ast/expressions/OptionalCallExpression.ts +++ b/packages/@romejs/js-ast/expressions/OptionalCallExpression.ts @@ -6,29 +6,29 @@ */ import { - AnyExpression, - JSNodeBase, - SpreadElement, - Super, - TSTypeParameterInstantiation, + AnyExpression, + JSNodeBase, + SpreadElement, + Super, + TSTypeParameterInstantiation, } from '../index'; import {createBuilder} from '../utils'; export type OptionalCallExpression = JSNodeBase & { - type: 'OptionalCallExpression'; - callee: AnyExpression | Super; - arguments: Array; - typeArguments?: TSTypeParameterInstantiation; + type: 'OptionalCallExpression'; + callee: AnyExpression | Super; + arguments: Array; + typeArguments?: TSTypeParameterInstantiation; }; export const optionalCallExpression = createBuilder( - 'OptionalCallExpression', - { - bindingKeys: {}, - visitorKeys: { - callee: true, - arguments: true, - typeArguments: true, - }, - }, + 'OptionalCallExpression', + { + bindingKeys: {}, + visitorKeys: { + callee: true, + arguments: true, + typeArguments: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/ReferenceIdentifier.ts b/packages/@romejs/js-ast/expressions/ReferenceIdentifier.ts index 0a2f18a04b9..5ddb5c003ae 100644 --- a/packages/@romejs/js-ast/expressions/ReferenceIdentifier.ts +++ b/packages/@romejs/js-ast/expressions/ReferenceIdentifier.ts @@ -9,22 +9,22 @@ import {JSNodeBase, PatternMeta} from '../index'; import {createQuickBuilder} from '../utils'; export type ReferenceIdentifier = JSNodeBase & { - type: 'ReferenceIdentifier'; - name: string; - definite?: boolean; - meta?: PatternMeta; + type: 'ReferenceIdentifier'; + name: string; + definite?: boolean; + meta?: PatternMeta; }; export const referenceIdentifier = createQuickBuilder< - ReferenceIdentifier, - 'name' + ReferenceIdentifier, + 'name' >( - 'ReferenceIdentifier', - 'name', - { - bindingKeys: {}, - visitorKeys: { - meta: true, - }, - }, + 'ReferenceIdentifier', + 'name', + { + bindingKeys: {}, + visitorKeys: { + meta: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/SequenceExpression.ts b/packages/@romejs/js-ast/expressions/SequenceExpression.ts index ddf032f4670..46a8ba306b4 100644 --- a/packages/@romejs/js-ast/expressions/SequenceExpression.ts +++ b/packages/@romejs/js-ast/expressions/SequenceExpression.ts @@ -9,11 +9,11 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type SequenceExpression = JSNodeBase & { - type: 'SequenceExpression'; - expressions: Array; + type: 'SequenceExpression'; + expressions: Array; }; export const sequenceExpression = createBuilder( - 'SequenceExpression', - {bindingKeys: {}, visitorKeys: {expressions: true}}, + 'SequenceExpression', + {bindingKeys: {}, visitorKeys: {expressions: true}}, ); diff --git a/packages/@romejs/js-ast/expressions/Super.ts b/packages/@romejs/js-ast/expressions/Super.ts index 6758fb557c2..660f40b0038 100644 --- a/packages/@romejs/js-ast/expressions/Super.ts +++ b/packages/@romejs/js-ast/expressions/Super.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type Super = JSNodeBase & { - type: 'Super'; + type: 'Super'; }; export const _super = createBuilder( - 'Super', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'Super', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/expressions/TaggedTemplateExpression.ts b/packages/@romejs/js-ast/expressions/TaggedTemplateExpression.ts index 4e029b8dffc..3aeb9016c81 100644 --- a/packages/@romejs/js-ast/expressions/TaggedTemplateExpression.ts +++ b/packages/@romejs/js-ast/expressions/TaggedTemplateExpression.ts @@ -6,28 +6,28 @@ */ import { - AnyExpression, - JSNodeBase, - TSTypeParameterInstantiation, - TemplateLiteral, + AnyExpression, + JSNodeBase, + TSTypeParameterInstantiation, + TemplateLiteral, } from '../index'; import {createBuilder} from '../utils'; export type TaggedTemplateExpression = JSNodeBase & { - type: 'TaggedTemplateExpression'; - tag: AnyExpression; - quasi: TemplateLiteral; - typeArguments?: TSTypeParameterInstantiation; + type: 'TaggedTemplateExpression'; + tag: AnyExpression; + quasi: TemplateLiteral; + typeArguments?: TSTypeParameterInstantiation; }; export const taggedTemplateExpression = createBuilder( - 'TaggedTemplateExpression', - { - bindingKeys: {}, - visitorKeys: { - tag: true, - quasi: true, - typeArguments: true, - }, - }, + 'TaggedTemplateExpression', + { + bindingKeys: {}, + visitorKeys: { + tag: true, + quasi: true, + typeArguments: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/ThisExpression.ts b/packages/@romejs/js-ast/expressions/ThisExpression.ts index e561d79e05d..9df9e356ca4 100644 --- a/packages/@romejs/js-ast/expressions/ThisExpression.ts +++ b/packages/@romejs/js-ast/expressions/ThisExpression.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ThisExpression = JSNodeBase & { - type: 'ThisExpression'; + type: 'ThisExpression'; }; export const thisExpression = createBuilder( - 'ThisExpression', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'ThisExpression', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/expressions/UnaryExpression.ts b/packages/@romejs/js-ast/expressions/UnaryExpression.ts index 2e5470fb038..a40365900dc 100644 --- a/packages/@romejs/js-ast/expressions/UnaryExpression.ts +++ b/packages/@romejs/js-ast/expressions/UnaryExpression.ts @@ -9,28 +9,28 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type UnaryExpression = JSNodeBase & { - type: 'UnaryExpression'; - operator: UnaryOperator; - prefix?: boolean; - argument: AnyExpression; + type: 'UnaryExpression'; + operator: UnaryOperator; + prefix?: boolean; + argument: AnyExpression; }; export type UnaryOperator = - | '-' - | '+' - | '!' - | '~' - | 'typeof' - | 'void' - | 'delete' - | 'throw'; + | '-' + | '+' + | '!' + | '~' + | 'typeof' + | 'void' + | 'delete' + | 'throw'; export const unaryExpression = createBuilder( - 'UnaryExpression', - { - bindingKeys: {}, - visitorKeys: { - argument: true, - }, - }, + 'UnaryExpression', + { + bindingKeys: {}, + visitorKeys: { + argument: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/UpdateExpression.ts b/packages/@romejs/js-ast/expressions/UpdateExpression.ts index 52f37b1d7e7..0de0ffb9b30 100644 --- a/packages/@romejs/js-ast/expressions/UpdateExpression.ts +++ b/packages/@romejs/js-ast/expressions/UpdateExpression.ts @@ -9,20 +9,20 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type UpdateExpression = JSNodeBase & { - type: 'UpdateExpression'; - operator: UpdateOperator; - argument: AnyExpression; - prefix?: boolean; + type: 'UpdateExpression'; + operator: UpdateOperator; + argument: AnyExpression; + prefix?: boolean; }; export type UpdateOperator = '++' | '--'; export const updateExpression = createBuilder( - 'UpdateExpression', - { - bindingKeys: {}, - visitorKeys: { - argument: true, - }, - }, + 'UpdateExpression', + { + bindingKeys: {}, + visitorKeys: { + argument: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/expressions/YieldExpression.ts b/packages/@romejs/js-ast/expressions/YieldExpression.ts index b98c8364b4d..298b897e53a 100644 --- a/packages/@romejs/js-ast/expressions/YieldExpression.ts +++ b/packages/@romejs/js-ast/expressions/YieldExpression.ts @@ -9,12 +9,12 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type YieldExpression = JSNodeBase & { - type: 'YieldExpression'; - delegate?: boolean; - argument?: AnyExpression; + type: 'YieldExpression'; + delegate?: boolean; + argument?: AnyExpression; }; export const yieldExpression = createBuilder( - 'YieldExpression', - {bindingKeys: {}, visitorKeys: {argument: true}}, + 'YieldExpression', + {bindingKeys: {}, visitorKeys: {argument: true}}, ); diff --git a/packages/@romejs/js-ast/jsx/JSXAttribute.ts b/packages/@romejs/js-ast/jsx/JSXAttribute.ts index 4963dbbf782..f94c456e2be 100644 --- a/packages/@romejs/js-ast/jsx/JSXAttribute.ts +++ b/packages/@romejs/js-ast/jsx/JSXAttribute.ts @@ -6,34 +6,34 @@ */ import { - JSNodeBase, - JSXElement, - JSXExpressionContainer, - JSXFragment, - JSXIdentifier, - JSXNamespacedName, - StringLiteral, + JSNodeBase, + JSXElement, + JSXExpressionContainer, + JSXFragment, + JSXIdentifier, + JSXNamespacedName, + StringLiteral, } from '../index'; import {createBuilder} from '../utils'; export type JSXAttribute = JSNodeBase & { - type: 'JSXAttribute'; - name: JSXIdentifier | JSXNamespacedName; - value?: - | undefined - | JSXElement - | JSXFragment - | StringLiteral - | JSXExpressionContainer; + type: 'JSXAttribute'; + name: JSXIdentifier | JSXNamespacedName; + value?: + | undefined + | JSXElement + | JSXFragment + | StringLiteral + | JSXExpressionContainer; }; export const jsxAttribute = createBuilder( - 'JSXAttribute', - { - bindingKeys: {}, - visitorKeys: { - name: true, - value: true, - }, - }, + 'JSXAttribute', + { + bindingKeys: {}, + visitorKeys: { + name: true, + value: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXElement.ts b/packages/@romejs/js-ast/jsx/JSXElement.ts index db8269c1c0e..c69d7f771d6 100644 --- a/packages/@romejs/js-ast/jsx/JSXElement.ts +++ b/packages/@romejs/js-ast/jsx/JSXElement.ts @@ -6,45 +6,45 @@ */ import { - JSNodeBase, - JSXAttribute, - JSXExpressionContainer, - JSXFragment, - JSXIdentifier, - JSXMemberExpression, - JSXNamespacedName, - JSXReferenceIdentifier, - JSXSpreadAttribute, - JSXSpreadChild, - JSXText, - TSTypeParameterInstantiation, + JSNodeBase, + JSXAttribute, + JSXExpressionContainer, + JSXFragment, + JSXIdentifier, + JSXMemberExpression, + JSXNamespacedName, + JSXReferenceIdentifier, + JSXSpreadAttribute, + JSXSpreadChild, + JSXText, + TSTypeParameterInstantiation, } from '../index'; import {createBuilder} from '../utils'; export type JSXElement = JSNodeBase & { - type: 'JSXElement'; - name: - | JSXReferenceIdentifier - | JSXIdentifier - | JSXNamespacedName - | JSXMemberExpression; - typeArguments?: TSTypeParameterInstantiation; - attributes: Array; - selfClosing: boolean; - children: Array< - JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement | JSXFragment - >; + type: 'JSXElement'; + name: + | JSXReferenceIdentifier + | JSXIdentifier + | JSXNamespacedName + | JSXMemberExpression; + typeArguments?: TSTypeParameterInstantiation; + attributes: Array; + selfClosing: boolean; + children: Array< + JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement | JSXFragment + >; }; export const jsxElement = createBuilder( - 'JSXElement', - { - bindingKeys: {}, - visitorKeys: { - name: true, - typeArguments: true, - attributes: true, - children: true, - }, - }, + 'JSXElement', + { + bindingKeys: {}, + visitorKeys: { + name: true, + typeArguments: true, + attributes: true, + children: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXEmptyExpression.ts b/packages/@romejs/js-ast/jsx/JSXEmptyExpression.ts index cbc048811ec..e5a8ae6a755 100644 --- a/packages/@romejs/js-ast/jsx/JSXEmptyExpression.ts +++ b/packages/@romejs/js-ast/jsx/JSXEmptyExpression.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type JSXEmptyExpression = JSNodeBase & { - type: 'JSXEmptyExpression'; + type: 'JSXEmptyExpression'; }; export const jsxEmptyExpression = createBuilder( - 'JSXEmptyExpression', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'JSXEmptyExpression', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXExpressionContainer.ts b/packages/@romejs/js-ast/jsx/JSXExpressionContainer.ts index e9a707e5728..ca21a110dff 100644 --- a/packages/@romejs/js-ast/jsx/JSXExpressionContainer.ts +++ b/packages/@romejs/js-ast/jsx/JSXExpressionContainer.ts @@ -9,16 +9,16 @@ import {AnyExpression, JSNodeBase, JSXEmptyExpression} from '../index'; import {createBuilder} from '../utils'; export type JSXExpressionContainer = JSNodeBase & { - type: 'JSXExpressionContainer'; - expression: AnyExpression | JSXEmptyExpression; + type: 'JSXExpressionContainer'; + expression: AnyExpression | JSXEmptyExpression; }; export const jsxExpressionContainer = createBuilder( - 'JSXExpressionContainer', - { - bindingKeys: {}, - visitorKeys: { - expression: true, - }, - }, + 'JSXExpressionContainer', + { + bindingKeys: {}, + visitorKeys: { + expression: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXFragment.ts b/packages/@romejs/js-ast/jsx/JSXFragment.ts index e0f38af6579..d96ea5a6db2 100644 --- a/packages/@romejs/js-ast/jsx/JSXFragment.ts +++ b/packages/@romejs/js-ast/jsx/JSXFragment.ts @@ -9,16 +9,16 @@ import {JSNodeBase, JSXElement} from '../index'; import {createBuilder} from '../utils'; export type JSXFragment = JSNodeBase & { - type: 'JSXFragment'; - children: JSXElement['children']; + type: 'JSXFragment'; + children: JSXElement['children']; }; export const jsxFragment = createBuilder( - 'JSXFragment', - { - bindingKeys: {}, - visitorKeys: { - children: true, - }, - }, + 'JSXFragment', + { + bindingKeys: {}, + visitorKeys: { + children: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXIdentifier.ts b/packages/@romejs/js-ast/jsx/JSXIdentifier.ts index 2e0cba64b04..96552ba5846 100644 --- a/packages/@romejs/js-ast/jsx/JSXIdentifier.ts +++ b/packages/@romejs/js-ast/jsx/JSXIdentifier.ts @@ -9,15 +9,15 @@ import {JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type JSXIdentifier = JSNodeBase & { - type: 'JSXIdentifier'; - name: string; + type: 'JSXIdentifier'; + name: string; }; export const jsxIdentifier = createQuickBuilder( - 'JSXIdentifier', - 'name', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'JSXIdentifier', + 'name', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXMemberExpression.ts b/packages/@romejs/js-ast/jsx/JSXMemberExpression.ts index 1a3479eeecc..7c8fd41b35c 100644 --- a/packages/@romejs/js-ast/jsx/JSXMemberExpression.ts +++ b/packages/@romejs/js-ast/jsx/JSXMemberExpression.ts @@ -6,30 +6,30 @@ */ import { - JSNodeBase, - JSXIdentifier, - JSXNamespacedName, - JSXReferenceIdentifier, + JSNodeBase, + JSXIdentifier, + JSXNamespacedName, + JSXReferenceIdentifier, } from '../index'; import {createBuilder} from '../utils'; export type JSXMemberExpression = JSNodeBase & { - type: 'JSXMemberExpression'; - object: - | JSXMemberExpression - | JSXIdentifier - | JSXReferenceIdentifier - | JSXNamespacedName; - property: JSXIdentifier; + type: 'JSXMemberExpression'; + object: + | JSXMemberExpression + | JSXIdentifier + | JSXReferenceIdentifier + | JSXNamespacedName; + property: JSXIdentifier; }; export const jsxMemberExpression = createBuilder( - 'JSXMemberExpression', - { - bindingKeys: {}, - visitorKeys: { - object: true, - property: true, - }, - }, + 'JSXMemberExpression', + { + bindingKeys: {}, + visitorKeys: { + object: true, + property: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXNamespacedName.ts b/packages/@romejs/js-ast/jsx/JSXNamespacedName.ts index 3eb2e2faa23..fe12337d662 100644 --- a/packages/@romejs/js-ast/jsx/JSXNamespacedName.ts +++ b/packages/@romejs/js-ast/jsx/JSXNamespacedName.ts @@ -9,18 +9,18 @@ import {JSNodeBase, JSXIdentifier} from '../index'; import {createBuilder} from '../utils'; export type JSXNamespacedName = JSNodeBase & { - type: 'JSXNamespacedName'; - namespace: JSXIdentifier; - name: JSXIdentifier; + type: 'JSXNamespacedName'; + namespace: JSXIdentifier; + name: JSXIdentifier; }; export const jsxNamespacedName = createBuilder( - 'JSXNamespacedName', - { - bindingKeys: {}, - visitorKeys: { - namespace: true, - name: true, - }, - }, + 'JSXNamespacedName', + { + bindingKeys: {}, + visitorKeys: { + namespace: true, + name: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXReferenceIdentifier.ts b/packages/@romejs/js-ast/jsx/JSXReferenceIdentifier.ts index fa21c42717a..08ab95a86c5 100644 --- a/packages/@romejs/js-ast/jsx/JSXReferenceIdentifier.ts +++ b/packages/@romejs/js-ast/jsx/JSXReferenceIdentifier.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type JSXReferenceIdentifier = JSNodeBase & { - type: 'JSXReferenceIdentifier'; - name: string; + type: 'JSXReferenceIdentifier'; + name: string; }; export const jsxReferenceIdentifier = createBuilder( - 'JSXReferenceIdentifier', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'JSXReferenceIdentifier', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXSpreadAttribute.ts b/packages/@romejs/js-ast/jsx/JSXSpreadAttribute.ts index 49944643674..04059645140 100644 --- a/packages/@romejs/js-ast/jsx/JSXSpreadAttribute.ts +++ b/packages/@romejs/js-ast/jsx/JSXSpreadAttribute.ts @@ -9,16 +9,16 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type JSXSpreadAttribute = JSNodeBase & { - type: 'JSXSpreadAttribute'; - argument: AnyExpression; + type: 'JSXSpreadAttribute'; + argument: AnyExpression; }; export const jsxSpreadAttribute = createBuilder( - 'JSXSpreadAttribute', - { - bindingKeys: {}, - visitorKeys: { - argument: true, - }, - }, + 'JSXSpreadAttribute', + { + bindingKeys: {}, + visitorKeys: { + argument: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXSpreadChild.ts b/packages/@romejs/js-ast/jsx/JSXSpreadChild.ts index 2a10da1419d..1bceedfdd34 100644 --- a/packages/@romejs/js-ast/jsx/JSXSpreadChild.ts +++ b/packages/@romejs/js-ast/jsx/JSXSpreadChild.ts @@ -9,16 +9,16 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type JSXSpreadChild = JSNodeBase & { - type: 'JSXSpreadChild'; - expression: AnyExpression; + type: 'JSXSpreadChild'; + expression: AnyExpression; }; export const jsxSpreadChild = createBuilder( - 'JSXSpreadChild', - { - bindingKeys: {}, - visitorKeys: { - expression: true, - }, - }, + 'JSXSpreadChild', + { + bindingKeys: {}, + visitorKeys: { + expression: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/jsx/JSXText.ts b/packages/@romejs/js-ast/jsx/JSXText.ts index 3b392b8aeff..5360e8d67e7 100644 --- a/packages/@romejs/js-ast/jsx/JSXText.ts +++ b/packages/@romejs/js-ast/jsx/JSXText.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type JSXText = JSNodeBase & { - type: 'JSXText'; - value: string; + type: 'JSXText'; + value: string; }; export const jsxText = createBuilder( - 'JSXText', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'JSXText', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/literals/BigIntLiteral.ts b/packages/@romejs/js-ast/literals/BigIntLiteral.ts index 50d778713f4..cae8f4ae7a7 100644 --- a/packages/@romejs/js-ast/literals/BigIntLiteral.ts +++ b/packages/@romejs/js-ast/literals/BigIntLiteral.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type BigIntLiteral = JSNodeBase & { - type: 'BigIntLiteral'; - value: string; + type: 'BigIntLiteral'; + value: string; }; export const bigIntLiteral = createBuilder( - 'BigIntLiteral', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'BigIntLiteral', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/literals/BooleanLiteral.ts b/packages/@romejs/js-ast/literals/BooleanLiteral.ts index b03a26a2c4d..92f87f5dc0a 100644 --- a/packages/@romejs/js-ast/literals/BooleanLiteral.ts +++ b/packages/@romejs/js-ast/literals/BooleanLiteral.ts @@ -9,15 +9,15 @@ import {JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type BooleanLiteral = JSNodeBase & { - type: 'BooleanLiteral'; - value: boolean; + type: 'BooleanLiteral'; + value: boolean; }; export const booleanLiteral = createQuickBuilder( - 'BooleanLiteral', - 'value', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'BooleanLiteral', + 'value', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/literals/NullLiteral.ts b/packages/@romejs/js-ast/literals/NullLiteral.ts index 9bee0aaf3b3..20b64960110 100644 --- a/packages/@romejs/js-ast/literals/NullLiteral.ts +++ b/packages/@romejs/js-ast/literals/NullLiteral.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type NullLiteral = JSNodeBase & { - type: 'NullLiteral'; + type: 'NullLiteral'; }; export const nullLiteral = createBuilder( - 'NullLiteral', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'NullLiteral', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/literals/NumericLiteral.ts b/packages/@romejs/js-ast/literals/NumericLiteral.ts index 960e3d63020..fe13a2dd49a 100644 --- a/packages/@romejs/js-ast/literals/NumericLiteral.ts +++ b/packages/@romejs/js-ast/literals/NumericLiteral.ts @@ -9,16 +9,16 @@ import {JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type NumericLiteral = JSNodeBase & { - type: 'NumericLiteral'; - value: number; - format?: 'octal' | 'binary' | 'hex'; + type: 'NumericLiteral'; + value: number; + format?: 'octal' | 'binary' | 'hex'; }; export const numericLiteral = createQuickBuilder( - 'NumericLiteral', - 'value', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'NumericLiteral', + 'value', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/literals/RegExpLiteral.ts b/packages/@romejs/js-ast/literals/RegExpLiteral.ts index 8708e6f0347..b9ca714f7d6 100644 --- a/packages/@romejs/js-ast/literals/RegExpLiteral.ts +++ b/packages/@romejs/js-ast/literals/RegExpLiteral.ts @@ -9,22 +9,22 @@ import {JSNodeBase, RegExpAlternation, RegExpSubExpression} from '../index'; import {createBuilder} from '../utils'; export type RegExpLiteral = JSNodeBase & { - type: 'RegExpLiteral'; - expression: RegExpSubExpression | RegExpAlternation; - global?: boolean; - multiline?: boolean; - sticky?: boolean; - insensitive?: boolean; - noDotNewline?: boolean; - unicode?: boolean; + type: 'RegExpLiteral'; + expression: RegExpSubExpression | RegExpAlternation; + global?: boolean; + multiline?: boolean; + sticky?: boolean; + insensitive?: boolean; + noDotNewline?: boolean; + unicode?: boolean; }; export const regExpLiteral = createBuilder( - 'RegExpLiteral', - { - bindingKeys: {}, - visitorKeys: { - expression: true, - }, - }, + 'RegExpLiteral', + { + bindingKeys: {}, + visitorKeys: { + expression: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/literals/StringLiteral.ts b/packages/@romejs/js-ast/literals/StringLiteral.ts index faaa5924df7..ab39826b5b9 100644 --- a/packages/@romejs/js-ast/literals/StringLiteral.ts +++ b/packages/@romejs/js-ast/literals/StringLiteral.ts @@ -9,15 +9,15 @@ import {JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type StringLiteral = JSNodeBase & { - type: 'StringLiteral'; - value: string; + type: 'StringLiteral'; + value: string; }; export const stringLiteral = createQuickBuilder( - 'StringLiteral', - 'value', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'StringLiteral', + 'value', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/literals/TemplateLiteral.ts b/packages/@romejs/js-ast/literals/TemplateLiteral.ts index 1354c0bc4e1..b32cfc313ef 100644 --- a/packages/@romejs/js-ast/literals/TemplateLiteral.ts +++ b/packages/@romejs/js-ast/literals/TemplateLiteral.ts @@ -9,18 +9,18 @@ import {AnyExpression, JSNodeBase, TemplateElement} from '../index'; import {createBuilder} from '../utils'; export type TemplateLiteral = JSNodeBase & { - type: 'TemplateLiteral'; - quasis: Array; - expressions: Array; + type: 'TemplateLiteral'; + quasis: Array; + expressions: Array; }; export const templateLiteral = createBuilder( - 'TemplateLiteral', - { - bindingKeys: {}, - visitorKeys: { - quasis: true, - expressions: true, - }, - }, + 'TemplateLiteral', + { + bindingKeys: {}, + visitorKeys: { + quasis: true, + expressions: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ExportAllDeclaration.ts b/packages/@romejs/js-ast/modules/ExportAllDeclaration.ts index 18f56b568e6..bdcf02dc82c 100644 --- a/packages/@romejs/js-ast/modules/ExportAllDeclaration.ts +++ b/packages/@romejs/js-ast/modules/ExportAllDeclaration.ts @@ -9,18 +9,18 @@ import {ConstExportModuleKind, JSNodeBase, StringLiteral} from '../index'; import {createBuilder} from '../utils'; export type ExportAllDeclaration = JSNodeBase & { - type: 'ExportAllDeclaration'; - source: StringLiteral; - exportKind?: ConstExportModuleKind; - declare?: boolean; + type: 'ExportAllDeclaration'; + source: StringLiteral; + exportKind?: ConstExportModuleKind; + declare?: boolean; }; export const exportAllDeclaration = createBuilder( - 'ExportAllDeclaration', - { - bindingKeys: {}, - visitorKeys: { - source: true, - }, - }, + 'ExportAllDeclaration', + { + bindingKeys: {}, + visitorKeys: { + source: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ExportDefaultDeclaration.ts b/packages/@romejs/js-ast/modules/ExportDefaultDeclaration.ts index 2a1ab973edf..0b14acbcc7b 100644 --- a/packages/@romejs/js-ast/modules/ExportDefaultDeclaration.ts +++ b/packages/@romejs/js-ast/modules/ExportDefaultDeclaration.ts @@ -6,33 +6,33 @@ */ import { - AnyExpression, - ClassDeclaration, - FunctionDeclaration, - JSNodeBase, - TSInterfaceDeclaration, + AnyExpression, + ClassDeclaration, + FunctionDeclaration, + JSNodeBase, + TSInterfaceDeclaration, } from '../index'; import {createBuilder} from '../utils'; import {TSDeclareFunction} from '../typescript/TSDeclareFunction'; export type ExportDefaultDeclaration = JSNodeBase & { - type: 'ExportDefaultDeclaration'; - declaration: - | FunctionDeclaration - | ClassDeclaration - | TSInterfaceDeclaration - | TSDeclareFunction - | AnyExpression; - exportKind?: undefined; - declare?: boolean; + type: 'ExportDefaultDeclaration'; + declaration: + | FunctionDeclaration + | ClassDeclaration + | TSInterfaceDeclaration + | TSDeclareFunction + | AnyExpression; + exportKind?: undefined; + declare?: boolean; }; export const exportDefaultDeclaration = createBuilder( - 'ExportDefaultDeclaration', - { - bindingKeys: {}, - visitorKeys: { - declaration: true, - }, - }, + 'ExportDefaultDeclaration', + { + bindingKeys: {}, + visitorKeys: { + declaration: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ExportDefaultSpecifier.ts b/packages/@romejs/js-ast/modules/ExportDefaultSpecifier.ts index 25cb68ae79d..b68a14d9f29 100644 --- a/packages/@romejs/js-ast/modules/ExportDefaultSpecifier.ts +++ b/packages/@romejs/js-ast/modules/ExportDefaultSpecifier.ts @@ -9,16 +9,16 @@ import {Identifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ExportDefaultSpecifier = JSNodeBase & { - type: 'ExportDefaultSpecifier'; - exported: Identifier; + type: 'ExportDefaultSpecifier'; + exported: Identifier; }; export const exportDefaultSpecifier = createBuilder( - 'ExportDefaultSpecifier', - { - bindingKeys: {}, - visitorKeys: { - exported: true, - }, - }, + 'ExportDefaultSpecifier', + { + bindingKeys: {}, + visitorKeys: { + exported: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ExportExternalDeclaration.ts b/packages/@romejs/js-ast/modules/ExportExternalDeclaration.ts index 4dba0442890..05c8f1f3459 100644 --- a/packages/@romejs/js-ast/modules/ExportExternalDeclaration.ts +++ b/packages/@romejs/js-ast/modules/ExportExternalDeclaration.ts @@ -6,38 +6,38 @@ */ import { - ConstExportModuleKind, - ExportDefaultSpecifier, - ExportExternalSpecifier, - ExportNamespaceSpecifier, - JSNodeBase, - StringLiteral, + ConstExportModuleKind, + ExportDefaultSpecifier, + ExportExternalSpecifier, + ExportNamespaceSpecifier, + JSNodeBase, + StringLiteral, } from '../index'; import {createBuilder} from '../utils'; export type AnyExportExternalSpecifier = - | ExportNamespaceSpecifier - | ExportDefaultSpecifier - | ExportExternalSpecifier; + | ExportNamespaceSpecifier + | ExportDefaultSpecifier + | ExportExternalSpecifier; export type ExportExternalDeclaration = JSNodeBase & { - type: 'ExportExternalDeclaration'; - defaultSpecifier?: ExportDefaultSpecifier; - namespaceSpecifier?: ExportNamespaceSpecifier; - namedSpecifiers: Array; - source: StringLiteral; - exportKind?: ConstExportModuleKind; + type: 'ExportExternalDeclaration'; + defaultSpecifier?: ExportDefaultSpecifier; + namespaceSpecifier?: ExportNamespaceSpecifier; + namedSpecifiers: Array; + source: StringLiteral; + exportKind?: ConstExportModuleKind; }; export const exportExternalDeclaration = createBuilder( - 'ExportExternalDeclaration', - { - bindingKeys: {}, - visitorKeys: { - defaultSpecifier: true, - namespaceSpecifier: true, - namedSpecifiers: true, - source: true, - }, - }, + 'ExportExternalDeclaration', + { + bindingKeys: {}, + visitorKeys: { + defaultSpecifier: true, + namespaceSpecifier: true, + namedSpecifiers: true, + source: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ExportExternalSpecifier.ts b/packages/@romejs/js-ast/modules/ExportExternalSpecifier.ts index 736f2a42adb..20edb164158 100644 --- a/packages/@romejs/js-ast/modules/ExportExternalSpecifier.ts +++ b/packages/@romejs/js-ast/modules/ExportExternalSpecifier.ts @@ -9,19 +9,19 @@ import {ConstExportModuleKind, Identifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ExportExternalSpecifier = JSNodeBase & { - type: 'ExportExternalSpecifier'; - exported: Identifier; - local: Identifier; - exportKind?: ConstExportModuleKind; + type: 'ExportExternalSpecifier'; + exported: Identifier; + local: Identifier; + exportKind?: ConstExportModuleKind; }; export const exportExternalSpecifier = createBuilder( - 'ExportExternalSpecifier', - { - bindingKeys: {}, - visitorKeys: { - exported: true, - local: true, - }, - }, + 'ExportExternalSpecifier', + { + bindingKeys: {}, + visitorKeys: { + exported: true, + local: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ExportLocalDeclaration.ts b/packages/@romejs/js-ast/modules/ExportLocalDeclaration.ts index 84772c855e8..412c3d28501 100644 --- a/packages/@romejs/js-ast/modules/ExportLocalDeclaration.ts +++ b/packages/@romejs/js-ast/modules/ExportLocalDeclaration.ts @@ -6,47 +6,47 @@ */ import { - ClassDeclaration, - ConstExportModuleKind, - ExportLocalSpecifier, - FunctionDeclaration, - JSNodeBase, - TSDeclareFunction, - TSEnumDeclaration, - TSInterfaceDeclaration, - TSModuleDeclaration, - TypeAliasTypeAnnotation, - VariableDeclarationStatement, + ClassDeclaration, + ConstExportModuleKind, + ExportLocalSpecifier, + FunctionDeclaration, + JSNodeBase, + TSDeclareFunction, + TSEnumDeclaration, + TSInterfaceDeclaration, + TSModuleDeclaration, + TypeAliasTypeAnnotation, + VariableDeclarationStatement, } from '../index'; import {createBuilder} from '../utils'; export type ExportLocalDeclaration = JSNodeBase & { - type: 'ExportLocalDeclaration'; - declaration?: - | undefined - | VariableDeclarationStatement - | FunctionDeclaration - | ClassDeclaration - | TSModuleDeclaration - | TSEnumDeclaration - | TypeAliasTypeAnnotation - | TSInterfaceDeclaration - | TSDeclareFunction - | TypeAliasTypeAnnotation; - specifiers?: Array; - exportKind?: ConstExportModuleKind; - declare?: boolean; + type: 'ExportLocalDeclaration'; + declaration?: + | undefined + | VariableDeclarationStatement + | FunctionDeclaration + | ClassDeclaration + | TSModuleDeclaration + | TSEnumDeclaration + | TypeAliasTypeAnnotation + | TSInterfaceDeclaration + | TSDeclareFunction + | TypeAliasTypeAnnotation; + specifiers?: Array; + exportKind?: ConstExportModuleKind; + declare?: boolean; }; export const exportLocalDeclaration = createBuilder( - 'ExportLocalDeclaration', - { - bindingKeys: { - declaration: true, - }, - visitorKeys: { - declaration: true, - specifiers: true, - }, - }, + 'ExportLocalDeclaration', + { + bindingKeys: { + declaration: true, + }, + visitorKeys: { + declaration: true, + specifiers: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ExportLocalSpecifier.ts b/packages/@romejs/js-ast/modules/ExportLocalSpecifier.ts index de32ad29fb2..c5c8e1c79b1 100644 --- a/packages/@romejs/js-ast/modules/ExportLocalSpecifier.ts +++ b/packages/@romejs/js-ast/modules/ExportLocalSpecifier.ts @@ -6,27 +6,27 @@ */ import { - ConstExportModuleKind, - Identifier, - JSNodeBase, - ReferenceIdentifier, + ConstExportModuleKind, + Identifier, + JSNodeBase, + ReferenceIdentifier, } from '../index'; import {createBuilder} from '../utils'; export type ExportLocalSpecifier = JSNodeBase & { - type: 'ExportLocalSpecifier'; - exported: Identifier; - local: ReferenceIdentifier; - exportKind?: ConstExportModuleKind; + type: 'ExportLocalSpecifier'; + exported: Identifier; + local: ReferenceIdentifier; + exportKind?: ConstExportModuleKind; }; export const exportLocalSpecifier = createBuilder( - 'ExportLocalSpecifier', - { - bindingKeys: {}, - visitorKeys: { - local: true, - exported: true, - }, - }, + 'ExportLocalSpecifier', + { + bindingKeys: {}, + visitorKeys: { + local: true, + exported: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ExportNamespaceSpecifier.ts b/packages/@romejs/js-ast/modules/ExportNamespaceSpecifier.ts index 9878a8b4bf4..a5241765ed6 100644 --- a/packages/@romejs/js-ast/modules/ExportNamespaceSpecifier.ts +++ b/packages/@romejs/js-ast/modules/ExportNamespaceSpecifier.ts @@ -9,16 +9,16 @@ import {Identifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ExportNamespaceSpecifier = JSNodeBase & { - type: 'ExportNamespaceSpecifier'; - exported: Identifier; + type: 'ExportNamespaceSpecifier'; + exported: Identifier; }; export const exportNamespaceSpecifier = createBuilder( - 'ExportNamespaceSpecifier', - { - bindingKeys: {}, - visitorKeys: { - exported: true, - }, - }, + 'ExportNamespaceSpecifier', + { + bindingKeys: {}, + visitorKeys: { + exported: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ImportCall.ts b/packages/@romejs/js-ast/modules/ImportCall.ts index fb052052b94..312ff43f821 100644 --- a/packages/@romejs/js-ast/modules/ImportCall.ts +++ b/packages/@romejs/js-ast/modules/ImportCall.ts @@ -9,16 +9,16 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ImportCall = JSNodeBase & { - type: 'ImportCall'; - argument: AnyExpression; + type: 'ImportCall'; + argument: AnyExpression; }; export const importCall = createBuilder( - 'ImportCall', - { - bindingKeys: {}, - visitorKeys: { - argument: true, - }, - }, + 'ImportCall', + { + bindingKeys: {}, + visitorKeys: { + argument: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ImportDeclaration.ts b/packages/@romejs/js-ast/modules/ImportDeclaration.ts index b05bf7f5459..97370a53172 100644 --- a/packages/@romejs/js-ast/modules/ImportDeclaration.ts +++ b/packages/@romejs/js-ast/modules/ImportDeclaration.ts @@ -6,42 +6,42 @@ */ import { - ConstImportModuleKind, - ImportDefaultSpecifier, - ImportNamespaceSpecifier, - ImportSpecifier, - JSNodeBase, - StringLiteral, + ConstImportModuleKind, + ImportDefaultSpecifier, + ImportNamespaceSpecifier, + ImportSpecifier, + JSNodeBase, + StringLiteral, } from '../index'; import {createBuilder} from '../utils'; export type AnyImportSpecifier = - | ImportDefaultSpecifier - | ImportNamespaceSpecifier - | ImportSpecifier; + | ImportDefaultSpecifier + | ImportNamespaceSpecifier + | ImportSpecifier; export type ImportDeclaration = JSNodeBase & { - type: 'ImportDeclaration'; - defaultSpecifier?: ImportDefaultSpecifier; - namespaceSpecifier?: ImportNamespaceSpecifier; - namedSpecifiers: Array; - source: StringLiteral; - importKind?: ConstImportModuleKind; + type: 'ImportDeclaration'; + defaultSpecifier?: ImportDefaultSpecifier; + namespaceSpecifier?: ImportNamespaceSpecifier; + namedSpecifiers: Array; + source: StringLiteral; + importKind?: ConstImportModuleKind; }; export const importDeclaration = createBuilder( - 'ImportDeclaration', - { - bindingKeys: { - defaultSpecifier: true, - namespaceSpecifier: true, - namedSpecifiers: true, - }, - visitorKeys: { - defaultSpecifier: true, - namespaceSpecifier: true, - namedSpecifiers: true, - source: true, - }, - }, + 'ImportDeclaration', + { + bindingKeys: { + defaultSpecifier: true, + namespaceSpecifier: true, + namedSpecifiers: true, + }, + visitorKeys: { + defaultSpecifier: true, + namespaceSpecifier: true, + namedSpecifiers: true, + source: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ImportDefaultSpecifier.ts b/packages/@romejs/js-ast/modules/ImportDefaultSpecifier.ts index 3c8ade7f2ce..038f98c358b 100644 --- a/packages/@romejs/js-ast/modules/ImportDefaultSpecifier.ts +++ b/packages/@romejs/js-ast/modules/ImportDefaultSpecifier.ts @@ -9,18 +9,18 @@ import {ImportSpecifierLocal, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ImportDefaultSpecifier = JSNodeBase & { - type: 'ImportDefaultSpecifier'; - local: ImportSpecifierLocal; + type: 'ImportDefaultSpecifier'; + local: ImportSpecifierLocal; }; export const importDefaultSpecifier = createBuilder( - 'ImportDefaultSpecifier', - { - bindingKeys: { - local: true, - }, - visitorKeys: { - local: true, - }, - }, + 'ImportDefaultSpecifier', + { + bindingKeys: { + local: true, + }, + visitorKeys: { + local: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ImportNamespaceSpecifier.ts b/packages/@romejs/js-ast/modules/ImportNamespaceSpecifier.ts index 7add89b66af..01ab30b26d7 100644 --- a/packages/@romejs/js-ast/modules/ImportNamespaceSpecifier.ts +++ b/packages/@romejs/js-ast/modules/ImportNamespaceSpecifier.ts @@ -9,18 +9,18 @@ import {ImportSpecifierLocal, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ImportNamespaceSpecifier = JSNodeBase & { - type: 'ImportNamespaceSpecifier'; - local: ImportSpecifierLocal; + type: 'ImportNamespaceSpecifier'; + local: ImportSpecifierLocal; }; export const importNamespaceSpecifier = createBuilder( - 'ImportNamespaceSpecifier', - { - bindingKeys: { - local: true, - }, - visitorKeys: { - local: true, - }, - }, + 'ImportNamespaceSpecifier', + { + bindingKeys: { + local: true, + }, + visitorKeys: { + local: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ImportSpecifier.ts b/packages/@romejs/js-ast/modules/ImportSpecifier.ts index 07a8ef67fc3..124f76d78bc 100644 --- a/packages/@romejs/js-ast/modules/ImportSpecifier.ts +++ b/packages/@romejs/js-ast/modules/ImportSpecifier.ts @@ -9,20 +9,20 @@ import {Identifier, ImportSpecifierLocal, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ImportSpecifier = JSNodeBase & { - type: 'ImportSpecifier'; - imported: Identifier; - local: ImportSpecifierLocal; + type: 'ImportSpecifier'; + imported: Identifier; + local: ImportSpecifierLocal; }; export const importSpecifier = createBuilder( - 'ImportSpecifier', - { - bindingKeys: { - local: true, - }, - visitorKeys: { - imported: true, - local: true, - }, - }, + 'ImportSpecifier', + { + bindingKeys: { + local: true, + }, + visitorKeys: { + imported: true, + local: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/modules/ImportSpecifierLocal.ts b/packages/@romejs/js-ast/modules/ImportSpecifierLocal.ts index 13234f02a50..aeeb0343e9a 100644 --- a/packages/@romejs/js-ast/modules/ImportSpecifierLocal.ts +++ b/packages/@romejs/js-ast/modules/ImportSpecifierLocal.ts @@ -9,22 +9,22 @@ import {BindingIdentifier, JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type ImportSpecifierLocal = JSNodeBase & { - type: 'ImportSpecifierLocal'; - name: BindingIdentifier; + type: 'ImportSpecifierLocal'; + name: BindingIdentifier; }; export const importSpecifierLocal = createQuickBuilder< - ImportSpecifierLocal, - 'name' + ImportSpecifierLocal, + 'name' >( - 'ImportSpecifierLocal', - 'name', - { - bindingKeys: { - name: true, - }, - visitorKeys: { - name: true, - }, - }, + 'ImportSpecifierLocal', + 'name', + { + bindingKeys: { + name: true, + }, + visitorKeys: { + name: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/objects/ComputedPropertyKey.ts b/packages/@romejs/js-ast/objects/ComputedPropertyKey.ts index 0f2bc2aeee0..0772b3862eb 100644 --- a/packages/@romejs/js-ast/objects/ComputedPropertyKey.ts +++ b/packages/@romejs/js-ast/objects/ComputedPropertyKey.ts @@ -9,20 +9,20 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type ComputedPropertyKey = JSNodeBase & { - type: 'ComputedPropertyKey'; - value: AnyExpression; + type: 'ComputedPropertyKey'; + value: AnyExpression; }; export const computedPropertyKey = createQuickBuilder< - ComputedPropertyKey, - 'value' + ComputedPropertyKey, + 'value' >( - 'ComputedPropertyKey', - 'value', - { - bindingKeys: {}, - visitorKeys: { - value: true, - }, - }, + 'ComputedPropertyKey', + 'value', + { + bindingKeys: {}, + visitorKeys: { + value: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/objects/ObjectExpression.ts b/packages/@romejs/js-ast/objects/ObjectExpression.ts index 23b61f99426..d3649615873 100644 --- a/packages/@romejs/js-ast/objects/ObjectExpression.ts +++ b/packages/@romejs/js-ast/objects/ObjectExpression.ts @@ -9,20 +9,20 @@ import {JSNodeBase, ObjectProperties} from '../index'; import {createQuickBuilder} from '../utils'; export type ObjectExpression = JSNodeBase & { - type: 'ObjectExpression'; - properties: ObjectProperties; + type: 'ObjectExpression'; + properties: ObjectProperties; }; export const objectExpression = createQuickBuilder< - ObjectExpression, - 'properties' + ObjectExpression, + 'properties' >( - 'ObjectExpression', - 'properties', - { - bindingKeys: {}, - visitorKeys: { - properties: true, - }, - }, + 'ObjectExpression', + 'properties', + { + bindingKeys: {}, + visitorKeys: { + properties: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/objects/ObjectMethod.ts b/packages/@romejs/js-ast/objects/ObjectMethod.ts index 3e73fdd315b..4dc8000f0a2 100644 --- a/packages/@romejs/js-ast/objects/ObjectMethod.ts +++ b/packages/@romejs/js-ast/objects/ObjectMethod.ts @@ -6,32 +6,32 @@ */ import { - BlockStatement, - ComputedPropertyKey, - FunctionHead, - JSNodeBase, - StaticPropertyKey, + BlockStatement, + ComputedPropertyKey, + FunctionHead, + JSNodeBase, + StaticPropertyKey, } from '../index'; import {createBuilder} from '../utils'; export type ObjectMethodKind = 'get' | 'set' | 'method'; export type ObjectMethod = JSNodeBase & { - key: ComputedPropertyKey | StaticPropertyKey; - type: 'ObjectMethod'; - kind: ObjectMethodKind; - head: FunctionHead; - body: BlockStatement; + key: ComputedPropertyKey | StaticPropertyKey; + type: 'ObjectMethod'; + kind: ObjectMethodKind; + head: FunctionHead; + body: BlockStatement; }; export const objectMethod = createBuilder( - 'ObjectMethod', - { - bindingKeys: {}, - visitorKeys: { - key: true, - head: true, - body: true, - }, - }, + 'ObjectMethod', + { + bindingKeys: {}, + visitorKeys: { + key: true, + head: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/objects/ObjectProperty.ts b/packages/@romejs/js-ast/objects/ObjectProperty.ts index d0793996933..c497d61c8af 100644 --- a/packages/@romejs/js-ast/objects/ObjectProperty.ts +++ b/packages/@romejs/js-ast/objects/ObjectProperty.ts @@ -6,26 +6,26 @@ */ import { - AnyExpression, - ComputedPropertyKey, - JSNodeBase, - StaticPropertyKey, + AnyExpression, + ComputedPropertyKey, + JSNodeBase, + StaticPropertyKey, } from '../index'; import {createBuilder} from '../utils'; export type ObjectProperty = JSNodeBase & { - type: 'ObjectProperty'; - key: StaticPropertyKey | ComputedPropertyKey; - value: AnyExpression; + type: 'ObjectProperty'; + key: StaticPropertyKey | ComputedPropertyKey; + value: AnyExpression; }; export const objectProperty = createBuilder( - 'ObjectProperty', - { - bindingKeys: {}, - visitorKeys: { - key: true, - value: true, - }, - }, + 'ObjectProperty', + { + bindingKeys: {}, + visitorKeys: { + key: true, + value: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/objects/SpreadProperty.ts b/packages/@romejs/js-ast/objects/SpreadProperty.ts index 34b09340658..a1d8bb85ae2 100644 --- a/packages/@romejs/js-ast/objects/SpreadProperty.ts +++ b/packages/@romejs/js-ast/objects/SpreadProperty.ts @@ -9,16 +9,16 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type SpreadProperty = JSNodeBase & { - type: 'SpreadProperty'; - argument: AnyExpression; + type: 'SpreadProperty'; + argument: AnyExpression; }; export const spreadProperty = createBuilder( - 'SpreadProperty', - { - bindingKeys: {}, - visitorKeys: { - argument: true, - }, - }, + 'SpreadProperty', + { + bindingKeys: {}, + visitorKeys: { + argument: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/objects/StaticPropertyKey.ts b/packages/@romejs/js-ast/objects/StaticPropertyKey.ts index e591bb8f429..5e584d40702 100644 --- a/packages/@romejs/js-ast/objects/StaticPropertyKey.ts +++ b/packages/@romejs/js-ast/objects/StaticPropertyKey.ts @@ -6,26 +6,26 @@ */ import { - Identifier, - JSNodeBase, - NumericLiteral, - PrivateName, - StringLiteral, + Identifier, + JSNodeBase, + NumericLiteral, + PrivateName, + StringLiteral, } from '../index'; import {createQuickBuilder} from '../utils'; export type StaticPropertyKey = JSNodeBase & { - type: 'StaticPropertyKey'; - value: Identifier | PrivateName | StringLiteral | NumericLiteral; + type: 'StaticPropertyKey'; + value: Identifier | PrivateName | StringLiteral | NumericLiteral; }; export const staticPropertyKey = createQuickBuilder( - 'StaticPropertyKey', - 'value', - { - bindingKeys: {}, - visitorKeys: { - value: true, - }, - }, + 'StaticPropertyKey', + 'value', + { + bindingKeys: {}, + visitorKeys: { + value: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/patterns/AssignmentArrayPattern.ts b/packages/@romejs/js-ast/patterns/AssignmentArrayPattern.ts index 3e550d72a84..8c6ba3b8107 100644 --- a/packages/@romejs/js-ast/patterns/AssignmentArrayPattern.ts +++ b/packages/@romejs/js-ast/patterns/AssignmentArrayPattern.ts @@ -4,29 +4,29 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import { - AnyAssignmentPattern, - AnyTargetAssignmentPattern, - ArrayHole, - JSNodeBase, - PatternMeta, + AnyAssignmentPattern, + AnyTargetAssignmentPattern, + ArrayHole, + JSNodeBase, + PatternMeta, } from '../index'; import {createBuilder} from '../utils'; export type AssignmentArrayPattern = JSNodeBase & { - type: 'AssignmentArrayPattern'; - meta?: PatternMeta; - elements: Array; - rest?: AnyTargetAssignmentPattern; + type: 'AssignmentArrayPattern'; + meta?: PatternMeta; + elements: Array; + rest?: AnyTargetAssignmentPattern; }; export const assignmentArrayPattern = createBuilder( - 'AssignmentArrayPattern', - { - bindingKeys: {}, - visitorKeys: { - elements: true, - rest: true, - meta: true, - }, - }, + 'AssignmentArrayPattern', + { + bindingKeys: {}, + visitorKeys: { + elements: true, + rest: true, + meta: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/patterns/AssignmentAssignmentPattern.ts b/packages/@romejs/js-ast/patterns/AssignmentAssignmentPattern.ts index c1491127baf..94175513b72 100644 --- a/packages/@romejs/js-ast/patterns/AssignmentAssignmentPattern.ts +++ b/packages/@romejs/js-ast/patterns/AssignmentAssignmentPattern.ts @@ -6,28 +6,28 @@ */ import { - AnyExpression, - AnyTargetAssignmentPattern, - JSNodeBase, - PatternMeta, + AnyExpression, + AnyTargetAssignmentPattern, + JSNodeBase, + PatternMeta, } from '../index'; import {createBuilder} from '../utils'; export type AssignmentAssignmentPattern = JSNodeBase & { - type: 'AssignmentAssignmentPattern'; - left: AnyTargetAssignmentPattern; - right: AnyExpression; - meta?: PatternMeta; + type: 'AssignmentAssignmentPattern'; + left: AnyTargetAssignmentPattern; + right: AnyExpression; + meta?: PatternMeta; }; export const assignmentAssignmentPattern = createBuilder( - 'AssignmentAssignmentPattern', - { - bindingKeys: {}, - visitorKeys: { - left: true, - right: true, - meta: true, - }, - }, + 'AssignmentAssignmentPattern', + { + bindingKeys: {}, + visitorKeys: { + left: true, + right: true, + meta: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/patterns/AssignmentIdentifier.ts b/packages/@romejs/js-ast/patterns/AssignmentIdentifier.ts index c76d57bfc94..974fe133b8d 100644 --- a/packages/@romejs/js-ast/patterns/AssignmentIdentifier.ts +++ b/packages/@romejs/js-ast/patterns/AssignmentIdentifier.ts @@ -9,19 +9,19 @@ import {JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type AssignmentIdentifier = JSNodeBase & { - type: 'AssignmentIdentifier'; - name: string; - definite?: boolean; + type: 'AssignmentIdentifier'; + name: string; + definite?: boolean; }; export const assignmentIdentifier = createQuickBuilder< - AssignmentIdentifier, - 'name' + AssignmentIdentifier, + 'name' >( - 'AssignmentIdentifier', - 'name', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'AssignmentIdentifier', + 'name', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/patterns/AssignmentObjectPattern.ts b/packages/@romejs/js-ast/patterns/AssignmentObjectPattern.ts index f417b788045..d81ebf731c1 100644 --- a/packages/@romejs/js-ast/patterns/AssignmentObjectPattern.ts +++ b/packages/@romejs/js-ast/patterns/AssignmentObjectPattern.ts @@ -6,28 +6,28 @@ */ import { - AssignmentIdentifier, - AssignmentObjectPatternProperty, - JSNodeBase, - PatternMeta, + AssignmentIdentifier, + AssignmentObjectPatternProperty, + JSNodeBase, + PatternMeta, } from '../index'; import {createBuilder} from '../utils'; export type AssignmentObjectPattern = JSNodeBase & { - type: 'AssignmentObjectPattern'; - meta?: PatternMeta; - properties: Array; - rest: undefined | AssignmentIdentifier; + type: 'AssignmentObjectPattern'; + meta?: PatternMeta; + properties: Array; + rest: undefined | AssignmentIdentifier; }; export const assignmentObjectPattern = createBuilder( - 'AssignmentObjectPattern', - { - bindingKeys: {}, - visitorKeys: { - properties: true, - rest: true, - meta: true, - }, - }, + 'AssignmentObjectPattern', + { + bindingKeys: {}, + visitorKeys: { + properties: true, + rest: true, + meta: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/patterns/AssignmentObjectPatternProperty.ts b/packages/@romejs/js-ast/patterns/AssignmentObjectPatternProperty.ts index 0bca757c402..ce031035693 100644 --- a/packages/@romejs/js-ast/patterns/AssignmentObjectPatternProperty.ts +++ b/packages/@romejs/js-ast/patterns/AssignmentObjectPatternProperty.ts @@ -9,18 +9,18 @@ import {AnyAssignmentPattern, AnyObjectPropertyKey, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type AssignmentObjectPatternProperty = JSNodeBase & { - type: 'AssignmentObjectPatternProperty'; - key: AnyObjectPropertyKey; - value: AnyAssignmentPattern; + type: 'AssignmentObjectPatternProperty'; + key: AnyObjectPropertyKey; + value: AnyAssignmentPattern; }; export const assignmentObjectPatternProperty = createBuilder( - 'AssignmentObjectPatternProperty', - { - bindingKeys: {}, - visitorKeys: { - key: true, - value: true, - }, - }, + 'AssignmentObjectPatternProperty', + { + bindingKeys: {}, + visitorKeys: { + key: true, + value: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/patterns/BindingArrayPattern.ts b/packages/@romejs/js-ast/patterns/BindingArrayPattern.ts index 098ea4728ce..ce7d3d260e6 100644 --- a/packages/@romejs/js-ast/patterns/BindingArrayPattern.ts +++ b/packages/@romejs/js-ast/patterns/BindingArrayPattern.ts @@ -4,32 +4,32 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import { - AnyParamBindingPattern, - AnyTargetBindingPattern, - ArrayHole, - JSNodeBase, - PatternMeta, + AnyParamBindingPattern, + AnyTargetBindingPattern, + ArrayHole, + JSNodeBase, + PatternMeta, } from '../index'; import {createBuilder} from '../utils'; export type BindingArrayPattern = JSNodeBase & { - type: 'BindingArrayPattern'; - meta?: PatternMeta; - elements: Array; - rest: undefined | AnyTargetBindingPattern; + type: 'BindingArrayPattern'; + meta?: PatternMeta; + elements: Array; + rest: undefined | AnyTargetBindingPattern; }; export const bindingArrayPattern = createBuilder( - 'BindingArrayPattern', - { - bindingKeys: { - elements: true, - rest: true, - }, - visitorKeys: { - elements: true, - rest: true, - meta: true, - }, - }, + 'BindingArrayPattern', + { + bindingKeys: { + elements: true, + rest: true, + }, + visitorKeys: { + elements: true, + rest: true, + meta: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/patterns/BindingAssignmentPattern.ts b/packages/@romejs/js-ast/patterns/BindingAssignmentPattern.ts index 13c188bffbf..7b04d42e00f 100644 --- a/packages/@romejs/js-ast/patterns/BindingAssignmentPattern.ts +++ b/packages/@romejs/js-ast/patterns/BindingAssignmentPattern.ts @@ -6,30 +6,30 @@ */ import { - AnyExpression, - AnyTargetBindingPattern, - JSNodeBase, - PatternMeta, + AnyExpression, + AnyTargetBindingPattern, + JSNodeBase, + PatternMeta, } from '../index'; import {createBuilder} from '../utils'; export type BindingAssignmentPattern = JSNodeBase & { - type: 'BindingAssignmentPattern'; - left: AnyTargetBindingPattern; - right: AnyExpression; - meta?: PatternMeta; + type: 'BindingAssignmentPattern'; + left: AnyTargetBindingPattern; + right: AnyExpression; + meta?: PatternMeta; }; export const bindingAssignmentPattern = createBuilder( - 'BindingAssignmentPattern', - { - bindingKeys: { - left: true, - }, - visitorKeys: { - left: true, - right: true, - meta: true, - }, - }, + 'BindingAssignmentPattern', + { + bindingKeys: { + left: true, + }, + visitorKeys: { + left: true, + right: true, + meta: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/patterns/BindingIdentifier.ts b/packages/@romejs/js-ast/patterns/BindingIdentifier.ts index 0b8197678aa..517b5928f80 100644 --- a/packages/@romejs/js-ast/patterns/BindingIdentifier.ts +++ b/packages/@romejs/js-ast/patterns/BindingIdentifier.ts @@ -9,19 +9,19 @@ import {JSNodeBase, PatternMeta} from '../index'; import {createQuickBuilder} from '../utils'; export type BindingIdentifier = JSNodeBase & { - type: 'BindingIdentifier'; - name: string; - definite?: boolean; - meta?: PatternMeta; + type: 'BindingIdentifier'; + name: string; + definite?: boolean; + meta?: PatternMeta; }; export const bindingIdentifier = createQuickBuilder( - 'BindingIdentifier', - 'name', - { - bindingKeys: {}, - visitorKeys: { - meta: true, - }, - }, + 'BindingIdentifier', + 'name', + { + bindingKeys: {}, + visitorKeys: { + meta: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/patterns/BindingObjectPattern.ts b/packages/@romejs/js-ast/patterns/BindingObjectPattern.ts index a916221ebe1..a77a181c850 100644 --- a/packages/@romejs/js-ast/patterns/BindingObjectPattern.ts +++ b/packages/@romejs/js-ast/patterns/BindingObjectPattern.ts @@ -6,31 +6,31 @@ */ import { - BindingIdentifier, - BindingObjectPatternProperty, - JSNodeBase, - PatternMeta, + BindingIdentifier, + BindingObjectPatternProperty, + JSNodeBase, + PatternMeta, } from '../index'; import {createBuilder} from '../utils'; export type BindingObjectPattern = JSNodeBase & { - meta?: PatternMeta; - type: 'BindingObjectPattern'; - properties: Array; - rest: undefined | BindingIdentifier; + meta?: PatternMeta; + type: 'BindingObjectPattern'; + properties: Array; + rest: undefined | BindingIdentifier; }; export const bindingObjectPattern = createBuilder( - 'BindingObjectPattern', - { - bindingKeys: { - properties: true, - rest: true, - }, - visitorKeys: { - properties: true, - rest: true, - meta: true, - }, - }, + 'BindingObjectPattern', + { + bindingKeys: { + properties: true, + rest: true, + }, + visitorKeys: { + properties: true, + rest: true, + meta: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/patterns/BindingObjectPatternProperty.ts b/packages/@romejs/js-ast/patterns/BindingObjectPatternProperty.ts index 5611b7e5cc6..c170b9a6dec 100644 --- a/packages/@romejs/js-ast/patterns/BindingObjectPatternProperty.ts +++ b/packages/@romejs/js-ast/patterns/BindingObjectPatternProperty.ts @@ -9,21 +9,21 @@ import {AnyBindingPattern, AnyObjectPropertyKey, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type BindingObjectPatternProperty = JSNodeBase & { - type: 'BindingObjectPatternProperty'; - key: AnyObjectPropertyKey; - value: AnyBindingPattern; - meta?: undefined; + type: 'BindingObjectPatternProperty'; + key: AnyObjectPropertyKey; + value: AnyBindingPattern; + meta?: undefined; }; export const bindingObjectPatternProperty = createBuilder( - 'BindingObjectPatternProperty', - { - bindingKeys: { - value: true, - }, - visitorKeys: { - key: true, - value: true, - }, - }, + 'BindingObjectPatternProperty', + { + bindingKeys: { + value: true, + }, + visitorKeys: { + key: true, + value: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/patterns/PatternMeta.ts b/packages/@romejs/js-ast/patterns/PatternMeta.ts index e1184ca7596..156e86e728f 100644 --- a/packages/@romejs/js-ast/patterns/PatternMeta.ts +++ b/packages/@romejs/js-ast/patterns/PatternMeta.ts @@ -9,21 +9,21 @@ import {AnyPrimaryType, JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type PatternMeta = JSNodeBase & { - type: 'PatternMeta'; - typeAnnotation?: AnyPrimaryType; - optional?: boolean; - accessibility?: string; - definite?: boolean; - readonly?: boolean; + type: 'PatternMeta'; + typeAnnotation?: AnyPrimaryType; + optional?: boolean; + accessibility?: string; + definite?: boolean; + readonly?: boolean; }; export const patternMeta = createQuickBuilder( - 'PatternMeta', - 'typeAnnotation', - { - bindingKeys: {}, - visitorKeys: { - typeAnnotation: true, - }, - }, + 'PatternMeta', + 'typeAnnotation', + { + bindingKeys: {}, + visitorKeys: { + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpAlternation.ts b/packages/@romejs/js-ast/regex/RegExpAlternation.ts index 7377fa3bd1d..c650fc98fb4 100644 --- a/packages/@romejs/js-ast/regex/RegExpAlternation.ts +++ b/packages/@romejs/js-ast/regex/RegExpAlternation.ts @@ -9,18 +9,18 @@ import {AnyRegExpExpression, JSNodeBase, RegExpSubExpression} from '../index'; import {createBuilder} from '../utils'; export type RegExpAlternation = JSNodeBase & { - type: 'RegExpAlternation'; - left: AnyRegExpExpression; - right: RegExpSubExpression; + type: 'RegExpAlternation'; + left: AnyRegExpExpression; + right: RegExpSubExpression; }; export const regExpAlternation = createBuilder( - 'RegExpAlternation', - { - bindingKeys: {}, - visitorKeys: { - left: true, - right: true, - }, - }, + 'RegExpAlternation', + { + bindingKeys: {}, + visitorKeys: { + left: true, + right: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpAnyCharacter.ts b/packages/@romejs/js-ast/regex/RegExpAnyCharacter.ts index cd29ccd2707..c594ffda9c3 100644 --- a/packages/@romejs/js-ast/regex/RegExpAnyCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpAnyCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpAnyCharacter = JSNodeBase & { - type: 'RegExpAnyCharacter'; + type: 'RegExpAnyCharacter'; }; export const regExpAnyCharacter = createBuilder( - 'RegExpAnyCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpAnyCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpCharSet.ts b/packages/@romejs/js-ast/regex/RegExpCharSet.ts index 73919271743..3ea2fc7baba 100644 --- a/packages/@romejs/js-ast/regex/RegExpCharSet.ts +++ b/packages/@romejs/js-ast/regex/RegExpCharSet.ts @@ -6,24 +6,24 @@ */ import { - AnyRegExpEscapedCharacter, - JSNodeBase, - RegExpCharSetRange, + AnyRegExpEscapedCharacter, + JSNodeBase, + RegExpCharSetRange, } from '../index'; import {createBuilder} from '../utils'; export type RegExpCharSet = JSNodeBase & { - type: 'RegExpCharSet'; - invert?: boolean; - body: Array; + type: 'RegExpCharSet'; + invert?: boolean; + body: Array; }; export const regExpCharSet = createBuilder( - 'RegExpCharSet', - { - bindingKeys: {}, - visitorKeys: { - body: true, - }, - }, + 'RegExpCharSet', + { + bindingKeys: {}, + visitorKeys: { + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpCharSetRange.ts b/packages/@romejs/js-ast/regex/RegExpCharSetRange.ts index cf71b3bac42..fb79fa489f0 100644 --- a/packages/@romejs/js-ast/regex/RegExpCharSetRange.ts +++ b/packages/@romejs/js-ast/regex/RegExpCharSetRange.ts @@ -9,18 +9,18 @@ import {AnyRegExpEscapedCharacter, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpCharSetRange = JSNodeBase & { - type: 'RegExpCharSetRange'; - start: AnyRegExpEscapedCharacter; - end: AnyRegExpEscapedCharacter; + type: 'RegExpCharSetRange'; + start: AnyRegExpEscapedCharacter; + end: AnyRegExpEscapedCharacter; }; export const regExpCharSetRange = createBuilder( - 'RegExpCharSetRange', - { - bindingKeys: {}, - visitorKeys: { - start: true, - end: true, - }, - }, + 'RegExpCharSetRange', + { + bindingKeys: {}, + visitorKeys: { + start: true, + end: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpCharacter.ts b/packages/@romejs/js-ast/regex/RegExpCharacter.ts index 33a7e5a4eaa..2e6d7583580 100644 --- a/packages/@romejs/js-ast/regex/RegExpCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpCharacter.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpCharacter = JSNodeBase & { - type: 'RegExpCharacter'; - value: string; + type: 'RegExpCharacter'; + value: string; }; export const regExpCharacter = createBuilder( - 'RegExpCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpControlCharacter.ts b/packages/@romejs/js-ast/regex/RegExpControlCharacter.ts index bfba158554d..5bb8ba48eb6 100644 --- a/packages/@romejs/js-ast/regex/RegExpControlCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpControlCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpControlCharacter = JSNodeBase & { - type: 'RegExpControlCharacter'; + type: 'RegExpControlCharacter'; }; export const regExpControlCharacter = createBuilder( - 'RegExpControlCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpControlCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpDigitCharacter.ts b/packages/@romejs/js-ast/regex/RegExpDigitCharacter.ts index 85fbcd0a4ac..0e7645cbdaa 100644 --- a/packages/@romejs/js-ast/regex/RegExpDigitCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpDigitCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpDigitCharacter = JSNodeBase & { - type: 'RegExpDigitCharacter'; + type: 'RegExpDigitCharacter'; }; export const regExpDigitCharacter = createBuilder( - 'RegExpDigitCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpDigitCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpEndCharacter.ts b/packages/@romejs/js-ast/regex/RegExpEndCharacter.ts index d9a625bf300..fab1b7aa869 100644 --- a/packages/@romejs/js-ast/regex/RegExpEndCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpEndCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpEndCharacter = JSNodeBase & { - type: 'RegExpEndCharacter'; + type: 'RegExpEndCharacter'; }; export const regExpEndCharacter = createBuilder( - 'RegExpEndCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpEndCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpGroupCapture.ts b/packages/@romejs/js-ast/regex/RegExpGroupCapture.ts index 72e1c02ceb0..7a297445796 100644 --- a/packages/@romejs/js-ast/regex/RegExpGroupCapture.ts +++ b/packages/@romejs/js-ast/regex/RegExpGroupCapture.ts @@ -9,17 +9,17 @@ import {AnyRegExpExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpGroupCapture = JSNodeBase & { - type: 'RegExpGroupCapture'; - expression: AnyRegExpExpression; - name?: string; + type: 'RegExpGroupCapture'; + expression: AnyRegExpExpression; + name?: string; }; export const regExpGroupCapture = createBuilder( - 'RegExpGroupCapture', - { - bindingKeys: {}, - visitorKeys: { - expression: true, - }, - }, + 'RegExpGroupCapture', + { + bindingKeys: {}, + visitorKeys: { + expression: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpGroupNonCapture.ts b/packages/@romejs/js-ast/regex/RegExpGroupNonCapture.ts index 9e34e95afd2..9ab305bd3ac 100644 --- a/packages/@romejs/js-ast/regex/RegExpGroupNonCapture.ts +++ b/packages/@romejs/js-ast/regex/RegExpGroupNonCapture.ts @@ -9,21 +9,21 @@ import {AnyRegExpExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpGroupNonCapture = JSNodeBase & { - type: 'RegExpGroupNonCapture'; - expression: AnyRegExpExpression; - kind?: - | 'positive-lookahead' - | 'negative-lookahead' - | 'positive-lookbehind' - | 'negative-lookbehind'; + type: 'RegExpGroupNonCapture'; + expression: AnyRegExpExpression; + kind?: + | 'positive-lookahead' + | 'negative-lookahead' + | 'positive-lookbehind' + | 'negative-lookbehind'; }; export const regExpGroupNonCapture = createBuilder( - 'RegExpGroupNonCapture', - { - bindingKeys: {}, - visitorKeys: { - expression: true, - }, - }, + 'RegExpGroupNonCapture', + { + bindingKeys: {}, + visitorKeys: { + expression: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpNamedBackReference.ts b/packages/@romejs/js-ast/regex/RegExpNamedBackReference.ts index 17e5a0f95c1..4cba7bca60a 100644 --- a/packages/@romejs/js-ast/regex/RegExpNamedBackReference.ts +++ b/packages/@romejs/js-ast/regex/RegExpNamedBackReference.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpNamedBackReference = JSNodeBase & { - type: 'RegExpNamedBackReference'; - name: string; + type: 'RegExpNamedBackReference'; + name: string; }; export const regExpNamedBackReference = createBuilder( - 'RegExpNamedBackReference', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpNamedBackReference', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpNonDigitCharacter.ts b/packages/@romejs/js-ast/regex/RegExpNonDigitCharacter.ts index 0120b7cf293..c28971dc2f9 100644 --- a/packages/@romejs/js-ast/regex/RegExpNonDigitCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpNonDigitCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpNonDigitCharacter = JSNodeBase & { - type: 'RegExpNonDigitCharacter'; + type: 'RegExpNonDigitCharacter'; }; export const regExpNonDigitCharacter = createBuilder( - 'RegExpNonDigitCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpNonDigitCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpNonWhiteSpaceCharacter.ts b/packages/@romejs/js-ast/regex/RegExpNonWhiteSpaceCharacter.ts index 047d252ffd3..a1d5b64ef51 100644 --- a/packages/@romejs/js-ast/regex/RegExpNonWhiteSpaceCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpNonWhiteSpaceCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpNonWhiteSpaceCharacter = JSNodeBase & { - type: 'RegExpNonWhiteSpaceCharacter'; + type: 'RegExpNonWhiteSpaceCharacter'; }; export const regExpNonWhiteSpaceCharacter = createBuilder( - 'RegExpNonWhiteSpaceCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpNonWhiteSpaceCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpNonWordBoundaryCharacter.ts b/packages/@romejs/js-ast/regex/RegExpNonWordBoundaryCharacter.ts index 5c1040521d5..a41de4f1593 100644 --- a/packages/@romejs/js-ast/regex/RegExpNonWordBoundaryCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpNonWordBoundaryCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpNonWordBoundaryCharacter = JSNodeBase & { - type: 'RegExpNonWordBoundaryCharacter'; + type: 'RegExpNonWordBoundaryCharacter'; }; export const regExpNonWordBoundaryCharacter = createBuilder( - 'RegExpNonWordBoundaryCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpNonWordBoundaryCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpNonWordCharacter.ts b/packages/@romejs/js-ast/regex/RegExpNonWordCharacter.ts index 8013fba0582..3410f7f26b0 100644 --- a/packages/@romejs/js-ast/regex/RegExpNonWordCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpNonWordCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpNonWordCharacter = JSNodeBase & { - type: 'RegExpNonWordCharacter'; + type: 'RegExpNonWordCharacter'; }; export const regExpNonWordCharacter = createBuilder( - 'RegExpNonWordCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpNonWordCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpNumericBackReference.ts b/packages/@romejs/js-ast/regex/RegExpNumericBackReference.ts index 4ba873f1169..cdf41735a2e 100644 --- a/packages/@romejs/js-ast/regex/RegExpNumericBackReference.ts +++ b/packages/@romejs/js-ast/regex/RegExpNumericBackReference.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpNumericBackReference = JSNodeBase & { - type: 'RegExpNumericBackReference'; - value: number; + type: 'RegExpNumericBackReference'; + value: number; }; export const regExpNumericBackReference = createBuilder( - 'RegExpNumericBackReference', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpNumericBackReference', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpQuantified.ts b/packages/@romejs/js-ast/regex/RegExpQuantified.ts index a8ee08ccbd2..b05f35e3544 100644 --- a/packages/@romejs/js-ast/regex/RegExpQuantified.ts +++ b/packages/@romejs/js-ast/regex/RegExpQuantified.ts @@ -9,19 +9,19 @@ import {AnyRegExpBodyItem, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpQuantified = JSNodeBase & { - type: 'RegExpQuantified'; - target: AnyRegExpBodyItem; - lazy?: boolean; - min: number; - max?: number; + type: 'RegExpQuantified'; + target: AnyRegExpBodyItem; + lazy?: boolean; + min: number; + max?: number; }; export const regExpQuantified = createBuilder( - 'RegExpQuantified', - { - bindingKeys: {}, - visitorKeys: { - target: true, - }, - }, + 'RegExpQuantified', + { + bindingKeys: {}, + visitorKeys: { + target: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpStartCharacter.ts b/packages/@romejs/js-ast/regex/RegExpStartCharacter.ts index 90d3738e0bf..2f90e056456 100644 --- a/packages/@romejs/js-ast/regex/RegExpStartCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpStartCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpStartCharacter = JSNodeBase & { - type: 'RegExpStartCharacter'; + type: 'RegExpStartCharacter'; }; export const regExpStartCharacter = createBuilder( - 'RegExpStartCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpStartCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpSubExpression.ts b/packages/@romejs/js-ast/regex/RegExpSubExpression.ts index 299082aa0b1..2b08029dc6d 100644 --- a/packages/@romejs/js-ast/regex/RegExpSubExpression.ts +++ b/packages/@romejs/js-ast/regex/RegExpSubExpression.ts @@ -9,16 +9,16 @@ import {AnyRegExpBodyItem, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpSubExpression = JSNodeBase & { - type: 'RegExpSubExpression'; - body: Array; + type: 'RegExpSubExpression'; + body: Array; }; export const regExpSubExpression = createBuilder( - 'RegExpSubExpression', - { - bindingKeys: {}, - visitorKeys: { - body: true, - }, - }, + 'RegExpSubExpression', + { + bindingKeys: {}, + visitorKeys: { + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpWhiteSpaceCharacter.ts b/packages/@romejs/js-ast/regex/RegExpWhiteSpaceCharacter.ts index 43c3b1a97e8..b5b9c913f08 100644 --- a/packages/@romejs/js-ast/regex/RegExpWhiteSpaceCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpWhiteSpaceCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpWhiteSpaceCharacter = JSNodeBase & { - type: 'RegExpWhiteSpaceCharacter'; + type: 'RegExpWhiteSpaceCharacter'; }; export const regExpWhiteSpaceCharacter = createBuilder( - 'RegExpWhiteSpaceCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpWhiteSpaceCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpWordBoundaryCharacter.ts b/packages/@romejs/js-ast/regex/RegExpWordBoundaryCharacter.ts index f42b2fd2263..39edca81ed0 100644 --- a/packages/@romejs/js-ast/regex/RegExpWordBoundaryCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpWordBoundaryCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpWordBoundaryCharacter = JSNodeBase & { - type: 'RegExpWordBoundaryCharacter'; + type: 'RegExpWordBoundaryCharacter'; }; export const regExpWordBoundaryCharacter = createBuilder( - 'RegExpWordBoundaryCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpWordBoundaryCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/regex/RegExpWordCharacter.ts b/packages/@romejs/js-ast/regex/RegExpWordCharacter.ts index 6b8bf4403a6..1676f1ae63a 100644 --- a/packages/@romejs/js-ast/regex/RegExpWordCharacter.ts +++ b/packages/@romejs/js-ast/regex/RegExpWordCharacter.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type RegExpWordCharacter = JSNodeBase & { - type: 'RegExpWordCharacter'; + type: 'RegExpWordCharacter'; }; export const regExpWordCharacter = createBuilder( - 'RegExpWordCharacter', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'RegExpWordCharacter', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/statements/BlockStatement.ts b/packages/@romejs/js-ast/statements/BlockStatement.ts index c19cb5ddc5a..0eaded0729c 100644 --- a/packages/@romejs/js-ast/statements/BlockStatement.ts +++ b/packages/@romejs/js-ast/statements/BlockStatement.ts @@ -9,19 +9,19 @@ import {AnyStatement, Directive, JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type BlockStatement = JSNodeBase & { - type: 'BlockStatement'; - body: Array; - directives?: Array; + type: 'BlockStatement'; + body: Array; + directives?: Array; }; export const blockStatement = createQuickBuilder( - 'BlockStatement', - 'body', - { - bindingKeys: {}, - visitorKeys: { - body: true, - directives: true, - }, - }, + 'BlockStatement', + 'body', + { + bindingKeys: {}, + visitorKeys: { + body: true, + directives: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/BreakStatement.ts b/packages/@romejs/js-ast/statements/BreakStatement.ts index f8fe42fb88a..b183b786bef 100644 --- a/packages/@romejs/js-ast/statements/BreakStatement.ts +++ b/packages/@romejs/js-ast/statements/BreakStatement.ts @@ -9,16 +9,16 @@ import {Identifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type BreakStatement = JSNodeBase & { - type: 'BreakStatement'; - label?: Identifier; + type: 'BreakStatement'; + label?: Identifier; }; export const breakStatement = createBuilder( - 'BreakStatement', - { - bindingKeys: {}, - visitorKeys: { - label: true, - }, - }, + 'BreakStatement', + { + bindingKeys: {}, + visitorKeys: { + label: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/ContinueStatement.ts b/packages/@romejs/js-ast/statements/ContinueStatement.ts index 92087eced8a..2eb8b315928 100644 --- a/packages/@romejs/js-ast/statements/ContinueStatement.ts +++ b/packages/@romejs/js-ast/statements/ContinueStatement.ts @@ -9,16 +9,16 @@ import {Identifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ContinueStatement = JSNodeBase & { - type: 'ContinueStatement'; - label?: Identifier; + type: 'ContinueStatement'; + label?: Identifier; }; export const continueStatement = createBuilder( - 'ContinueStatement', - { - bindingKeys: {}, - visitorKeys: { - label: true, - }, - }, + 'ContinueStatement', + { + bindingKeys: {}, + visitorKeys: { + label: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/DebuggerStatement.ts b/packages/@romejs/js-ast/statements/DebuggerStatement.ts index f853fc2f972..989946810b8 100644 --- a/packages/@romejs/js-ast/statements/DebuggerStatement.ts +++ b/packages/@romejs/js-ast/statements/DebuggerStatement.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type DebuggerStatement = JSNodeBase & { - type: 'DebuggerStatement'; + type: 'DebuggerStatement'; }; export const debuggerStatement = createBuilder( - 'DebuggerStatement', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'DebuggerStatement', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/statements/DoWhileStatement.ts b/packages/@romejs/js-ast/statements/DoWhileStatement.ts index d0a69e5be05..b9093e37bc1 100644 --- a/packages/@romejs/js-ast/statements/DoWhileStatement.ts +++ b/packages/@romejs/js-ast/statements/DoWhileStatement.ts @@ -9,18 +9,18 @@ import {AnyExpression, AnyStatement, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type DoWhileStatement = JSNodeBase & { - type: 'DoWhileStatement'; - body: AnyStatement; - test: AnyExpression; + type: 'DoWhileStatement'; + body: AnyStatement; + test: AnyExpression; }; export const doWhileStatement = createBuilder( - 'DoWhileStatement', - { - bindingKeys: {}, - visitorKeys: { - test: true, - body: true, - }, - }, + 'DoWhileStatement', + { + bindingKeys: {}, + visitorKeys: { + test: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/EmptyStatement.ts b/packages/@romejs/js-ast/statements/EmptyStatement.ts index b97c9f4bcc7..12ac7a5b327 100644 --- a/packages/@romejs/js-ast/statements/EmptyStatement.ts +++ b/packages/@romejs/js-ast/statements/EmptyStatement.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type EmptyStatement = JSNodeBase & { - type: 'EmptyStatement'; + type: 'EmptyStatement'; }; export const emptyStatement = createBuilder( - 'EmptyStatement', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'EmptyStatement', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/statements/ExpressionStatement.ts b/packages/@romejs/js-ast/statements/ExpressionStatement.ts index 7b861486304..8ddde6de89d 100644 --- a/packages/@romejs/js-ast/statements/ExpressionStatement.ts +++ b/packages/@romejs/js-ast/statements/ExpressionStatement.ts @@ -9,16 +9,16 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ExpressionStatement = JSNodeBase & { - type: 'ExpressionStatement'; - expression: AnyExpression; + type: 'ExpressionStatement'; + expression: AnyExpression; }; export const expressionStatement = createBuilder( - 'ExpressionStatement', - { - bindingKeys: {}, - visitorKeys: { - expression: true, - }, - }, + 'ExpressionStatement', + { + bindingKeys: {}, + visitorKeys: { + expression: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/ForInStatement.ts b/packages/@romejs/js-ast/statements/ForInStatement.ts index 604a39cfd82..7211a645d33 100644 --- a/packages/@romejs/js-ast/statements/ForInStatement.ts +++ b/packages/@romejs/js-ast/statements/ForInStatement.ts @@ -6,29 +6,29 @@ */ import { - AnyExpression, - AnyStatement, - AnyTargetAssignmentPattern, - JSNodeBase, - VariableDeclaration, + AnyExpression, + AnyStatement, + AnyTargetAssignmentPattern, + JSNodeBase, + VariableDeclaration, } from '../index'; import {createBuilder} from '../utils'; export type ForInStatement = JSNodeBase & { - type: 'ForInStatement'; - left: VariableDeclaration | AnyTargetAssignmentPattern; - right: AnyExpression; - body: AnyStatement; + type: 'ForInStatement'; + left: VariableDeclaration | AnyTargetAssignmentPattern; + right: AnyExpression; + body: AnyStatement; }; export const forInStatement = createBuilder( - 'ForInStatement', - { - bindingKeys: {}, - visitorKeys: { - left: true, - right: true, - body: true, - }, - }, + 'ForInStatement', + { + bindingKeys: {}, + visitorKeys: { + left: true, + right: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/ForOfStatement.ts b/packages/@romejs/js-ast/statements/ForOfStatement.ts index b4a020c8073..bfb1a8852e6 100644 --- a/packages/@romejs/js-ast/statements/ForOfStatement.ts +++ b/packages/@romejs/js-ast/statements/ForOfStatement.ts @@ -6,30 +6,30 @@ */ import { - AnyExpression, - AnyStatement, - AnyTargetAssignmentPattern, - JSNodeBase, - VariableDeclaration, + AnyExpression, + AnyStatement, + AnyTargetAssignmentPattern, + JSNodeBase, + VariableDeclaration, } from '../index'; import {createBuilder} from '../utils'; export type ForOfStatement = JSNodeBase & { - type: 'ForOfStatement'; - await?: boolean; - left: VariableDeclaration | AnyTargetAssignmentPattern; - right: AnyExpression; - body: AnyStatement; + type: 'ForOfStatement'; + await?: boolean; + left: VariableDeclaration | AnyTargetAssignmentPattern; + right: AnyExpression; + body: AnyStatement; }; export const forOfStatement = createBuilder( - 'ForOfStatement', - { - bindingKeys: {}, - visitorKeys: { - left: true, - right: true, - body: true, - }, - }, + 'ForOfStatement', + { + bindingKeys: {}, + visitorKeys: { + left: true, + right: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/ForStatement.ts b/packages/@romejs/js-ast/statements/ForStatement.ts index fadb718a55d..5de9c512e15 100644 --- a/packages/@romejs/js-ast/statements/ForStatement.ts +++ b/packages/@romejs/js-ast/statements/ForStatement.ts @@ -6,30 +6,30 @@ */ import { - AnyExpression, - AnyStatement, - JSNodeBase, - VariableDeclaration, + AnyExpression, + AnyStatement, + JSNodeBase, + VariableDeclaration, } from '../index'; import {createBuilder} from '../utils'; export type ForStatement = JSNodeBase & { - type: 'ForStatement'; - init?: VariableDeclaration | AnyExpression; - test?: AnyExpression; - update?: AnyExpression; - body: AnyStatement; + type: 'ForStatement'; + init?: VariableDeclaration | AnyExpression; + test?: AnyExpression; + update?: AnyExpression; + body: AnyStatement; }; export const forStatement = createBuilder( - 'ForStatement', - { - bindingKeys: {}, - visitorKeys: { - init: true, - test: true, - update: true, - body: true, - }, - }, + 'ForStatement', + { + bindingKeys: {}, + visitorKeys: { + init: true, + test: true, + update: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/FunctionDeclaration.ts b/packages/@romejs/js-ast/statements/FunctionDeclaration.ts index e510708c824..ca58f75d11d 100644 --- a/packages/@romejs/js-ast/statements/FunctionDeclaration.ts +++ b/packages/@romejs/js-ast/statements/FunctionDeclaration.ts @@ -6,31 +6,31 @@ */ import { - BindingIdentifier, - BlockStatement, - FunctionHead, - JSNodeBase, + BindingIdentifier, + BlockStatement, + FunctionHead, + JSNodeBase, } from '../index'; import {createBuilder} from '../utils'; export type FunctionDeclaration = JSNodeBase & { - type: 'FunctionDeclaration'; - id: BindingIdentifier; - declare?: boolean; - head: FunctionHead; - body: BlockStatement; + type: 'FunctionDeclaration'; + id: BindingIdentifier; + declare?: boolean; + head: FunctionHead; + body: BlockStatement; }; export const functionDeclaration = createBuilder( - 'FunctionDeclaration', - { - bindingKeys: { - id: true, - }, - visitorKeys: { - head: true, - id: true, - body: true, - }, - }, + 'FunctionDeclaration', + { + bindingKeys: { + id: true, + }, + visitorKeys: { + head: true, + id: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/IfStatement.ts b/packages/@romejs/js-ast/statements/IfStatement.ts index 0a5baaa4f5a..ce94ff56f1f 100644 --- a/packages/@romejs/js-ast/statements/IfStatement.ts +++ b/packages/@romejs/js-ast/statements/IfStatement.ts @@ -9,20 +9,20 @@ import {AnyExpression, AnyStatement, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type IfStatement = JSNodeBase & { - type: 'IfStatement'; - test: AnyExpression; - consequent: AnyStatement; - alternate?: AnyStatement; + type: 'IfStatement'; + test: AnyExpression; + consequent: AnyStatement; + alternate?: AnyStatement; }; export const ifStatement = createBuilder( - 'IfStatement', - { - bindingKeys: {}, - visitorKeys: { - test: true, - consequent: true, - alternate: true, - }, - }, + 'IfStatement', + { + bindingKeys: {}, + visitorKeys: { + test: true, + consequent: true, + alternate: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/LabeledStatement.ts b/packages/@romejs/js-ast/statements/LabeledStatement.ts index cd76474536c..da57aeb283d 100644 --- a/packages/@romejs/js-ast/statements/LabeledStatement.ts +++ b/packages/@romejs/js-ast/statements/LabeledStatement.ts @@ -9,18 +9,18 @@ import {AnyStatement, Identifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type LabeledStatement = JSNodeBase & { - type: 'LabeledStatement'; - label: Identifier; - body: AnyStatement; + type: 'LabeledStatement'; + label: Identifier; + body: AnyStatement; }; export const labeledStatement = createBuilder( - 'LabeledStatement', - { - bindingKeys: {}, - visitorKeys: { - label: true, - body: true, - }, - }, + 'LabeledStatement', + { + bindingKeys: {}, + visitorKeys: { + label: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/ReturnStatement.ts b/packages/@romejs/js-ast/statements/ReturnStatement.ts index 50f644a75a4..92115a15ebc 100644 --- a/packages/@romejs/js-ast/statements/ReturnStatement.ts +++ b/packages/@romejs/js-ast/statements/ReturnStatement.ts @@ -9,17 +9,17 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createQuickBuilder} from '../utils'; export type ReturnStatement = JSNodeBase & { - type: 'ReturnStatement'; - argument?: AnyExpression; + type: 'ReturnStatement'; + argument?: AnyExpression; }; export const returnStatement = createQuickBuilder( - 'ReturnStatement', - 'argument', - { - bindingKeys: {}, - visitorKeys: { - argument: true, - }, - }, + 'ReturnStatement', + 'argument', + { + bindingKeys: {}, + visitorKeys: { + argument: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/SwitchStatement.ts b/packages/@romejs/js-ast/statements/SwitchStatement.ts index 6b778dc578d..f3dc46a50a2 100644 --- a/packages/@romejs/js-ast/statements/SwitchStatement.ts +++ b/packages/@romejs/js-ast/statements/SwitchStatement.ts @@ -9,18 +9,18 @@ import {AnyExpression, JSNodeBase, SwitchCase} from '../index'; import {createBuilder} from '../utils'; export type SwitchStatement = JSNodeBase & { - type: 'SwitchStatement'; - discriminant: AnyExpression; - cases: Array; + type: 'SwitchStatement'; + discriminant: AnyExpression; + cases: Array; }; export const switchStatement = createBuilder( - 'SwitchStatement', - { - bindingKeys: {}, - visitorKeys: { - discriminant: true, - cases: true, - }, - }, + 'SwitchStatement', + { + bindingKeys: {}, + visitorKeys: { + discriminant: true, + cases: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/ThrowStatement.ts b/packages/@romejs/js-ast/statements/ThrowStatement.ts index 330a29dbc95..0ed1b07bdd8 100644 --- a/packages/@romejs/js-ast/statements/ThrowStatement.ts +++ b/packages/@romejs/js-ast/statements/ThrowStatement.ts @@ -9,16 +9,16 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ThrowStatement = JSNodeBase & { - type: 'ThrowStatement'; - argument: AnyExpression; + type: 'ThrowStatement'; + argument: AnyExpression; }; export const throwStatement = createBuilder( - 'ThrowStatement', - { - bindingKeys: {}, - visitorKeys: { - argument: true, - }, - }, + 'ThrowStatement', + { + bindingKeys: {}, + visitorKeys: { + argument: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/TryStatement.ts b/packages/@romejs/js-ast/statements/TryStatement.ts index 45516b316be..61387890571 100644 --- a/packages/@romejs/js-ast/statements/TryStatement.ts +++ b/packages/@romejs/js-ast/statements/TryStatement.ts @@ -9,20 +9,20 @@ import {BlockStatement, CatchClause, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TryStatement = JSNodeBase & { - type: 'TryStatement'; - block: BlockStatement; - handler: undefined | CatchClause; - finalizer: undefined | BlockStatement; + type: 'TryStatement'; + block: BlockStatement; + handler: undefined | CatchClause; + finalizer: undefined | BlockStatement; }; export const tryStatement = createBuilder( - 'TryStatement', - { - bindingKeys: {}, - visitorKeys: { - block: true, - handler: true, - finalizer: true, - }, - }, + 'TryStatement', + { + bindingKeys: {}, + visitorKeys: { + block: true, + handler: true, + finalizer: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/VariableDeclarationStatement.ts b/packages/@romejs/js-ast/statements/VariableDeclarationStatement.ts index dcbb63a31d1..dbc4c922c6b 100644 --- a/packages/@romejs/js-ast/statements/VariableDeclarationStatement.ts +++ b/packages/@romejs/js-ast/statements/VariableDeclarationStatement.ts @@ -10,23 +10,23 @@ import {createQuickBuilder} from '../utils'; import {VariableDeclaration} from '../auxiliary/VariableDeclaration'; export type VariableDeclarationStatement = JSNodeBase & { - type: 'VariableDeclarationStatement'; - declaration: VariableDeclaration; - declare?: boolean; + type: 'VariableDeclarationStatement'; + declaration: VariableDeclaration; + declare?: boolean; }; export const variableDeclarationStatement = createQuickBuilder< - VariableDeclarationStatement, - 'declaration' + VariableDeclarationStatement, + 'declaration' >( - 'VariableDeclarationStatement', - 'declaration', - { - bindingKeys: { - declaration: true, - }, - visitorKeys: { - declaration: true, - }, - }, + 'VariableDeclarationStatement', + 'declaration', + { + bindingKeys: { + declaration: true, + }, + visitorKeys: { + declaration: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/WhileStatement.ts b/packages/@romejs/js-ast/statements/WhileStatement.ts index 64726b59bee..cc814eb874b 100644 --- a/packages/@romejs/js-ast/statements/WhileStatement.ts +++ b/packages/@romejs/js-ast/statements/WhileStatement.ts @@ -9,18 +9,18 @@ import {AnyExpression, AnyStatement, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type WhileStatement = JSNodeBase & { - type: 'WhileStatement'; - test: AnyExpression; - body: AnyStatement; + type: 'WhileStatement'; + test: AnyExpression; + body: AnyStatement; }; export const whileStatement = createBuilder( - 'WhileStatement', - { - bindingKeys: {}, - visitorKeys: { - test: true, - body: true, - }, - }, + 'WhileStatement', + { + bindingKeys: {}, + visitorKeys: { + test: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/statements/WithStatement.ts b/packages/@romejs/js-ast/statements/WithStatement.ts index d29cc7ecf5f..c79899807bb 100644 --- a/packages/@romejs/js-ast/statements/WithStatement.ts +++ b/packages/@romejs/js-ast/statements/WithStatement.ts @@ -9,18 +9,18 @@ import {AnyExpression, AnyStatement, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type WithStatement = JSNodeBase & { - type: 'WithStatement'; - object: AnyExpression; - body: AnyStatement; + type: 'WithStatement'; + object: AnyExpression; + body: AnyStatement; }; export const withStatement = createBuilder( - 'WithStatement', - { - bindingKeys: {}, - visitorKeys: { - object: true, - body: true, - }, - }, + 'WithStatement', + { + bindingKeys: {}, + visitorKeys: { + object: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/temp/AmbiguousFlowTypeCastExpression.ts b/packages/@romejs/js-ast/temp/AmbiguousFlowTypeCastExpression.ts index dbdb59e7c8c..e619ce86ecb 100644 --- a/packages/@romejs/js-ast/temp/AmbiguousFlowTypeCastExpression.ts +++ b/packages/@romejs/js-ast/temp/AmbiguousFlowTypeCastExpression.ts @@ -6,31 +6,31 @@ */ import { - AnyExpression, - AnyPrimaryType, - JSNodeBase, - SpreadElement, + AnyExpression, + AnyPrimaryType, + JSNodeBase, + SpreadElement, } from '../index'; import {createBuilder} from '../utils'; export type AmbiguousFlowTypeCastExpression = JSNodeBase & { - type: 'AmbiguousFlowTypeCastExpression'; - expression: AnyExpression | SpreadElement; - typeAnnotation?: AnyPrimaryType; + type: 'AmbiguousFlowTypeCastExpression'; + expression: AnyExpression | SpreadElement; + typeAnnotation?: AnyPrimaryType; - // This is for js-parser so that we can convert type casts to parameters + // This is for js-parser so that we can convert type casts to parameters - // We should figure out some way to remove this - optional?: boolean; + // We should figure out some way to remove this + optional?: boolean; }; export const ambiguousFlowTypeCastExpression = createBuilder( - 'AmbiguousFlowTypeCastExpression', - { - bindingKeys: {}, - visitorKeys: { - expression: true, - typeAnnotation: true, - }, - }, + 'AmbiguousFlowTypeCastExpression', + { + bindingKeys: {}, + visitorKeys: { + expression: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/temp/MockParent.ts b/packages/@romejs/js-ast/temp/MockParent.ts index 1f1fe445b3e..a09a4b28668 100644 --- a/packages/@romejs/js-ast/temp/MockParent.ts +++ b/packages/@romejs/js-ast/temp/MockParent.ts @@ -9,17 +9,17 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type MockParent = JSNodeBase & { - type: 'MockParent'; + type: 'MockParent'; }; export const mockParent = createBuilder( - 'MockParent', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'MockParent', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); export const MOCK_PARENT: MockParent = { - type: 'MockParent', + type: 'MockParent', }; diff --git a/packages/@romejs/js-ast/types/AnyKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/AnyKeywordTypeAnnotation.ts index 88f3feaf2a4..576ec8a9603 100644 --- a/packages/@romejs/js-ast/types/AnyKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/AnyKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type AnyKeywordTypeAnnotation = JSNodeBase & { - type: 'AnyKeywordTypeAnnotation'; + type: 'AnyKeywordTypeAnnotation'; }; export const anyKeywordTypeAnnotation = createBuilder( - 'AnyKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'AnyKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/BigIntKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/BigIntKeywordTypeAnnotation.ts index ee0ca8df37f..1c7dc7323cb 100644 --- a/packages/@romejs/js-ast/types/BigIntKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/BigIntKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type BigIntKeywordTypeAnnotation = JSNodeBase & { - type: 'BigIntKeywordTypeAnnotation'; + type: 'BigIntKeywordTypeAnnotation'; }; export const bigIntKeywordTypeAnnotation = createBuilder( - 'BigIntKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'BigIntKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/BooleanKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/BooleanKeywordTypeAnnotation.ts index 968f504d861..74977504ba4 100644 --- a/packages/@romejs/js-ast/types/BooleanKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/BooleanKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type BooleanKeywordTypeAnnotation = JSNodeBase & { - type: 'BooleanKeywordTypeAnnotation'; + type: 'BooleanKeywordTypeAnnotation'; }; export const booleanKeywordTypeAnnotation = createBuilder( - 'BooleanKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'BooleanKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/BooleanLiteralTypeAnnotation.ts b/packages/@romejs/js-ast/types/BooleanLiteralTypeAnnotation.ts index dc777a7be7b..63ba636e69c 100644 --- a/packages/@romejs/js-ast/types/BooleanLiteralTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/BooleanLiteralTypeAnnotation.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type BooleanLiteralTypeAnnotation = JSNodeBase & { - type: 'BooleanLiteralTypeAnnotation'; - value: boolean; + type: 'BooleanLiteralTypeAnnotation'; + value: boolean; }; export const booleanLiteralTypeAnnotation = createBuilder( - 'BooleanLiteralTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'BooleanLiteralTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/EmptyKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/EmptyKeywordTypeAnnotation.ts index c3ec4a484e9..3d108e5569b 100644 --- a/packages/@romejs/js-ast/types/EmptyKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/EmptyKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type EmptyKeywordTypeAnnotation = JSNodeBase & { - type: 'EmptyKeywordTypeAnnotation'; + type: 'EmptyKeywordTypeAnnotation'; }; export const emptyKeywordTypeAnnotation = createBuilder( - 'EmptyKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'EmptyKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/IntersectionTypeAnnotation.ts b/packages/@romejs/js-ast/types/IntersectionTypeAnnotation.ts index ed2915c2c1d..9c441e9bdb8 100644 --- a/packages/@romejs/js-ast/types/IntersectionTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/IntersectionTypeAnnotation.ts @@ -9,16 +9,16 @@ import {AnyFlowPrimary, AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type IntersectionTypeAnnotation = JSNodeBase & { - type: 'IntersectionTypeAnnotation'; - types: Array; + type: 'IntersectionTypeAnnotation'; + types: Array; }; export const intersectionTypeAnnotation = createBuilder( - 'IntersectionTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: { - types: true, - }, - }, + 'IntersectionTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: { + types: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/types/MixedKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/MixedKeywordTypeAnnotation.ts index 04a17def952..491d8b33d7c 100644 --- a/packages/@romejs/js-ast/types/MixedKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/MixedKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type MixedKeywordTypeAnnotation = JSNodeBase & { - type: 'MixedKeywordTypeAnnotation'; + type: 'MixedKeywordTypeAnnotation'; }; export const mixedKeywordTypeAnnotation = createBuilder( - 'MixedKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'MixedKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/NeverKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/NeverKeywordTypeAnnotation.ts index 1475e0ded91..b2fdd7af9a9 100644 --- a/packages/@romejs/js-ast/types/NeverKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/NeverKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type NeverKeywordTypeAnnotation = JSNodeBase & { - type: 'NeverKeywordTypeAnnotation'; + type: 'NeverKeywordTypeAnnotation'; }; export const neverKeywordTypeAnnotation = createBuilder( - 'NeverKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'NeverKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/NullKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/NullKeywordTypeAnnotation.ts index 5386a92e0b3..f89940b2799 100644 --- a/packages/@romejs/js-ast/types/NullKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/NullKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type NullKeywordTypeAnnotation = JSNodeBase & { - type: 'NullKeywordTypeAnnotation'; + type: 'NullKeywordTypeAnnotation'; }; export const nullKeywordTypeAnnotation = createBuilder( - 'NullKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'NullKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/NumberKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/NumberKeywordTypeAnnotation.ts index d3f914b0ad1..ff014b5614d 100644 --- a/packages/@romejs/js-ast/types/NumberKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/NumberKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type NumberKeywordTypeAnnotation = JSNodeBase & { - type: 'NumberKeywordTypeAnnotation'; + type: 'NumberKeywordTypeAnnotation'; }; export const numberKeywordTypeAnnotation = createBuilder( - 'NumberKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'NumberKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/NumericLiteralTypeAnnotation.ts b/packages/@romejs/js-ast/types/NumericLiteralTypeAnnotation.ts index d4656c3a35e..4be67c07903 100644 --- a/packages/@romejs/js-ast/types/NumericLiteralTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/NumericLiteralTypeAnnotation.ts @@ -9,15 +9,15 @@ import {JSNodeBase, NumericLiteral} from '../index'; import {createBuilder} from '../utils'; export type NumericLiteralTypeAnnotation = JSNodeBase & { - type: 'NumericLiteralTypeAnnotation'; - value: number; - format?: NumericLiteral['format']; + type: 'NumericLiteralTypeAnnotation'; + value: number; + format?: NumericLiteral['format']; }; export const numericLiteralTypeAnnotation = createBuilder( - 'NumericLiteralTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'NumericLiteralTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/ObjectKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/ObjectKeywordTypeAnnotation.ts index fb6405302b2..d5558f52928 100644 --- a/packages/@romejs/js-ast/types/ObjectKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/ObjectKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type ObjectKeywordTypeAnnotation = JSNodeBase & { - type: 'ObjectKeywordTypeAnnotation'; + type: 'ObjectKeywordTypeAnnotation'; }; export const objectKeywordTypeAnnotation = createBuilder( - 'ObjectKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'ObjectKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/StringKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/StringKeywordTypeAnnotation.ts index 6773b8405e6..81ffb37db07 100644 --- a/packages/@romejs/js-ast/types/StringKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/StringKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type StringKeywordTypeAnnotation = JSNodeBase & { - type: 'StringKeywordTypeAnnotation'; + type: 'StringKeywordTypeAnnotation'; }; export const stringKeywordTypeAnnotation = createBuilder( - 'StringKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'StringKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/StringLiteralTypeAnnotation.ts b/packages/@romejs/js-ast/types/StringLiteralTypeAnnotation.ts index a57f9b2d99f..8ebf2040e15 100644 --- a/packages/@romejs/js-ast/types/StringLiteralTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/StringLiteralTypeAnnotation.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type StringLiteralTypeAnnotation = JSNodeBase & { - type: 'StringLiteralTypeAnnotation'; - value: string; + type: 'StringLiteralTypeAnnotation'; + value: string; }; export const stringLiteralTypeAnnotation = createBuilder( - 'StringLiteralTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'StringLiteralTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/SymbolKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/SymbolKeywordTypeAnnotation.ts index a81fcb48432..b0a02abd4b0 100644 --- a/packages/@romejs/js-ast/types/SymbolKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/SymbolKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type SymbolKeywordTypeAnnotation = JSNodeBase & { - type: 'SymbolKeywordTypeAnnotation'; + type: 'SymbolKeywordTypeAnnotation'; }; export const symbolKeywordTypeAnnotation = createBuilder( - 'SymbolKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'SymbolKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/TemplateLiteralTypeAnnotation.ts b/packages/@romejs/js-ast/types/TemplateLiteralTypeAnnotation.ts index dede9bd5fb3..b3aafb6f12e 100644 --- a/packages/@romejs/js-ast/types/TemplateLiteralTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/TemplateLiteralTypeAnnotation.ts @@ -9,14 +9,14 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TemplateLiteralTypeAnnotation = JSNodeBase & { - type: 'TemplateLiteralTypeAnnotation'; - value: string; + type: 'TemplateLiteralTypeAnnotation'; + value: string; }; export const templateLiteralTypeAnnotation = createBuilder( - 'TemplateLiteralTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'TemplateLiteralTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/TypeAliasTypeAnnotation.ts b/packages/@romejs/js-ast/types/TypeAliasTypeAnnotation.ts index db687f67155..f47101d201e 100644 --- a/packages/@romejs/js-ast/types/TypeAliasTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/TypeAliasTypeAnnotation.ts @@ -6,31 +6,31 @@ */ import { - AnyPrimaryType, - BindingIdentifier, - JSNodeBase, - TSTypeParameterDeclaration, + AnyPrimaryType, + BindingIdentifier, + JSNodeBase, + TSTypeParameterDeclaration, } from '../index'; import {createBuilder} from '../utils'; export type TypeAliasTypeAnnotation = JSNodeBase & { - type: 'TypeAliasTypeAnnotation'; - id: BindingIdentifier; - typeParameters?: TSTypeParameterDeclaration; - right: AnyPrimaryType; - declare?: boolean | undefined; + type: 'TypeAliasTypeAnnotation'; + id: BindingIdentifier; + typeParameters?: TSTypeParameterDeclaration; + right: AnyPrimaryType; + declare?: boolean | undefined; }; export const typeAliasTypeAnnotation = createBuilder( - 'TypeAliasTypeAnnotation', - { - bindingKeys: { - id: true, - }, - visitorKeys: { - id: true, - typeParameters: true, - right: true, - }, - }, + 'TypeAliasTypeAnnotation', + { + bindingKeys: { + id: true, + }, + visitorKeys: { + id: true, + typeParameters: true, + right: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/types/UndefinedKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/UndefinedKeywordTypeAnnotation.ts index 4f7ff585ec7..5cbb06627de 100644 --- a/packages/@romejs/js-ast/types/UndefinedKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/UndefinedKeywordTypeAnnotation.ts @@ -9,10 +9,10 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type UndefinedKeywordTypeAnnotation = JSNodeBase & { - type: 'UndefinedKeywordTypeAnnotation'; + type: 'UndefinedKeywordTypeAnnotation'; }; export const undefinedKeywordTypeAnnotation = createBuilder( - 'UndefinedKeywordTypeAnnotation', - {bindingKeys: {}, visitorKeys: {}}, + 'UndefinedKeywordTypeAnnotation', + {bindingKeys: {}, visitorKeys: {}}, ); diff --git a/packages/@romejs/js-ast/types/UnionTypeAnnotation.ts b/packages/@romejs/js-ast/types/UnionTypeAnnotation.ts index 255798d1eb0..c688219fce4 100644 --- a/packages/@romejs/js-ast/types/UnionTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/UnionTypeAnnotation.ts @@ -9,16 +9,16 @@ import {AnyFlowPrimary, AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type UnionTypeAnnotation = JSNodeBase & { - type: 'UnionTypeAnnotation'; - types: Array; + type: 'UnionTypeAnnotation'; + types: Array; }; export const unionTypeAnnotation = createBuilder( - 'UnionTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: { - types: true, - }, - }, + 'UnionTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: { + types: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/types/UnknownKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/UnknownKeywordTypeAnnotation.ts index 81134d932ba..1ad30f7d0ca 100644 --- a/packages/@romejs/js-ast/types/UnknownKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/UnknownKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type UnknownKeywordTypeAnnotation = JSNodeBase & { - type: 'UnknownKeywordTypeAnnotation'; + type: 'UnknownKeywordTypeAnnotation'; }; export const unknownKeywordTypeAnnotation = createBuilder( - 'UnknownKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'UnknownKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/types/VoidKeywordTypeAnnotation.ts b/packages/@romejs/js-ast/types/VoidKeywordTypeAnnotation.ts index 1753a857eea..714e540dbe6 100644 --- a/packages/@romejs/js-ast/types/VoidKeywordTypeAnnotation.ts +++ b/packages/@romejs/js-ast/types/VoidKeywordTypeAnnotation.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type VoidKeywordTypeAnnotation = JSNodeBase & { - type: 'VoidKeywordTypeAnnotation'; + type: 'VoidKeywordTypeAnnotation'; }; export const voidKeywordTypeAnnotation = createBuilder( - 'VoidKeywordTypeAnnotation', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'VoidKeywordTypeAnnotation', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSArrayType.ts b/packages/@romejs/js-ast/typescript/TSArrayType.ts index 788cd2a292a..6aebaaba90a 100644 --- a/packages/@romejs/js-ast/typescript/TSArrayType.ts +++ b/packages/@romejs/js-ast/typescript/TSArrayType.ts @@ -9,14 +9,14 @@ import {AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSArrayType = JSNodeBase & { - type: 'TSArrayType'; - elementType: AnyTSPrimary; + type: 'TSArrayType'; + elementType: AnyTSPrimary; }; export const tsArrayType = createBuilder( - 'TSArrayType', - { - bindingKeys: {}, - visitorKeys: {elementType: true}, - }, + 'TSArrayType', + { + bindingKeys: {}, + visitorKeys: {elementType: true}, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSAsExpression.ts b/packages/@romejs/js-ast/typescript/TSAsExpression.ts index 1ec9229b0ce..881d47af6e0 100644 --- a/packages/@romejs/js-ast/typescript/TSAsExpression.ts +++ b/packages/@romejs/js-ast/typescript/TSAsExpression.ts @@ -9,15 +9,15 @@ import {AnyExpression, AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSAsExpression = JSNodeBase & { - type: 'TSAsExpression'; - typeAnnotation: AnyTSPrimary; - expression: AnyExpression; + type: 'TSAsExpression'; + typeAnnotation: AnyTSPrimary; + expression: AnyExpression; }; export const tsAsExpression = createBuilder( - 'TSAsExpression', - { - bindingKeys: {}, - visitorKeys: {expression: true, typeAnnotation: true}, - }, + 'TSAsExpression', + { + bindingKeys: {}, + visitorKeys: {expression: true, typeAnnotation: true}, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSAssignmentAsExpression.ts b/packages/@romejs/js-ast/typescript/TSAssignmentAsExpression.ts index 8a1b6f2bead..71edf61f14f 100644 --- a/packages/@romejs/js-ast/typescript/TSAssignmentAsExpression.ts +++ b/packages/@romejs/js-ast/typescript/TSAssignmentAsExpression.ts @@ -9,15 +9,15 @@ import {AnyTSPrimary, AnyTargetAssignmentPattern, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSAssignmentAsExpression = JSNodeBase & { - type: 'TSAssignmentAsExpression'; - typeAnnotation: AnyTSPrimary; - expression: AnyTargetAssignmentPattern; + type: 'TSAssignmentAsExpression'; + typeAnnotation: AnyTSPrimary; + expression: AnyTargetAssignmentPattern; }; export const tsAssignmentAsExpression = createBuilder( - 'TSAssignmentAsExpression', - { - bindingKeys: {}, - visitorKeys: {expression: true, typeAnnotation: true}, - }, + 'TSAssignmentAsExpression', + { + bindingKeys: {}, + visitorKeys: {expression: true, typeAnnotation: true}, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSAssignmentNonNullExpression.ts b/packages/@romejs/js-ast/typescript/TSAssignmentNonNullExpression.ts index a31ed9ba652..306cfbd463b 100644 --- a/packages/@romejs/js-ast/typescript/TSAssignmentNonNullExpression.ts +++ b/packages/@romejs/js-ast/typescript/TSAssignmentNonNullExpression.ts @@ -9,14 +9,14 @@ import {AnyTargetAssignmentPattern, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSAssignmentNonNullExpression = JSNodeBase & { - type: 'TSAssignmentNonNullExpression'; - expression: AnyTargetAssignmentPattern; + type: 'TSAssignmentNonNullExpression'; + expression: AnyTargetAssignmentPattern; }; export const tsAssignmentNonNullExpression = createBuilder( - 'TSAssignmentNonNullExpression', - { - bindingKeys: {}, - visitorKeys: {expression: true}, - }, + 'TSAssignmentNonNullExpression', + { + bindingKeys: {}, + visitorKeys: {expression: true}, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSAssignmentTypeAssertion.ts b/packages/@romejs/js-ast/typescript/TSAssignmentTypeAssertion.ts index bd94b16405d..d97bc381f27 100644 --- a/packages/@romejs/js-ast/typescript/TSAssignmentTypeAssertion.ts +++ b/packages/@romejs/js-ast/typescript/TSAssignmentTypeAssertion.ts @@ -9,15 +9,15 @@ import {AnyTSPrimary, AnyTargetAssignmentPattern, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSAssignmentTypeAssertion = JSNodeBase & { - type: 'TSAssignmentTypeAssertion'; - typeAnnotation: AnyTSPrimary; - expression: AnyTargetAssignmentPattern; + type: 'TSAssignmentTypeAssertion'; + typeAnnotation: AnyTSPrimary; + expression: AnyTargetAssignmentPattern; }; export const tsAssignmentTypeAssertion = createBuilder( - 'TSAssignmentTypeAssertion', - { - bindingKeys: {}, - visitorKeys: {expression: true, typeAnnotation: true}, - }, + 'TSAssignmentTypeAssertion', + { + bindingKeys: {}, + visitorKeys: {expression: true, typeAnnotation: true}, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSCallSignatureDeclaration.ts b/packages/@romejs/js-ast/typescript/TSCallSignatureDeclaration.ts index b14b08f42c6..69edc05dd88 100644 --- a/packages/@romejs/js-ast/typescript/TSCallSignatureDeclaration.ts +++ b/packages/@romejs/js-ast/typescript/TSCallSignatureDeclaration.ts @@ -9,18 +9,18 @@ import {AnyTSPrimary, JSNodeBase, TSSignatureDeclarationMeta} from '../index'; import {createBuilder} from '../utils'; export type TSCallSignatureDeclaration = JSNodeBase & { - type: 'TSCallSignatureDeclaration'; - meta: TSSignatureDeclarationMeta; - typeAnnotation?: AnyTSPrimary; + type: 'TSCallSignatureDeclaration'; + meta: TSSignatureDeclarationMeta; + typeAnnotation?: AnyTSPrimary; }; export const tsCallSignatureDeclaration = createBuilder( - 'TSCallSignatureDeclaration', - { - bindingKeys: {}, - visitorKeys: { - meta: true, - typeAnnotation: true, - }, - }, + 'TSCallSignatureDeclaration', + { + bindingKeys: {}, + visitorKeys: { + meta: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSConditionalType.ts b/packages/@romejs/js-ast/typescript/TSConditionalType.ts index 3a1e6fde069..6c838d61af7 100644 --- a/packages/@romejs/js-ast/typescript/TSConditionalType.ts +++ b/packages/@romejs/js-ast/typescript/TSConditionalType.ts @@ -9,22 +9,22 @@ import {AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSConditionalType = JSNodeBase & { - type: 'TSConditionalType'; - checkType: AnyTSPrimary; - extendsType: AnyTSPrimary; - trueType: AnyTSPrimary; - falseType: AnyTSPrimary; + type: 'TSConditionalType'; + checkType: AnyTSPrimary; + extendsType: AnyTSPrimary; + trueType: AnyTSPrimary; + falseType: AnyTSPrimary; }; export const tsConditionalType = createBuilder( - 'TSConditionalType', - { - bindingKeys: {}, - visitorKeys: { - checkType: true, - extendsType: true, - trueType: true, - falseType: true, - }, - }, + 'TSConditionalType', + { + bindingKeys: {}, + visitorKeys: { + checkType: true, + extendsType: true, + trueType: true, + falseType: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSConstructSignatureDeclaration.ts b/packages/@romejs/js-ast/typescript/TSConstructSignatureDeclaration.ts index 23f5e572fcf..8128d8d6b10 100644 --- a/packages/@romejs/js-ast/typescript/TSConstructSignatureDeclaration.ts +++ b/packages/@romejs/js-ast/typescript/TSConstructSignatureDeclaration.ts @@ -9,18 +9,18 @@ import {AnyTSPrimary, JSNodeBase, TSSignatureDeclarationMeta} from '../index'; import {createBuilder} from '../utils'; export type TSConstructSignatureDeclaration = JSNodeBase & { - type: 'TSConstructSignatureDeclaration'; - meta: TSSignatureDeclarationMeta; - typeAnnotation?: AnyTSPrimary; + type: 'TSConstructSignatureDeclaration'; + meta: TSSignatureDeclarationMeta; + typeAnnotation?: AnyTSPrimary; }; export const tsConstructSignatureDeclaration = createBuilder( - 'TSConstructSignatureDeclaration', - { - bindingKeys: {}, - visitorKeys: { - meta: true, - typeAnnotation: true, - }, - }, + 'TSConstructSignatureDeclaration', + { + bindingKeys: {}, + visitorKeys: { + meta: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSConstructorType.ts b/packages/@romejs/js-ast/typescript/TSConstructorType.ts index 62e830db317..2faaddae9a8 100644 --- a/packages/@romejs/js-ast/typescript/TSConstructorType.ts +++ b/packages/@romejs/js-ast/typescript/TSConstructorType.ts @@ -9,18 +9,18 @@ import {AnyPrimaryType, JSNodeBase, TSSignatureDeclarationMeta} from '../index'; import {createBuilder} from '../utils'; export type TSConstructorType = JSNodeBase & { - type: 'TSConstructorType'; - meta: TSSignatureDeclarationMeta; - typeAnnotation: AnyPrimaryType; + type: 'TSConstructorType'; + meta: TSSignatureDeclarationMeta; + typeAnnotation: AnyPrimaryType; }; export const tsConstructorType = createBuilder( - 'TSConstructorType', - { - bindingKeys: {}, - visitorKeys: { - meta: true, - typeAnnotation: true, - }, - }, + 'TSConstructorType', + { + bindingKeys: {}, + visitorKeys: { + meta: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSDeclareFunction.ts b/packages/@romejs/js-ast/typescript/TSDeclareFunction.ts index c8eb40b2fb7..a4708d33b96 100644 --- a/packages/@romejs/js-ast/typescript/TSDeclareFunction.ts +++ b/packages/@romejs/js-ast/typescript/TSDeclareFunction.ts @@ -10,23 +10,23 @@ import {createBuilder} from '../utils'; import {FunctionHead} from '../auxiliary/FunctionHead'; export type TSDeclareFunction = JSNodeBase & { - type: 'TSDeclareFunction'; - id: BindingIdentifier; - head: FunctionHead; + type: 'TSDeclareFunction'; + id: BindingIdentifier; + head: FunctionHead; - // For consistency with FunctionDeclaration, this can mostly be ignored - declare?: boolean; + // For consistency with FunctionDeclaration, this can mostly be ignored + declare?: boolean; }; export const tsDeclareFunction = createBuilder( - 'TSDeclareFunction', - { - bindingKeys: { - id: true, - }, - visitorKeys: { - id: true, - head: true, - }, - }, + 'TSDeclareFunction', + { + bindingKeys: { + id: true, + }, + visitorKeys: { + id: true, + head: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSDeclareMethod.ts b/packages/@romejs/js-ast/typescript/TSDeclareMethod.ts index a882d5738dd..9f359a49187 100644 --- a/packages/@romejs/js-ast/typescript/TSDeclareMethod.ts +++ b/packages/@romejs/js-ast/typescript/TSDeclareMethod.ts @@ -6,31 +6,31 @@ */ import { - AnyObjectPropertyKey, - ClassMethodKind, - ClassPropertyMeta, - FunctionHead, - JSNodeBase, + AnyObjectPropertyKey, + ClassMethodKind, + ClassPropertyMeta, + FunctionHead, + JSNodeBase, } from '../index'; import {createBuilder} from '../utils'; export type TSDeclareMethod = JSNodeBase & { - type: 'TSDeclareMethod'; - meta: ClassPropertyMeta; - kind: ClassMethodKind; - key: AnyObjectPropertyKey; - head: FunctionHead; - body?: void; + type: 'TSDeclareMethod'; + meta: ClassPropertyMeta; + kind: ClassMethodKind; + key: AnyObjectPropertyKey; + head: FunctionHead; + body?: void; }; export const tsDeclareMethod = createBuilder( - 'TSDeclareMethod', - { - bindingKeys: {}, - visitorKeys: { - meta: true, - key: true, - head: true, - }, - }, + 'TSDeclareMethod', + { + bindingKeys: {}, + visitorKeys: { + meta: true, + key: true, + head: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSEnumDeclaration.ts b/packages/@romejs/js-ast/typescript/TSEnumDeclaration.ts index 61c30fb0e07..ab85f0a896e 100644 --- a/packages/@romejs/js-ast/typescript/TSEnumDeclaration.ts +++ b/packages/@romejs/js-ast/typescript/TSEnumDeclaration.ts @@ -9,22 +9,22 @@ import {BindingIdentifier, JSNodeBase, TSEnumMember} from '../index'; import {createBuilder} from '../utils'; export type TSEnumDeclaration = JSNodeBase & { - type: 'TSEnumDeclaration'; - id: BindingIdentifier; - const?: boolean; - members: Array; - declare?: boolean; + type: 'TSEnumDeclaration'; + id: BindingIdentifier; + const?: boolean; + members: Array; + declare?: boolean; }; export const tsEnumDeclaration = createBuilder( - 'TSEnumDeclaration', - { - bindingKeys: { - id: true, - }, - visitorKeys: { - id: true, - members: true, - }, - }, + 'TSEnumDeclaration', + { + bindingKeys: { + id: true, + }, + visitorKeys: { + id: true, + members: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSEnumMember.ts b/packages/@romejs/js-ast/typescript/TSEnumMember.ts index 791eb08cc7c..55f5cf3e328 100644 --- a/packages/@romejs/js-ast/typescript/TSEnumMember.ts +++ b/packages/@romejs/js-ast/typescript/TSEnumMember.ts @@ -9,18 +9,18 @@ import {AnyExpression, Identifier, JSNodeBase, StringLiteral} from '../index'; import {createBuilder} from '../utils'; export type TSEnumMember = JSNodeBase & { - type: 'TSEnumMember'; - id: StringLiteral | Identifier; - initializer?: AnyExpression; + type: 'TSEnumMember'; + id: StringLiteral | Identifier; + initializer?: AnyExpression; }; export const tsEnumMember = createBuilder( - 'TSEnumMember', - { - bindingKeys: {}, - visitorKeys: { - id: true, - initializer: true, - }, - }, + 'TSEnumMember', + { + bindingKeys: {}, + visitorKeys: { + id: true, + initializer: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSExportAssignment.ts b/packages/@romejs/js-ast/typescript/TSExportAssignment.ts index c84fdfe0d52..94676d951c1 100644 --- a/packages/@romejs/js-ast/typescript/TSExportAssignment.ts +++ b/packages/@romejs/js-ast/typescript/TSExportAssignment.ts @@ -9,11 +9,11 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSExportAssignment = JSNodeBase & { - type: 'TSExportAssignment'; - expression: AnyExpression; + type: 'TSExportAssignment'; + expression: AnyExpression; }; export const tsExportAssignment = createBuilder( - 'TSExportAssignment', - {bindingKeys: {}, visitorKeys: {expression: true}}, + 'TSExportAssignment', + {bindingKeys: {}, visitorKeys: {expression: true}}, ); diff --git a/packages/@romejs/js-ast/typescript/TSExpressionWithTypeArguments.ts b/packages/@romejs/js-ast/typescript/TSExpressionWithTypeArguments.ts index 999ecc91f8f..c54884bc2d6 100644 --- a/packages/@romejs/js-ast/typescript/TSExpressionWithTypeArguments.ts +++ b/packages/@romejs/js-ast/typescript/TSExpressionWithTypeArguments.ts @@ -6,25 +6,25 @@ */ import { - AnyTSEntityName, - JSNodeBase, - TSTypeParameterInstantiation, + AnyTSEntityName, + JSNodeBase, + TSTypeParameterInstantiation, } from '../index'; import {createBuilder} from '../utils'; export type TSExpressionWithTypeArguments = JSNodeBase & { - type: 'TSExpressionWithTypeArguments'; - expression: AnyTSEntityName; - typeParameters?: TSTypeParameterInstantiation; + type: 'TSExpressionWithTypeArguments'; + expression: AnyTSEntityName; + typeParameters?: TSTypeParameterInstantiation; }; export const tsExpressionWithTypeArguments = createBuilder( - 'TSExpressionWithTypeArguments', - { - bindingKeys: {}, - visitorKeys: { - expression: true, - typeParameters: true, - }, - }, + 'TSExpressionWithTypeArguments', + { + bindingKeys: {}, + visitorKeys: { + expression: true, + typeParameters: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSExternalModuleReference.ts b/packages/@romejs/js-ast/typescript/TSExternalModuleReference.ts index 7931443a37a..d870fc7b20f 100644 --- a/packages/@romejs/js-ast/typescript/TSExternalModuleReference.ts +++ b/packages/@romejs/js-ast/typescript/TSExternalModuleReference.ts @@ -9,14 +9,14 @@ import {JSNodeBase, StringLiteral} from '../index'; import {createBuilder} from '../utils'; export type TSExternalModuleReference = JSNodeBase & { - type: 'TSExternalModuleReference'; - expression: StringLiteral; + type: 'TSExternalModuleReference'; + expression: StringLiteral; }; export const tsExternalModuleReference = createBuilder( - 'TSExternalModuleReference', - { - bindingKeys: {}, - visitorKeys: {expression: true}, - }, + 'TSExternalModuleReference', + { + bindingKeys: {}, + visitorKeys: {expression: true}, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSFunctionType.ts b/packages/@romejs/js-ast/typescript/TSFunctionType.ts index 45598ce3e96..56178c62159 100644 --- a/packages/@romejs/js-ast/typescript/TSFunctionType.ts +++ b/packages/@romejs/js-ast/typescript/TSFunctionType.ts @@ -9,18 +9,18 @@ import {AnyPrimaryType, JSNodeBase, TSSignatureDeclarationMeta} from '../index'; import {createBuilder} from '../utils'; export type TSFunctionType = JSNodeBase & { - type: 'TSFunctionType'; - meta: TSSignatureDeclarationMeta; - typeAnnotation: AnyPrimaryType; + type: 'TSFunctionType'; + meta: TSSignatureDeclarationMeta; + typeAnnotation: AnyPrimaryType; }; export const tsFunctionType = createBuilder( - 'TSFunctionType', - { - bindingKeys: {}, - visitorKeys: { - meta: true, - typeAnnotation: true, - }, - }, + 'TSFunctionType', + { + bindingKeys: {}, + visitorKeys: { + meta: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSImportEqualsDeclaration.ts b/packages/@romejs/js-ast/typescript/TSImportEqualsDeclaration.ts index 96690233529..5f8df2ba15a 100644 --- a/packages/@romejs/js-ast/typescript/TSImportEqualsDeclaration.ts +++ b/packages/@romejs/js-ast/typescript/TSImportEqualsDeclaration.ts @@ -9,18 +9,18 @@ import {AnyTSModuleReference, BindingIdentifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSImportEqualsDeclaration = JSNodeBase & { - type: 'TSImportEqualsDeclaration'; - id: BindingIdentifier; - moduleReference: AnyTSModuleReference; - isExport?: boolean; + type: 'TSImportEqualsDeclaration'; + id: BindingIdentifier; + moduleReference: AnyTSModuleReference; + isExport?: boolean; }; export const tsImportEqualsDeclaration = createBuilder( - 'TSImportEqualsDeclaration', - { - bindingKeys: { - id: true, - }, - visitorKeys: {id: true, moduleReference: true}, - }, + 'TSImportEqualsDeclaration', + { + bindingKeys: { + id: true, + }, + visitorKeys: {id: true, moduleReference: true}, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSImportType.ts b/packages/@romejs/js-ast/typescript/TSImportType.ts index b421249c156..2d462ad4d62 100644 --- a/packages/@romejs/js-ast/typescript/TSImportType.ts +++ b/packages/@romejs/js-ast/typescript/TSImportType.ts @@ -6,28 +6,28 @@ */ import { - AnyExpression, - AnyTSEntityName, - JSNodeBase, - TSTypeParameterInstantiation, + AnyExpression, + AnyTSEntityName, + JSNodeBase, + TSTypeParameterInstantiation, } from '../index'; import {createBuilder} from '../utils'; export type TSImportType = JSNodeBase & { - type: 'TSImportType'; - argument: AnyExpression; - typeParameters?: TSTypeParameterInstantiation; - qualifier?: AnyTSEntityName; + type: 'TSImportType'; + argument: AnyExpression; + typeParameters?: TSTypeParameterInstantiation; + qualifier?: AnyTSEntityName; }; export const tsImportType = createBuilder( - 'TSImportType', - { - bindingKeys: {}, - visitorKeys: { - argument: true, - typeParameters: true, - qualifier: true, - }, - }, + 'TSImportType', + { + bindingKeys: {}, + visitorKeys: { + argument: true, + typeParameters: true, + qualifier: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSIndexSignature.ts b/packages/@romejs/js-ast/typescript/TSIndexSignature.ts index 1bcead4db05..6f1dfede4bc 100644 --- a/packages/@romejs/js-ast/typescript/TSIndexSignature.ts +++ b/packages/@romejs/js-ast/typescript/TSIndexSignature.ts @@ -9,21 +9,21 @@ import {AnyPrimaryType, BindingIdentifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSIndexSignature = JSNodeBase & { - type: 'TSIndexSignature'; - readonly?: boolean; - key: BindingIdentifier; - typeAnnotation: undefined | AnyPrimaryType; + type: 'TSIndexSignature'; + readonly?: boolean; + key: BindingIdentifier; + typeAnnotation: undefined | AnyPrimaryType; }; export const tsIndexSignature = createBuilder( - 'TSIndexSignature', - { - bindingKeys: { - key: true, - }, - visitorKeys: { - typeAnnotation: true, - key: true, - }, - }, + 'TSIndexSignature', + { + bindingKeys: { + key: true, + }, + visitorKeys: { + typeAnnotation: true, + key: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSIndexedAccessType.ts b/packages/@romejs/js-ast/typescript/TSIndexedAccessType.ts index 1c359dd69e5..d6aeb5f4b87 100644 --- a/packages/@romejs/js-ast/typescript/TSIndexedAccessType.ts +++ b/packages/@romejs/js-ast/typescript/TSIndexedAccessType.ts @@ -9,18 +9,18 @@ import {AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSIndexedAccessType = JSNodeBase & { - type: 'TSIndexedAccessType'; - objectType: AnyTSPrimary; - indexType: AnyTSPrimary; + type: 'TSIndexedAccessType'; + objectType: AnyTSPrimary; + indexType: AnyTSPrimary; }; export const tsIndexedAccessType = createBuilder( - 'TSIndexedAccessType', - { - bindingKeys: {}, - visitorKeys: { - objectType: true, - indexType: true, - }, - }, + 'TSIndexedAccessType', + { + bindingKeys: {}, + visitorKeys: { + objectType: true, + indexType: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSInferType.ts b/packages/@romejs/js-ast/typescript/TSInferType.ts index 272f297cf62..58b7f64c612 100644 --- a/packages/@romejs/js-ast/typescript/TSInferType.ts +++ b/packages/@romejs/js-ast/typescript/TSInferType.ts @@ -9,16 +9,16 @@ import {JSNodeBase, TSTypeParameter} from '../index'; import {createBuilder} from '../utils'; export type TSInferType = JSNodeBase & { - type: 'TSInferType'; - typeParameter: TSTypeParameter; + type: 'TSInferType'; + typeParameter: TSTypeParameter; }; export const tsInferType = createBuilder( - 'TSInferType', - { - bindingKeys: {}, - visitorKeys: { - typeParameter: true, - }, - }, + 'TSInferType', + { + bindingKeys: {}, + visitorKeys: { + typeParameter: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSInterfaceBody.ts b/packages/@romejs/js-ast/typescript/TSInterfaceBody.ts index 19c77686b28..5ed441fee22 100644 --- a/packages/@romejs/js-ast/typescript/TSInterfaceBody.ts +++ b/packages/@romejs/js-ast/typescript/TSInterfaceBody.ts @@ -9,16 +9,16 @@ import {AnyTSTypeElement, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSInterfaceBody = JSNodeBase & { - type: 'TSInterfaceBody'; - body: Array; + type: 'TSInterfaceBody'; + body: Array; }; export const tsInterfaceBody = createBuilder( - 'TSInterfaceBody', - { - bindingKeys: {}, - visitorKeys: { - body: true, - }, - }, + 'TSInterfaceBody', + { + bindingKeys: {}, + visitorKeys: { + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSInterfaceDeclaration.ts b/packages/@romejs/js-ast/typescript/TSInterfaceDeclaration.ts index fa7f7d81a45..f2339d07adc 100644 --- a/packages/@romejs/js-ast/typescript/TSInterfaceDeclaration.ts +++ b/packages/@romejs/js-ast/typescript/TSInterfaceDeclaration.ts @@ -6,34 +6,34 @@ */ import { - BindingIdentifier, - JSNodeBase, - TSExpressionWithTypeArguments, - TSInterfaceBody, - TSTypeParameterDeclaration, + BindingIdentifier, + JSNodeBase, + TSExpressionWithTypeArguments, + TSInterfaceBody, + TSTypeParameterDeclaration, } from '../index'; import {createBuilder} from '../utils'; export type TSInterfaceDeclaration = JSNodeBase & { - type: 'TSInterfaceDeclaration'; - id: BindingIdentifier; - body: TSInterfaceBody; - typeParameters?: TSTypeParameterDeclaration; - extends?: Array; - declare?: boolean; + type: 'TSInterfaceDeclaration'; + id: BindingIdentifier; + body: TSInterfaceBody; + typeParameters?: TSTypeParameterDeclaration; + extends?: Array; + declare?: boolean; }; export const tsInterfaceDeclaration = createBuilder( - 'TSInterfaceDeclaration', - { - bindingKeys: { - id: true, - }, - visitorKeys: { - id: true, - body: true, - typeParameters: true, - extends: true, - }, - }, + 'TSInterfaceDeclaration', + { + bindingKeys: { + id: true, + }, + visitorKeys: { + id: true, + body: true, + typeParameters: true, + extends: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSMappedType.ts b/packages/@romejs/js-ast/typescript/TSMappedType.ts index fb54311fec8..97c8d57ab10 100644 --- a/packages/@romejs/js-ast/typescript/TSMappedType.ts +++ b/packages/@romejs/js-ast/typescript/TSMappedType.ts @@ -11,20 +11,20 @@ import {createBuilder} from '../utils'; export type TSMappedTypeBoolean = undefined | boolean | '+' | '-'; export type TSMappedType = JSNodeBase & { - type: 'TSMappedType'; - typeParameter: TSTypeParameter; - typeAnnotation?: AnyTSPrimary; - optional?: TSMappedTypeBoolean; - readonly?: TSMappedTypeBoolean; + type: 'TSMappedType'; + typeParameter: TSTypeParameter; + typeAnnotation?: AnyTSPrimary; + optional?: TSMappedTypeBoolean; + readonly?: TSMappedTypeBoolean; }; export const tsMappedType = createBuilder( - 'TSMappedType', - { - bindingKeys: {}, - visitorKeys: { - typeParameter: true, - typeAnnotation: true, - }, - }, + 'TSMappedType', + { + bindingKeys: {}, + visitorKeys: { + typeParameter: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSMethodSignature.ts b/packages/@romejs/js-ast/typescript/TSMethodSignature.ts index 114258a9ac2..a5e6404ac8a 100644 --- a/packages/@romejs/js-ast/typescript/TSMethodSignature.ts +++ b/packages/@romejs/js-ast/typescript/TSMethodSignature.ts @@ -6,29 +6,29 @@ */ import { - AnyObjectPropertyKey, - AnyTSPrimary, - JSNodeBase, - TSSignatureDeclarationMeta, + AnyObjectPropertyKey, + AnyTSPrimary, + JSNodeBase, + TSSignatureDeclarationMeta, } from '../index'; import {createBuilder} from '../utils'; export type TSMethodSignature = JSNodeBase & { - key: AnyObjectPropertyKey; - type: 'TSMethodSignature'; - optional?: boolean; - meta: TSSignatureDeclarationMeta; - returnType?: AnyTSPrimary; + key: AnyObjectPropertyKey; + type: 'TSMethodSignature'; + optional?: boolean; + meta: TSSignatureDeclarationMeta; + returnType?: AnyTSPrimary; }; export const tsMethodSignature = createBuilder( - 'TSMethodSignature', - { - bindingKeys: {}, - visitorKeys: { - key: true, - meta: true, - returnType: true, - }, - }, + 'TSMethodSignature', + { + bindingKeys: {}, + visitorKeys: { + key: true, + meta: true, + returnType: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSModuleBlock.ts b/packages/@romejs/js-ast/typescript/TSModuleBlock.ts index 5ea9aefb0a4..f0692519ab9 100644 --- a/packages/@romejs/js-ast/typescript/TSModuleBlock.ts +++ b/packages/@romejs/js-ast/typescript/TSModuleBlock.ts @@ -9,16 +9,16 @@ import {AnyStatement, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSModuleBlock = JSNodeBase & { - type: 'TSModuleBlock'; - body: Array; + type: 'TSModuleBlock'; + body: Array; }; export const tsModuleBlock = createBuilder( - 'TSModuleBlock', - { - bindingKeys: {}, - visitorKeys: { - body: true, - }, - }, + 'TSModuleBlock', + { + bindingKeys: {}, + visitorKeys: { + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSModuleDeclaration.ts b/packages/@romejs/js-ast/typescript/TSModuleDeclaration.ts index d7867293b5a..90c34fb24bf 100644 --- a/packages/@romejs/js-ast/typescript/TSModuleDeclaration.ts +++ b/packages/@romejs/js-ast/typescript/TSModuleDeclaration.ts @@ -6,28 +6,28 @@ */ import { - BindingIdentifier, - JSNodeBase, - StringLiteral, - TSModuleBlock, + BindingIdentifier, + JSNodeBase, + StringLiteral, + TSModuleBlock, } from '../index'; import {createBuilder} from '../utils'; export type TSModuleDeclaration = JSNodeBase & { - type: 'TSModuleDeclaration'; - id: BindingIdentifier | StringLiteral; - global?: boolean; - body?: TSModuleBlock | TSModuleDeclaration; - declare?: boolean; + type: 'TSModuleDeclaration'; + id: BindingIdentifier | StringLiteral; + global?: boolean; + body?: TSModuleBlock | TSModuleDeclaration; + declare?: boolean; }; export const tsModuleDeclaration = createBuilder( - 'TSModuleDeclaration', - { - bindingKeys: {}, - visitorKeys: { - id: true, - body: true, - }, - }, + 'TSModuleDeclaration', + { + bindingKeys: {}, + visitorKeys: { + id: true, + body: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSNamespaceExportDeclaration.ts b/packages/@romejs/js-ast/typescript/TSNamespaceExportDeclaration.ts index 24ef0b67a5e..19b21fa1b98 100644 --- a/packages/@romejs/js-ast/typescript/TSNamespaceExportDeclaration.ts +++ b/packages/@romejs/js-ast/typescript/TSNamespaceExportDeclaration.ts @@ -9,11 +9,11 @@ import {Identifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSNamespaceExportDeclaration = JSNodeBase & { - type: 'TSNamespaceExportDeclaration'; - id: Identifier; + type: 'TSNamespaceExportDeclaration'; + id: Identifier; }; export const tsNamespaceExportDeclaration = createBuilder( - 'TSNamespaceExportDeclaration', - {bindingKeys: {}, visitorKeys: {id: true}}, + 'TSNamespaceExportDeclaration', + {bindingKeys: {}, visitorKeys: {id: true}}, ); diff --git a/packages/@romejs/js-ast/typescript/TSNonNullExpression.ts b/packages/@romejs/js-ast/typescript/TSNonNullExpression.ts index 06d8f87652f..af6d4b1cdaf 100644 --- a/packages/@romejs/js-ast/typescript/TSNonNullExpression.ts +++ b/packages/@romejs/js-ast/typescript/TSNonNullExpression.ts @@ -9,16 +9,16 @@ import {AnyExpression, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSNonNullExpression = JSNodeBase & { - type: 'TSNonNullExpression'; - expression: AnyExpression; + type: 'TSNonNullExpression'; + expression: AnyExpression; }; export const tsNonNullExpression = createBuilder( - 'TSNonNullExpression', - { - bindingKeys: {}, - visitorKeys: { - expression: true, - }, - }, + 'TSNonNullExpression', + { + bindingKeys: {}, + visitorKeys: { + expression: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSOptionalType.ts b/packages/@romejs/js-ast/typescript/TSOptionalType.ts index 8899873012a..29b0e72276e 100644 --- a/packages/@romejs/js-ast/typescript/TSOptionalType.ts +++ b/packages/@romejs/js-ast/typescript/TSOptionalType.ts @@ -9,16 +9,16 @@ import {AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSOptionalType = JSNodeBase & { - type: 'TSOptionalType'; - typeAnnotation: AnyTSPrimary; + type: 'TSOptionalType'; + typeAnnotation: AnyTSPrimary; }; export const tsOptionalType = createBuilder( - 'TSOptionalType', - { - bindingKeys: {}, - visitorKeys: { - typeAnnotation: true, - }, - }, + 'TSOptionalType', + { + bindingKeys: {}, + visitorKeys: { + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSParenthesizedType.ts b/packages/@romejs/js-ast/typescript/TSParenthesizedType.ts index 52dafed012f..69f17026b15 100644 --- a/packages/@romejs/js-ast/typescript/TSParenthesizedType.ts +++ b/packages/@romejs/js-ast/typescript/TSParenthesizedType.ts @@ -9,16 +9,16 @@ import {AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSParenthesizedType = JSNodeBase & { - type: 'TSParenthesizedType'; - typeAnnotation: AnyTSPrimary; + type: 'TSParenthesizedType'; + typeAnnotation: AnyTSPrimary; }; export const tsParenthesizedType = createBuilder( - 'TSParenthesizedType', - { - bindingKeys: {}, - visitorKeys: { - typeAnnotation: true, - }, - }, + 'TSParenthesizedType', + { + bindingKeys: {}, + visitorKeys: { + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSPropertySignature.ts b/packages/@romejs/js-ast/typescript/TSPropertySignature.ts index b20eb3935a1..d0083d61479 100644 --- a/packages/@romejs/js-ast/typescript/TSPropertySignature.ts +++ b/packages/@romejs/js-ast/typescript/TSPropertySignature.ts @@ -9,20 +9,20 @@ import {AnyObjectPropertyKey, AnyPrimaryType, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSPropertySignature = JSNodeBase & { - type: 'TSPropertySignature'; - key: AnyObjectPropertyKey; - optional?: boolean; - readonly?: boolean; - typeAnnotation?: AnyPrimaryType; + type: 'TSPropertySignature'; + key: AnyObjectPropertyKey; + optional?: boolean; + readonly?: boolean; + typeAnnotation?: AnyPrimaryType; }; export const tsPropertySignature = createBuilder( - 'TSPropertySignature', - { - bindingKeys: {}, - visitorKeys: { - key: true, - typeAnnotation: true, - }, - }, + 'TSPropertySignature', + { + bindingKeys: {}, + visitorKeys: { + key: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSQualifiedName.ts b/packages/@romejs/js-ast/typescript/TSQualifiedName.ts index a51f7ad713f..d4b596beb09 100644 --- a/packages/@romejs/js-ast/typescript/TSQualifiedName.ts +++ b/packages/@romejs/js-ast/typescript/TSQualifiedName.ts @@ -9,18 +9,18 @@ import {AnyTSEntityName, Identifier, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSQualifiedName = JSNodeBase & { - type: 'TSQualifiedName'; - left: AnyTSEntityName; - right: Identifier; + type: 'TSQualifiedName'; + left: AnyTSEntityName; + right: Identifier; }; export const tsQualifiedName = createBuilder( - 'TSQualifiedName', - { - bindingKeys: {}, - visitorKeys: { - left: true, - right: true, - }, - }, + 'TSQualifiedName', + { + bindingKeys: {}, + visitorKeys: { + left: true, + right: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSSignatureDeclarationMeta.ts b/packages/@romejs/js-ast/typescript/TSSignatureDeclarationMeta.ts index df8949274b8..ed9a4d0cf6c 100644 --- a/packages/@romejs/js-ast/typescript/TSSignatureDeclarationMeta.ts +++ b/packages/@romejs/js-ast/typescript/TSSignatureDeclarationMeta.ts @@ -6,32 +6,32 @@ */ import { - AnyTargetBindingPattern, - BindingArrayPattern, - BindingIdentifier, - BindingObjectPattern, - JSNodeBase, - TSTypeParameterDeclaration, + AnyTargetBindingPattern, + BindingArrayPattern, + BindingIdentifier, + BindingObjectPattern, + JSNodeBase, + TSTypeParameterDeclaration, } from '../index'; import {createBuilder} from '../utils'; export type TSSignatureDeclarationMeta = JSNodeBase & { - type: 'TSSignatureDeclarationMeta'; - parameters: Array< - BindingIdentifier | BindingObjectPattern | BindingArrayPattern - >; - rest: undefined | AnyTargetBindingPattern; - typeParameters: undefined | TSTypeParameterDeclaration; + type: 'TSSignatureDeclarationMeta'; + parameters: Array< + BindingIdentifier | BindingObjectPattern | BindingArrayPattern + >; + rest: undefined | AnyTargetBindingPattern; + typeParameters: undefined | TSTypeParameterDeclaration; }; export const tsSignatureDeclarationMeta = createBuilder( - 'TSSignatureDeclarationMeta', - { - bindingKeys: {}, - visitorKeys: { - parameters: true, - rest: true, - typeParameters: true, - }, - }, + 'TSSignatureDeclarationMeta', + { + bindingKeys: {}, + visitorKeys: { + parameters: true, + rest: true, + typeParameters: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSThisType.ts b/packages/@romejs/js-ast/typescript/TSThisType.ts index cc2acfee3ab..12c5c28746f 100644 --- a/packages/@romejs/js-ast/typescript/TSThisType.ts +++ b/packages/@romejs/js-ast/typescript/TSThisType.ts @@ -9,13 +9,13 @@ import {JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSThisType = JSNodeBase & { - type: 'TSThisType'; + type: 'TSThisType'; }; export const tsThisType = createBuilder( - 'TSThisType', - { - bindingKeys: {}, - visitorKeys: {}, - }, + 'TSThisType', + { + bindingKeys: {}, + visitorKeys: {}, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSTupleType.ts b/packages/@romejs/js-ast/typescript/TSTupleType.ts index 4c169ff42f8..9c8a17f1a64 100644 --- a/packages/@romejs/js-ast/typescript/TSTupleType.ts +++ b/packages/@romejs/js-ast/typescript/TSTupleType.ts @@ -9,18 +9,18 @@ import {AnyTSPrimary, JSNodeBase, TSOptionalType} from '../index'; import {createBuilder} from '../utils'; export type TSTupleType = JSNodeBase & { - type: 'TSTupleType'; - elementTypes: Array; - rest?: AnyTSPrimary; + type: 'TSTupleType'; + elementTypes: Array; + rest?: AnyTSPrimary; }; export const tsTupleType = createBuilder( - 'TSTupleType', - { - bindingKeys: {}, - visitorKeys: { - elementTypes: true, - rest: true, - }, - }, + 'TSTupleType', + { + bindingKeys: {}, + visitorKeys: { + elementTypes: true, + rest: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSTypeAssertion.ts b/packages/@romejs/js-ast/typescript/TSTypeAssertion.ts index 386be46705a..c3f07200393 100644 --- a/packages/@romejs/js-ast/typescript/TSTypeAssertion.ts +++ b/packages/@romejs/js-ast/typescript/TSTypeAssertion.ts @@ -9,18 +9,18 @@ import {AnyExpression, AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSTypeAssertion = JSNodeBase & { - type: 'TSTypeAssertion'; - expression: AnyExpression; - typeAnnotation: AnyTSPrimary; + type: 'TSTypeAssertion'; + expression: AnyExpression; + typeAnnotation: AnyTSPrimary; }; export const tsTypeAssertion = createBuilder( - 'TSTypeAssertion', - { - bindingKeys: {}, - visitorKeys: { - expression: true, - typeAnnotation: true, - }, - }, + 'TSTypeAssertion', + { + bindingKeys: {}, + visitorKeys: { + expression: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSTypeLiteral.ts b/packages/@romejs/js-ast/typescript/TSTypeLiteral.ts index 843497eccc1..e0ec98cf536 100644 --- a/packages/@romejs/js-ast/typescript/TSTypeLiteral.ts +++ b/packages/@romejs/js-ast/typescript/TSTypeLiteral.ts @@ -9,16 +9,16 @@ import {AnyTSTypeElement, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSTypeLiteral = JSNodeBase & { - type: 'TSTypeLiteral'; - members: Array; + type: 'TSTypeLiteral'; + members: Array; }; export const tsTypeLiteral = createBuilder( - 'TSTypeLiteral', - { - bindingKeys: {}, - visitorKeys: { - members: true, - }, - }, + 'TSTypeLiteral', + { + bindingKeys: {}, + visitorKeys: { + members: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSTypeOperator.ts b/packages/@romejs/js-ast/typescript/TSTypeOperator.ts index 6e14afa81e4..8d9aea3c4e3 100644 --- a/packages/@romejs/js-ast/typescript/TSTypeOperator.ts +++ b/packages/@romejs/js-ast/typescript/TSTypeOperator.ts @@ -9,17 +9,17 @@ import {AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSTypeOperator = JSNodeBase & { - type: 'TSTypeOperator'; - operator: 'keyof' | 'unique' | 'readonly'; - typeAnnotation: AnyTSPrimary; + type: 'TSTypeOperator'; + operator: 'keyof' | 'unique' | 'readonly'; + typeAnnotation: AnyTSPrimary; }; export const tsTypeOperator = createBuilder( - 'TSTypeOperator', - { - bindingKeys: {}, - visitorKeys: { - typeAnnotation: true, - }, - }, + 'TSTypeOperator', + { + bindingKeys: {}, + visitorKeys: { + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSTypeParameter.ts b/packages/@romejs/js-ast/typescript/TSTypeParameter.ts index 7f7784f1908..47792be45fd 100644 --- a/packages/@romejs/js-ast/typescript/TSTypeParameter.ts +++ b/packages/@romejs/js-ast/typescript/TSTypeParameter.ts @@ -9,19 +9,19 @@ import {AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSTypeParameter = JSNodeBase & { - type: 'TSTypeParameter'; - name: string; - default?: AnyTSPrimary; - constraint?: AnyTSPrimary; + type: 'TSTypeParameter'; + name: string; + default?: AnyTSPrimary; + constraint?: AnyTSPrimary; }; export const tsTypeParameter = createBuilder( - 'TSTypeParameter', - { - bindingKeys: {}, - visitorKeys: { - default: true, - constraint: true, - }, - }, + 'TSTypeParameter', + { + bindingKeys: {}, + visitorKeys: { + default: true, + constraint: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSTypeParameterDeclaration.ts b/packages/@romejs/js-ast/typescript/TSTypeParameterDeclaration.ts index ba8429b0632..7e51f82b803 100644 --- a/packages/@romejs/js-ast/typescript/TSTypeParameterDeclaration.ts +++ b/packages/@romejs/js-ast/typescript/TSTypeParameterDeclaration.ts @@ -9,16 +9,16 @@ import {JSNodeBase, TSTypeParameter} from '../index'; import {createBuilder} from '../utils'; export type TSTypeParameterDeclaration = JSNodeBase & { - type: 'TSTypeParameterDeclaration'; - params: Array; + type: 'TSTypeParameterDeclaration'; + params: Array; }; export const tsTypeParameterDeclaration = createBuilder( - 'TSTypeParameterDeclaration', - { - bindingKeys: {}, - visitorKeys: { - params: true, - }, - }, + 'TSTypeParameterDeclaration', + { + bindingKeys: {}, + visitorKeys: { + params: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSTypeParameterInstantiation.ts b/packages/@romejs/js-ast/typescript/TSTypeParameterInstantiation.ts index 45d20440116..c53929c5561 100644 --- a/packages/@romejs/js-ast/typescript/TSTypeParameterInstantiation.ts +++ b/packages/@romejs/js-ast/typescript/TSTypeParameterInstantiation.ts @@ -9,16 +9,16 @@ import {AnyTSPrimary, JSNodeBase} from '../index'; import {createBuilder} from '../utils'; export type TSTypeParameterInstantiation = JSNodeBase & { - type: 'TSTypeParameterInstantiation'; - params: Array; + type: 'TSTypeParameterInstantiation'; + params: Array; }; export const tsTypeParameterInstantiation = createBuilder( - 'TSTypeParameterInstantiation', - { - bindingKeys: {}, - visitorKeys: { - params: true, - }, - }, + 'TSTypeParameterInstantiation', + { + bindingKeys: {}, + visitorKeys: { + params: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSTypePredicate.ts b/packages/@romejs/js-ast/typescript/TSTypePredicate.ts index eac6e254e37..936b1896396 100644 --- a/packages/@romejs/js-ast/typescript/TSTypePredicate.ts +++ b/packages/@romejs/js-ast/typescript/TSTypePredicate.ts @@ -9,19 +9,19 @@ import {AnyPrimaryType, Identifier, JSNodeBase, TSThisType} from '../index'; import {createBuilder} from '../utils'; export type TSTypePredicate = JSNodeBase & { - type: 'TSTypePredicate'; - asserts: boolean; - parameterName: Identifier | TSThisType; - typeAnnotation?: AnyPrimaryType; + type: 'TSTypePredicate'; + asserts: boolean; + parameterName: Identifier | TSThisType; + typeAnnotation?: AnyPrimaryType; }; export const tsTypePredicate = createBuilder( - 'TSTypePredicate', - { - bindingKeys: {}, - visitorKeys: { - parameterName: true, - typeAnnotation: true, - }, - }, + 'TSTypePredicate', + { + bindingKeys: {}, + visitorKeys: { + parameterName: true, + typeAnnotation: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSTypeQuery.ts b/packages/@romejs/js-ast/typescript/TSTypeQuery.ts index 593ac182896..b2e4bed1f6d 100644 --- a/packages/@romejs/js-ast/typescript/TSTypeQuery.ts +++ b/packages/@romejs/js-ast/typescript/TSTypeQuery.ts @@ -9,16 +9,16 @@ import {AnyTSEntityName, JSNodeBase, TSImportType} from '../index'; import {createBuilder} from '../utils'; export type TSTypeQuery = JSNodeBase & { - type: 'TSTypeQuery'; - exprName: TSImportType | AnyTSEntityName; + type: 'TSTypeQuery'; + exprName: TSImportType | AnyTSEntityName; }; export const tsTypeQuery = createBuilder( - 'TSTypeQuery', - { - bindingKeys: {}, - visitorKeys: { - exprName: true, - }, - }, + 'TSTypeQuery', + { + bindingKeys: {}, + visitorKeys: { + exprName: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/typescript/TSTypeReference.ts b/packages/@romejs/js-ast/typescript/TSTypeReference.ts index d3452937481..21ec7576618 100644 --- a/packages/@romejs/js-ast/typescript/TSTypeReference.ts +++ b/packages/@romejs/js-ast/typescript/TSTypeReference.ts @@ -6,25 +6,25 @@ */ import { - AnyTSEntityName, - JSNodeBase, - TSTypeParameterInstantiation, + AnyTSEntityName, + JSNodeBase, + TSTypeParameterInstantiation, } from '../index'; import {createBuilder} from '../utils'; export type TSTypeReference = JSNodeBase & { - type: 'TSTypeReference'; - typeName: AnyTSEntityName; - typeParameters?: TSTypeParameterInstantiation; + type: 'TSTypeReference'; + typeName: AnyTSEntityName; + typeParameters?: TSTypeParameterInstantiation; }; export const tsTypeReference = createBuilder( - 'TSTypeReference', - { - bindingKeys: {}, - visitorKeys: { - typeName: true, - typeParameters: true, - }, - }, + 'TSTypeReference', + { + bindingKeys: {}, + visitorKeys: { + typeName: true, + typeParameters: true, + }, + }, ); diff --git a/packages/@romejs/js-ast/unions.ts b/packages/@romejs/js-ast/unions.ts index 4d7d74659aa..df08031cfc2 100644 --- a/packages/@romejs/js-ast/unions.ts +++ b/packages/@romejs/js-ast/unions.ts @@ -11,44 +11,44 @@ import {OptionalProps} from '@romejs/typescript-helpers'; export type AnyTSEntityName = n.ReferenceIdentifier | n.TSQualifiedName; export type AnyTSTypeElement = - | n.TSCallSignatureDeclaration - | n.TSConstructSignatureDeclaration - | n.TSIndexSignature - | n.TSPropertySignature - | n.TSMethodSignature; + | n.TSCallSignatureDeclaration + | n.TSConstructSignatureDeclaration + | n.TSIndexSignature + | n.TSPropertySignature + | n.TSMethodSignature; export type AnyTSModuleReference = AnyTSEntityName | n.TSExternalModuleReference; export type AnyTSFunctionOrConstructorType = - | n.TSFunctionType - | n.TSConstructorType; + | n.TSFunctionType + | n.TSConstructorType; export type ObjectProperties = Array< - n.ObjectProperty | n.ObjectMethod | n.SpreadProperty + n.ObjectProperty | n.ObjectMethod | n.SpreadProperty >; export type AnyFunction = - | n.ArrowFunctionExpression - | n.FunctionDeclaration - | n.FunctionExpression - | n.ObjectMethod - | n.ClassMethod; + | n.ArrowFunctionExpression + | n.FunctionDeclaration + | n.FunctionExpression + | n.ObjectMethod + | n.ClassMethod; export type AnyVariableIdentifier = - | n.BindingIdentifier - | n.AssignmentIdentifier - | n.ReferenceIdentifier - | n.JSXReferenceIdentifier; + | n.BindingIdentifier + | n.AssignmentIdentifier + | n.ReferenceIdentifier + | n.JSXReferenceIdentifier; export type AnyObjectOrClassMember = AnyClassMember | AnyObjectMember; export type AnyClassMember = - | n.ClassMethod - | n.ClassPrivateMethod - | n.ClassProperty - | n.ClassPrivateProperty - | n.TSDeclareMethod - | n.TSIndexSignature; + | n.ClassMethod + | n.ClassPrivateMethod + | n.ClassProperty + | n.ClassPrivateProperty + | n.TSDeclareMethod + | n.TSIndexSignature; export type AnyObjectMember = n.ObjectProperty | n.ObjectMethod; @@ -59,510 +59,510 @@ export type AnyForStatement = AnyForInOfStatement | n.ForStatement; export type AnyForInOfStatement = n.ForInStatement | n.ForOfStatement; export type AnyLiteral = - | n.StringLiteral - | n.BooleanLiteral - | n.NumericLiteral - | n.RegExpLiteral - | n.NullLiteral; + | n.StringLiteral + | n.BooleanLiteral + | n.NumericLiteral + | n.RegExpLiteral + | n.NullLiteral; export type AnyClass = n.ClassDeclaration | n.ClassExpression; export type AnyAuxiliary = - | AnyClassMember - | AnyObjectMember - | n.ObjectProperty - | n.ObjectMethod - | n.ArrayHole - | n.SpreadElement - | n.SpreadProperty - | n.JSXSpreadAttribute - | n.JSXText - | n.JSXNamespacedName - | n.JSXSpreadChild - | n.JSXExpressionContainer - | n.JSXAttribute - | n.ImportSpecifier - | n.ImportDefaultSpecifier - | n.ImportNamespaceSpecifier - | n.ExportLocalSpecifier - | n.ExportNamespaceSpecifier - | n.Directive - | n.InterpreterDirective - | n.JSXReferenceIdentifier - | AnyBindingPattern - | AnyAssignmentPattern - | AnyComment - | n.Identifier - | n.VariableDeclaration - | n.CatchClause - | n.ClassHead - | n.ClassPropertyMeta - | n.ComputedMemberProperty - | n.ComputedPropertyKey - | n.AnyExportExternalSpecifier - | n.ExportLocalSpecifier - | n.FunctionHead - | n.PatternMeta - | n.PrivateName - | n.StaticMemberProperty - | n.StaticPropertyKey - | n.SwitchCase - | n.TemplateElement - | n.VariableDeclarator - | n.AmbiguousFlowTypeCastExpression - | n.MockParent; + | AnyClassMember + | AnyObjectMember + | n.ObjectProperty + | n.ObjectMethod + | n.ArrayHole + | n.SpreadElement + | n.SpreadProperty + | n.JSXSpreadAttribute + | n.JSXText + | n.JSXNamespacedName + | n.JSXSpreadChild + | n.JSXExpressionContainer + | n.JSXAttribute + | n.ImportSpecifier + | n.ImportDefaultSpecifier + | n.ImportNamespaceSpecifier + | n.ExportLocalSpecifier + | n.ExportNamespaceSpecifier + | n.Directive + | n.InterpreterDirective + | n.JSXReferenceIdentifier + | AnyBindingPattern + | AnyAssignmentPattern + | AnyComment + | n.Identifier + | n.VariableDeclaration + | n.CatchClause + | n.ClassHead + | n.ClassPropertyMeta + | n.ComputedMemberProperty + | n.ComputedPropertyKey + | n.AnyExportExternalSpecifier + | n.ExportLocalSpecifier + | n.FunctionHead + | n.PatternMeta + | n.PrivateName + | n.StaticMemberProperty + | n.StaticPropertyKey + | n.SwitchCase + | n.TemplateElement + | n.VariableDeclarator + | n.AmbiguousFlowTypeCastExpression + | n.MockParent; export type AnyComment = n.CommentBlock | n.CommentLine; export type AnyCommentOptionalId = - | OptionalProps - | OptionalProps; + | OptionalProps + | OptionalProps; export type AnyIdentifier = - | n.Identifier - | n.JSXIdentifier - | n.BindingIdentifier - | n.AssignmentIdentifier - | n.ReferenceIdentifier - | n.JSXReferenceIdentifier; + | n.Identifier + | n.JSXIdentifier + | n.BindingIdentifier + | n.AssignmentIdentifier + | n.ReferenceIdentifier + | n.JSXReferenceIdentifier; export type AnyReference = n.ReferenceIdentifier | n.MemberExpression; export type AnyExpression = - | n.ReferenceIdentifier - | n.JSXElement - | n.JSXFragment - | n.JSXMemberExpression - | n.JSXEmptyExpression - | n.JSXIdentifier - | n.ClassExpression - | n.FunctionExpression - | n.UnaryExpression - | n.UpdateExpression - | n.BinaryExpression - | n.AssignmentExpression - | n.LogicalExpression - | n.MemberExpression - | n.ConditionalExpression - | n.CallExpression - | n.NewExpression - | n.DoExpression - | n.SequenceExpression - | n.RegExpLiteral - | n.NullLiteral - | n.StringLiteral - | n.BooleanLiteral - | n.NumericLiteral - | n.BigIntLiteral - | n.Super - | n.ImportCall - | n.ThisExpression - | n.ArrowFunctionExpression - | n.YieldExpression - | n.AwaitExpression - | n.ArrayExpression - | n.ObjectExpression - | n.OptionalCallExpression - | n.TemplateLiteral - | n.TaggedTemplateExpression - | n.MetaProperty - | n.TSNonNullExpression - | n.TSAsExpression - | n.TSTypeAssertion; + | n.ReferenceIdentifier + | n.JSXElement + | n.JSXFragment + | n.JSXMemberExpression + | n.JSXEmptyExpression + | n.JSXIdentifier + | n.ClassExpression + | n.FunctionExpression + | n.UnaryExpression + | n.UpdateExpression + | n.BinaryExpression + | n.AssignmentExpression + | n.LogicalExpression + | n.MemberExpression + | n.ConditionalExpression + | n.CallExpression + | n.NewExpression + | n.DoExpression + | n.SequenceExpression + | n.RegExpLiteral + | n.NullLiteral + | n.StringLiteral + | n.BooleanLiteral + | n.NumericLiteral + | n.BigIntLiteral + | n.Super + | n.ImportCall + | n.ThisExpression + | n.ArrowFunctionExpression + | n.YieldExpression + | n.AwaitExpression + | n.ArrayExpression + | n.ObjectExpression + | n.OptionalCallExpression + | n.TemplateLiteral + | n.TaggedTemplateExpression + | n.MetaProperty + | n.TSNonNullExpression + | n.TSAsExpression + | n.TSTypeAssertion; export type AnyWhileStatement = n.WhileStatement | n.DoWhileStatement; type AnyStatementWithBodyReducer = T extends { - body: AnyStatement; + body: AnyStatement; } - ? T - : never; + ? T + : never; export type AnyStatementWithBody = AnyStatementWithBodyReducer; export type AnyStatement = - | n.AnyDeclaration - | n.ExpressionStatement - | n.ForStatement - | n.ForOfStatement - | n.ForInStatement - | n.WhileStatement - | n.DoWhileStatement - | n.BlockStatement - | n.EmptyStatement - | n.DebuggerStatement - | n.ReturnStatement - | n.WithStatement - | n.LabeledStatement - | n.BreakStatement - | n.ContinueStatement - | n.IfStatement - | n.SwitchStatement - | n.ThrowStatement - | n.TryStatement; + | n.AnyDeclaration + | n.ExpressionStatement + | n.ForStatement + | n.ForOfStatement + | n.ForInStatement + | n.WhileStatement + | n.DoWhileStatement + | n.BlockStatement + | n.EmptyStatement + | n.DebuggerStatement + | n.ReturnStatement + | n.WithStatement + | n.LabeledStatement + | n.BreakStatement + | n.ContinueStatement + | n.IfStatement + | n.SwitchStatement + | n.ThrowStatement + | n.TryStatement; export type AnyBindingPattern = - | n.BindingAssignmentPattern - | n.BindingIdentifier - | n.BindingObjectPattern - | n.BindingArrayPattern - | n.BindingObjectPatternProperty; + | n.BindingAssignmentPattern + | n.BindingIdentifier + | n.BindingObjectPattern + | n.BindingArrayPattern + | n.BindingObjectPatternProperty; export type AnyParamBindingPattern = - | n.AnyTargetBindingPattern - | n.BindingAssignmentPattern; + | n.AnyTargetBindingPattern + | n.BindingAssignmentPattern; export type AnyTargetBindingPattern = - | n.BindingIdentifier - | n.BindingArrayPattern - | n.BindingObjectPattern; + | n.BindingIdentifier + | n.BindingArrayPattern + | n.BindingObjectPattern; export type AnyAssignmentPattern = - | n.AssignmentAssignmentPattern - | n.AssignmentObjectPatternProperty - | n.AnyTargetAssignmentPattern; + | n.AssignmentAssignmentPattern + | n.AssignmentObjectPatternProperty + | n.AnyTargetAssignmentPattern; export type AnyTargetAssignmentPattern = - | n.MemberExpression - | n.AssignmentIdentifier - | n.AssignmentArrayPattern - | n.AssignmentObjectPattern - | n.TSAssignmentAsExpression - | n.TSAssignmentNonNullExpression - | n.TSAssignmentTypeAssertion; + | n.MemberExpression + | n.AssignmentIdentifier + | n.AssignmentArrayPattern + | n.AssignmentObjectPattern + | n.TSAssignmentAsExpression + | n.TSAssignmentNonNullExpression + | n.TSAssignmentTypeAssertion; export type AnyArrayPattern = n.AssignmentArrayPattern | n.BindingArrayPattern; export type AnyDeclaration = - | n.VariableDeclarationStatement - | n.ClassDeclaration - | n.FunctionDeclaration - | n.ImportDeclaration - | n.ExportLocalDeclaration - | n.ExportDefaultDeclaration - | n.ExportExternalDeclaration - | n.ExportAllDeclaration - | n.TypeAliasTypeAnnotation - | n.TSEnumDeclaration - | n.TypeAliasTypeAnnotation - | n.TSInterfaceDeclaration - | n.TSNamespaceExportDeclaration - | n.TSExportAssignment - | n.TSImportEqualsDeclaration - | n.TSDeclareFunction - | n.TSModuleDeclaration; + | n.VariableDeclarationStatement + | n.ClassDeclaration + | n.FunctionDeclaration + | n.ImportDeclaration + | n.ExportLocalDeclaration + | n.ExportDefaultDeclaration + | n.ExportExternalDeclaration + | n.ExportAllDeclaration + | n.TypeAliasTypeAnnotation + | n.TSEnumDeclaration + | n.TypeAliasTypeAnnotation + | n.TSInterfaceDeclaration + | n.TSNamespaceExportDeclaration + | n.TSExportAssignment + | n.TSImportEqualsDeclaration + | n.TSDeclareFunction + | n.TSModuleDeclaration; export type AnyFlowPrimary = - | n.IntersectionTypeAnnotation - | n.UnionTypeAnnotation - | n.IntersectionTypeAnnotation - | n.UnionTypeAnnotation - | n.IntersectionTypeAnnotation - | n.UnionTypeAnnotation - | n.NullKeywordTypeAnnotation - | AnyLiteralTypeAnnotation - | AnyFlowKeywordTypeAnnotation; + | n.IntersectionTypeAnnotation + | n.UnionTypeAnnotation + | n.IntersectionTypeAnnotation + | n.UnionTypeAnnotation + | n.IntersectionTypeAnnotation + | n.UnionTypeAnnotation + | n.NullKeywordTypeAnnotation + | AnyLiteralTypeAnnotation + | AnyFlowKeywordTypeAnnotation; export type AnyLiteralTypeAnnotation = - | n.StringLiteralTypeAnnotation - | n.BooleanLiteralTypeAnnotation - | n.NumericLiteralTypeAnnotation; + | n.StringLiteralTypeAnnotation + | n.BooleanLiteralTypeAnnotation + | n.NumericLiteralTypeAnnotation; // Keyword type annotations that both Flow and TS share export type AnySharedKeywordTypeAnnotation = - | n.AnyKeywordTypeAnnotation - | n.BooleanKeywordTypeAnnotation - | n.NumberKeywordTypeAnnotation - | n.StringKeywordTypeAnnotation - | n.BigIntKeywordTypeAnnotation; + | n.AnyKeywordTypeAnnotation + | n.BooleanKeywordTypeAnnotation + | n.NumberKeywordTypeAnnotation + | n.StringKeywordTypeAnnotation + | n.BigIntKeywordTypeAnnotation; export type AnyFlowKeywordTypeAnnotation = - | AnySharedKeywordTypeAnnotation - | n.VoidKeywordTypeAnnotation - | n.MixedKeywordTypeAnnotation - | n.EmptyKeywordTypeAnnotation; + | AnySharedKeywordTypeAnnotation + | n.VoidKeywordTypeAnnotation + | n.MixedKeywordTypeAnnotation + | n.EmptyKeywordTypeAnnotation; export type AnyTSKeywordTypeAnnotation = - | AnySharedKeywordTypeAnnotation - | n.NeverKeywordTypeAnnotation - | n.NumberKeywordTypeAnnotation - | n.ObjectKeywordTypeAnnotation - | n.SymbolKeywordTypeAnnotation - | n.UndefinedKeywordTypeAnnotation - | n.UnknownKeywordTypeAnnotation; + | AnySharedKeywordTypeAnnotation + | n.NeverKeywordTypeAnnotation + | n.NumberKeywordTypeAnnotation + | n.ObjectKeywordTypeAnnotation + | n.SymbolKeywordTypeAnnotation + | n.UndefinedKeywordTypeAnnotation + | n.UnknownKeywordTypeAnnotation; export type AnyTSPrimary = - | n.TSOptionalType - | n.TSTypeLiteral - | n.TSTypeReference - | n.TSThisType - | n.TSParenthesizedType - | n.TSArrayType - | n.TSIndexedAccessType - | n.TSTupleType - | n.TSTypeQuery - | n.TSMappedType - | n.TSTypePredicate - | n.TSTypeOperator - | n.TSInferType - | n.UnionTypeAnnotation - | n.TSConditionalType - | n.TSFunctionType - | n.TSImportType - | n.TSConstructorType - | n.IntersectionTypeAnnotation - | n.TemplateLiteralTypeAnnotation - | AnyLiteralTypeAnnotation - | AnyTSKeywordTypeAnnotation; + | n.TSOptionalType + | n.TSTypeLiteral + | n.TSTypeReference + | n.TSThisType + | n.TSParenthesizedType + | n.TSArrayType + | n.TSIndexedAccessType + | n.TSTupleType + | n.TSTypeQuery + | n.TSMappedType + | n.TSTypePredicate + | n.TSTypeOperator + | n.TSInferType + | n.UnionTypeAnnotation + | n.TSConditionalType + | n.TSFunctionType + | n.TSImportType + | n.TSConstructorType + | n.IntersectionTypeAnnotation + | n.TemplateLiteralTypeAnnotation + | AnyLiteralTypeAnnotation + | AnyTSKeywordTypeAnnotation; export type AnyPrimaryType = AnyFlowPrimary | AnyTSPrimary; export type AnyRegExpEscapedCharacter = - | n.RegExpCharacter - | n.RegExpDigitCharacter - | n.RegExpNonDigitCharacter - | n.RegExpWordBoundaryCharacter - | n.RegExpNamedBackReference - | n.RegExpNonWordBoundaryCharacter - | n.RegExpNumericBackReference - | n.RegExpWhiteSpaceCharacter - | n.RegExpNonWhiteSpaceCharacter - | n.RegExpWordCharacter - | n.RegExpNonWordCharacter - | n.RegExpControlCharacter; + | n.RegExpCharacter + | n.RegExpDigitCharacter + | n.RegExpNonDigitCharacter + | n.RegExpWordBoundaryCharacter + | n.RegExpNamedBackReference + | n.RegExpNonWordBoundaryCharacter + | n.RegExpNumericBackReference + | n.RegExpWhiteSpaceCharacter + | n.RegExpNonWhiteSpaceCharacter + | n.RegExpWordCharacter + | n.RegExpNonWordCharacter + | n.RegExpControlCharacter; export type AnyRegExpBodyItem = - | AnyRegExpEscapedCharacter - | n.RegExpStartCharacter - | n.RegExpEndCharacter - | n.RegExpAnyCharacter - | n.RegExpDigitCharacter - | n.RegExpNonDigitCharacter - | n.RegExpWhiteSpaceCharacter - | n.RegExpNonWhiteSpaceCharacter - | n.RegExpWordCharacter - | n.RegExpNonWordCharacter - | n.RegExpWordBoundaryCharacter - | n.RegExpNonWordBoundaryCharacter - | n.RegExpControlCharacter - | n.RegExpCharacter - | n.RegExpQuantified - | n.RegExpGroupCapture - | n.RegExpCharSet - | n.RegExpGroupNonCapture - | n.RegExpSubExpression - | n.RegExpAlternation; + | AnyRegExpEscapedCharacter + | n.RegExpStartCharacter + | n.RegExpEndCharacter + | n.RegExpAnyCharacter + | n.RegExpDigitCharacter + | n.RegExpNonDigitCharacter + | n.RegExpWhiteSpaceCharacter + | n.RegExpNonWhiteSpaceCharacter + | n.RegExpWordCharacter + | n.RegExpNonWordCharacter + | n.RegExpWordBoundaryCharacter + | n.RegExpNonWordBoundaryCharacter + | n.RegExpControlCharacter + | n.RegExpCharacter + | n.RegExpQuantified + | n.RegExpGroupCapture + | n.RegExpCharSet + | n.RegExpGroupNonCapture + | n.RegExpSubExpression + | n.RegExpAlternation; export type AnyRegExpExpression = n.RegExpSubExpression | n.RegExpAlternation; // EVERYTHING BELOW IS AUTOGENERATED. SEE SCRIPTS FOLDER FOR UPDATE SCRIPTS export type AnyNode = - | n.AmbiguousFlowTypeCastExpression - | n.AnyKeywordTypeAnnotation - | n.ArrayExpression - | n.ArrayHole - | n.ArrowFunctionExpression - | n.AssignmentArrayPattern - | n.AssignmentAssignmentPattern - | n.AssignmentExpression - | n.AssignmentIdentifier - | n.AssignmentObjectPattern - | n.AssignmentObjectPatternProperty - | n.AwaitExpression - | n.BigIntKeywordTypeAnnotation - | n.BigIntLiteral - | n.BinaryExpression - | n.BindingArrayPattern - | n.BindingAssignmentPattern - | n.BindingIdentifier - | n.BindingObjectPattern - | n.BindingObjectPatternProperty - | n.BlockStatement - | n.BooleanKeywordTypeAnnotation - | n.BooleanLiteral - | n.BooleanLiteralTypeAnnotation - | n.BreakStatement - | n.CallExpression - | n.CatchClause - | n.ClassDeclaration - | n.ClassExpression - | n.ClassHead - | n.ClassMethod - | n.ClassPrivateMethod - | n.ClassPrivateProperty - | n.ClassProperty - | n.ClassPropertyMeta - | n.CommentBlock - | n.CommentLine - | n.ComputedMemberProperty - | n.ComputedPropertyKey - | n.ConditionalExpression - | n.ContinueStatement - | n.DebuggerStatement - | n.Directive - | n.DoExpression - | n.DoWhileStatement - | n.EmptyKeywordTypeAnnotation - | n.EmptyStatement - | n.ExportAllDeclaration - | n.ExportDefaultDeclaration - | n.ExportDefaultSpecifier - | n.ExportExternalDeclaration - | n.ExportExternalSpecifier - | n.ExportLocalDeclaration - | n.ExportLocalSpecifier - | n.ExportNamespaceSpecifier - | n.ExpressionStatement - | n.ForInStatement - | n.ForOfStatement - | n.ForStatement - | n.FunctionDeclaration - | n.FunctionExpression - | n.FunctionHead - | n.Identifier - | n.IfStatement - | n.ImportCall - | n.ImportDeclaration - | n.ImportDefaultSpecifier - | n.ImportNamespaceSpecifier - | n.ImportSpecifier - | n.ImportSpecifierLocal - | n.InterpreterDirective - | n.IntersectionTypeAnnotation - | n.JSXAttribute - | n.JSXElement - | n.JSXEmptyExpression - | n.JSXExpressionContainer - | n.JSXFragment - | n.JSXIdentifier - | n.JSXMemberExpression - | n.JSXNamespacedName - | n.JSXReferenceIdentifier - | n.JSXSpreadAttribute - | n.JSXSpreadChild - | n.JSXText - | n.LabeledStatement - | n.LogicalExpression - | n.MemberExpression - | n.MetaProperty - | n.MixedKeywordTypeAnnotation - | n.MockParent - | n.NeverKeywordTypeAnnotation - | n.NewExpression - | n.NullKeywordTypeAnnotation - | n.NullLiteral - | n.NumberKeywordTypeAnnotation - | n.NumericLiteral - | n.NumericLiteralTypeAnnotation - | n.ObjectExpression - | n.ObjectKeywordTypeAnnotation - | n.ObjectMethod - | n.ObjectProperty - | n.OptionalCallExpression - | n.PatternMeta - | n.PrivateName - | n.Program - | n.ReferenceIdentifier - | n.RegExpAlternation - | n.RegExpAnyCharacter - | n.RegExpCharacter - | n.RegExpCharSet - | n.RegExpCharSetRange - | n.RegExpControlCharacter - | n.RegExpDigitCharacter - | n.RegExpEndCharacter - | n.RegExpGroupCapture - | n.RegExpGroupNonCapture - | n.RegExpLiteral - | n.RegExpNamedBackReference - | n.RegExpNonDigitCharacter - | n.RegExpNonWhiteSpaceCharacter - | n.RegExpNonWordBoundaryCharacter - | n.RegExpNonWordCharacter - | n.RegExpNumericBackReference - | n.RegExpQuantified - | n.RegExpStartCharacter - | n.RegExpSubExpression - | n.RegExpWhiteSpaceCharacter - | n.RegExpWordBoundaryCharacter - | n.RegExpWordCharacter - | n.ReturnStatement - | n.SequenceExpression - | n.SpreadElement - | n.SpreadProperty - | n.StaticMemberProperty - | n.StaticPropertyKey - | n.StringKeywordTypeAnnotation - | n.StringLiteral - | n.StringLiteralTypeAnnotation - | n.Super - | n.SwitchCase - | n.SwitchStatement - | n.SymbolKeywordTypeAnnotation - | n.TaggedTemplateExpression - | n.TemplateElement - | n.TemplateLiteral - | n.TemplateLiteralTypeAnnotation - | n.ThisExpression - | n.ThrowStatement - | n.TryStatement - | n.TSArrayType - | n.TSAsExpression - | n.TSAssignmentAsExpression - | n.TSAssignmentNonNullExpression - | n.TSAssignmentTypeAssertion - | n.TSCallSignatureDeclaration - | n.TSConditionalType - | n.TSConstructorType - | n.TSConstructSignatureDeclaration - | n.TSDeclareFunction - | n.TSDeclareMethod - | n.TSEnumDeclaration - | n.TSEnumMember - | n.TSExportAssignment - | n.TSExpressionWithTypeArguments - | n.TSExternalModuleReference - | n.TSFunctionType - | n.TSImportEqualsDeclaration - | n.TSImportType - | n.TSIndexedAccessType - | n.TSIndexSignature - | n.TSInferType - | n.TSInterfaceBody - | n.TSInterfaceDeclaration - | n.TSMappedType - | n.TSMethodSignature - | n.TSModuleBlock - | n.TSModuleDeclaration - | n.TSNamespaceExportDeclaration - | n.TSNonNullExpression - | n.TSOptionalType - | n.TSParenthesizedType - | n.TSPropertySignature - | n.TSQualifiedName - | n.TSSignatureDeclarationMeta - | n.TSThisType - | n.TSTupleType - | n.TSTypeAssertion - | n.TSTypeLiteral - | n.TSTypeOperator - | n.TSTypeParameter - | n.TSTypeParameterDeclaration - | n.TSTypeParameterInstantiation - | n.TSTypePredicate - | n.TSTypeQuery - | n.TSTypeReference - | n.TypeAliasTypeAnnotation - | n.UnaryExpression - | n.UndefinedKeywordTypeAnnotation - | n.UnionTypeAnnotation - | n.UnknownKeywordTypeAnnotation - | n.UpdateExpression - | n.VariableDeclaration - | n.VariableDeclarationStatement - | n.VariableDeclarator - | n.VoidKeywordTypeAnnotation - | n.WhileStatement - | n.WithStatement - | n.YieldExpression; + | n.AmbiguousFlowTypeCastExpression + | n.AnyKeywordTypeAnnotation + | n.ArrayExpression + | n.ArrayHole + | n.ArrowFunctionExpression + | n.AssignmentArrayPattern + | n.AssignmentAssignmentPattern + | n.AssignmentExpression + | n.AssignmentIdentifier + | n.AssignmentObjectPattern + | n.AssignmentObjectPatternProperty + | n.AwaitExpression + | n.BigIntKeywordTypeAnnotation + | n.BigIntLiteral + | n.BinaryExpression + | n.BindingArrayPattern + | n.BindingAssignmentPattern + | n.BindingIdentifier + | n.BindingObjectPattern + | n.BindingObjectPatternProperty + | n.BlockStatement + | n.BooleanKeywordTypeAnnotation + | n.BooleanLiteral + | n.BooleanLiteralTypeAnnotation + | n.BreakStatement + | n.CallExpression + | n.CatchClause + | n.ClassDeclaration + | n.ClassExpression + | n.ClassHead + | n.ClassMethod + | n.ClassPrivateMethod + | n.ClassPrivateProperty + | n.ClassProperty + | n.ClassPropertyMeta + | n.CommentBlock + | n.CommentLine + | n.ComputedMemberProperty + | n.ComputedPropertyKey + | n.ConditionalExpression + | n.ContinueStatement + | n.DebuggerStatement + | n.Directive + | n.DoExpression + | n.DoWhileStatement + | n.EmptyKeywordTypeAnnotation + | n.EmptyStatement + | n.ExportAllDeclaration + | n.ExportDefaultDeclaration + | n.ExportDefaultSpecifier + | n.ExportExternalDeclaration + | n.ExportExternalSpecifier + | n.ExportLocalDeclaration + | n.ExportLocalSpecifier + | n.ExportNamespaceSpecifier + | n.ExpressionStatement + | n.ForInStatement + | n.ForOfStatement + | n.ForStatement + | n.FunctionDeclaration + | n.FunctionExpression + | n.FunctionHead + | n.Identifier + | n.IfStatement + | n.ImportCall + | n.ImportDeclaration + | n.ImportDefaultSpecifier + | n.ImportNamespaceSpecifier + | n.ImportSpecifier + | n.ImportSpecifierLocal + | n.InterpreterDirective + | n.IntersectionTypeAnnotation + | n.JSXAttribute + | n.JSXElement + | n.JSXEmptyExpression + | n.JSXExpressionContainer + | n.JSXFragment + | n.JSXIdentifier + | n.JSXMemberExpression + | n.JSXNamespacedName + | n.JSXReferenceIdentifier + | n.JSXSpreadAttribute + | n.JSXSpreadChild + | n.JSXText + | n.LabeledStatement + | n.LogicalExpression + | n.MemberExpression + | n.MetaProperty + | n.MixedKeywordTypeAnnotation + | n.MockParent + | n.NeverKeywordTypeAnnotation + | n.NewExpression + | n.NullKeywordTypeAnnotation + | n.NullLiteral + | n.NumberKeywordTypeAnnotation + | n.NumericLiteral + | n.NumericLiteralTypeAnnotation + | n.ObjectExpression + | n.ObjectKeywordTypeAnnotation + | n.ObjectMethod + | n.ObjectProperty + | n.OptionalCallExpression + | n.PatternMeta + | n.PrivateName + | n.Program + | n.ReferenceIdentifier + | n.RegExpAlternation + | n.RegExpAnyCharacter + | n.RegExpCharacter + | n.RegExpCharSet + | n.RegExpCharSetRange + | n.RegExpControlCharacter + | n.RegExpDigitCharacter + | n.RegExpEndCharacter + | n.RegExpGroupCapture + | n.RegExpGroupNonCapture + | n.RegExpLiteral + | n.RegExpNamedBackReference + | n.RegExpNonDigitCharacter + | n.RegExpNonWhiteSpaceCharacter + | n.RegExpNonWordBoundaryCharacter + | n.RegExpNonWordCharacter + | n.RegExpNumericBackReference + | n.RegExpQuantified + | n.RegExpStartCharacter + | n.RegExpSubExpression + | n.RegExpWhiteSpaceCharacter + | n.RegExpWordBoundaryCharacter + | n.RegExpWordCharacter + | n.ReturnStatement + | n.SequenceExpression + | n.SpreadElement + | n.SpreadProperty + | n.StaticMemberProperty + | n.StaticPropertyKey + | n.StringKeywordTypeAnnotation + | n.StringLiteral + | n.StringLiteralTypeAnnotation + | n.Super + | n.SwitchCase + | n.SwitchStatement + | n.SymbolKeywordTypeAnnotation + | n.TaggedTemplateExpression + | n.TemplateElement + | n.TemplateLiteral + | n.TemplateLiteralTypeAnnotation + | n.ThisExpression + | n.ThrowStatement + | n.TryStatement + | n.TSArrayType + | n.TSAsExpression + | n.TSAssignmentAsExpression + | n.TSAssignmentNonNullExpression + | n.TSAssignmentTypeAssertion + | n.TSCallSignatureDeclaration + | n.TSConditionalType + | n.TSConstructorType + | n.TSConstructSignatureDeclaration + | n.TSDeclareFunction + | n.TSDeclareMethod + | n.TSEnumDeclaration + | n.TSEnumMember + | n.TSExportAssignment + | n.TSExpressionWithTypeArguments + | n.TSExternalModuleReference + | n.TSFunctionType + | n.TSImportEqualsDeclaration + | n.TSImportType + | n.TSIndexedAccessType + | n.TSIndexSignature + | n.TSInferType + | n.TSInterfaceBody + | n.TSInterfaceDeclaration + | n.TSMappedType + | n.TSMethodSignature + | n.TSModuleBlock + | n.TSModuleDeclaration + | n.TSNamespaceExportDeclaration + | n.TSNonNullExpression + | n.TSOptionalType + | n.TSParenthesizedType + | n.TSPropertySignature + | n.TSQualifiedName + | n.TSSignatureDeclarationMeta + | n.TSThisType + | n.TSTupleType + | n.TSTypeAssertion + | n.TSTypeLiteral + | n.TSTypeOperator + | n.TSTypeParameter + | n.TSTypeParameterDeclaration + | n.TSTypeParameterInstantiation + | n.TSTypePredicate + | n.TSTypeQuery + | n.TSTypeReference + | n.TypeAliasTypeAnnotation + | n.UnaryExpression + | n.UndefinedKeywordTypeAnnotation + | n.UnionTypeAnnotation + | n.UnknownKeywordTypeAnnotation + | n.UpdateExpression + | n.VariableDeclaration + | n.VariableDeclarationStatement + | n.VariableDeclarator + | n.VoidKeywordTypeAnnotation + | n.WhileStatement + | n.WithStatement + | n.YieldExpression; diff --git a/packages/@romejs/js-ast/utils.ts b/packages/@romejs/js-ast/utils.ts index d3803e37f5a..4545c6a04ac 100644 --- a/packages/@romejs/js-ast/utils.ts +++ b/packages/@romejs/js-ast/utils.ts @@ -16,14 +16,14 @@ export const visitorKeys: Map> = new Map(); export const nodeNames: Set = new Set(); type JustNodeKeysProp = V extends - | NodeBase - | Array - | Array - ? K - : never; + | NodeBase + | Array + | Array + ? K + : never; type JustNodeKeys = ExcludeCoreNodeKeys< - {[K in keyof T]: JustNodeKeysProp>}[keyof T] + {[K in keyof T]: JustNodeKeysProp>}[keyof T] >; type ExcludeCoreNodeKeys = Exclude; @@ -33,109 +33,105 @@ type VisitorKeys = {[K in JustNodeKeys]: true}; type BindingKeys = {[K in JustNodeKeys]?: true}; type CreateBuilderOptions = { - bindingKeys: BindingKeys; - visitorKeys: VisitorKeys; + bindingKeys: BindingKeys; + visitorKeys: VisitorKeys; }; function declareBuilder(type: string, opts: CreateBuilderOptions) { - nodeNames.add(type); + nodeNames.add(type); - if (opts.visitorKeys !== undefined) { - visitorKeys.set(type, Object.keys(opts.visitorKeys)); - } + if (opts.visitorKeys !== undefined) { + visitorKeys.set(type, Object.keys(opts.visitorKeys)); + } - if (opts.bindingKeys !== undefined) { - bindingKeys.set(type, Object.keys(opts.bindingKeys)); - } + if (opts.bindingKeys !== undefined) { + bindingKeys.set(type, Object.keys(opts.bindingKeys)); + } } // TODO only allow this method to be called on a node with only one required property export function createQuickBuilder< - Node extends AnyNode, - QuickKey extends keyof Node + Node extends AnyNode, + QuickKey extends keyof Node >( - type: Node['type'], - quickKey: QuickKey, - opts: CreateBuilderOptions, + type: Node['type'], + quickKey: QuickKey, + opts: CreateBuilderOptions, ): QuickBuilder { - declareBuilder(type, opts); + declareBuilder(type, opts); - return new QuickBuilder(type, opts.visitorKeys, quickKey); + return new QuickBuilder(type, opts.visitorKeys, quickKey); } export function createBuilder( - type: string, - opts: CreateBuilderOptions, + type: string, + opts: CreateBuilderOptions, ): Builder { - declareBuilder(type, opts); + declareBuilder(type, opts); - return new Builder(type, opts.visitorKeys); + return new Builder(type, opts.visitorKeys); } class Builder { - constructor(type: string, visitorKeys: VisitorKeys) { - this.type = type; - this.visitorKeys = visitorKeys; - } - - type: string; - visitorKeys: VisitorKeys; - - create(opts: Omit, inheritNode?: AnyNode): Node { - // @ts-ignore - return { - loc: inheritNode === undefined ? undefined : inheritLoc(inheritNode), - ...opts, - type: this.type, - }; - } - - is(node: undefined | AnyNode): node is Node { - return node !== undefined && node.type === this.type; - } - - normalize(node: undefined | AnyNode): undefined | Node { - if (this.is(node)) { - return node; - } else { - return undefined; - } - } - - assert(res: undefined | TransformExitResult): Node { - if (res === undefined) { - throw new Error(`Expected ${this.type} Node but got undefined`); - } - - const node = assertSingleNode(res); - - if (node.type !== this.type) { - throw new Error(`Expected ${this.type} Node but got ${node.type}`); - } - - // @ts-ignore - return node; - } + constructor(type: string, visitorKeys: VisitorKeys) { + this.type = type; + this.visitorKeys = visitorKeys; + } + + type: string; + visitorKeys: VisitorKeys; + + create(opts: Omit, inheritNode?: AnyNode): Node { + // @ts-ignore + return { + loc: inheritNode === undefined ? undefined : inheritLoc(inheritNode), + ...opts, + type: this.type, + }; + } + + is(node: undefined | AnyNode): node is Node { + return node !== undefined && node.type === this.type; + } + + normalize(node: undefined | AnyNode): undefined | Node { + if (this.is(node)) { + return node; + } else { + return undefined; + } + } + + assert(res: undefined | TransformExitResult): Node { + if (res === undefined) { + throw new Error(`Expected ${this.type} Node but got undefined`); + } + + const node = assertSingleNode(res); + + if (node.type !== this.type) { + throw new Error(`Expected ${this.type} Node but got ${node.type}`); + } + + // @ts-ignore + return node; + } } class QuickBuilder extends Builder { - constructor( - type: string, - visitorKeys: VisitorKeys, - quickKey: keyof Node, - ) { - super(type, visitorKeys); - this.quickKey = quickKey; - } - - quickKey: keyof Node; - - quick(arg: Arg, opts?: Partial>, inheritNode?: Node): Node { - const node = ({ - ...opts, - [this.quickKey]: arg, - } as Node); - - return this.create(node, inheritNode); - } + constructor(type: string, visitorKeys: VisitorKeys, quickKey: keyof Node) { + super(type, visitorKeys); + this.quickKey = quickKey; + } + + quickKey: keyof Node; + + quick(arg: Arg, opts?: Partial>, inheritNode?: Node): Node { + const node = ({ + ...opts, + [this.quickKey]: arg, + } as Node); + + return this.create(node, inheritNode); + } } diff --git a/packages/@romejs/js-compiler/api/analyzeDependencies.test.md b/packages/@romejs/js-compiler/api/analyzeDependencies.test.md index 322aaf7fea5..8e401efbcd3 100644 --- a/packages/@romejs/js-compiler/api/analyzeDependencies.test.md +++ b/packages/@romejs/js-compiler/api/analyzeDependencies.test.md @@ -6,80 +6,80 @@ ```javascript Object { - dependencies: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object {} - exports: Array [ - local { - kind: 'value' - name: 'foo' - valueType: 'other' - loc: Object { - filename: 'unknown' - end: Object { - column: 19 - index: 19 - line: 1 - } - start: Object { - column: 14 - index: 14 - line: 1 - } - } - } - ] - diagnostics: Array [ - Object { - origins: Array [Object {category: 'analyzeDependencies'}] - location: Object { - filename: 'unknown' - language: 'js' - marker: undefined - mtime: undefined - sourceType: 'module' - end: Object { - column: 19 - index: 19 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - description: Object { - category: 'analyzeDependencies/cjsExportInES' - message: PARTIAL_BLESSED_DIAGNOSTIC_MESSAGE {value: 'You cannot use CommonJS exports in an ES module'} - advice: Array [ - action { - command: 'lint' - extra: undefined - hidden: true - instruction: 'To suppress this error run' - noun: 'Add suppression comment' - shortcut: 's' - args: Array ['unknown'] - commandFlags: Object {decisions: Array ['suppress-analyzeDependencies/cjsExportInES-unknown-1']} - } - action { - args: Array [] - command: 'lint' - extra: true - hidden: true - instruction: 'To add suppression comments for ALL files with this category run' - noun: 'Add suppression comments for ALL files with this category' - shortcut: undefined - commandFlags: Object {decisions: Array ['global-suppress-analyzeDependencies/cjsExportInES']} - } - ] - } - } - ] + dependencies: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object {} + exports: Array [ + local { + kind: 'value' + name: 'foo' + valueType: 'other' + loc: Object { + filename: 'unknown' + end: Object { + column: 19 + index: 19 + line: 1 + } + start: Object { + column: 14 + index: 14 + line: 1 + } + } + } + ] + diagnostics: Array [ + Object { + origins: Array [Object {category: 'analyzeDependencies'}] + location: Object { + filename: 'unknown' + language: 'js' + marker: undefined + mtime: undefined + sourceType: 'module' + end: Object { + column: 19 + index: 19 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + description: Object { + category: 'analyzeDependencies/cjsExportInES' + message: PARTIAL_BLESSED_DIAGNOSTIC_MESSAGE {value: 'You cannot use CommonJS exports in an ES module'} + advice: Array [ + action { + command: 'lint' + extra: undefined + hidden: true + instruction: 'To suppress this error run' + noun: 'Add suppression comment' + shortcut: 's' + args: Array ['unknown'] + commandFlags: Object {decisions: Array ['suppress-analyzeDependencies/cjsExportInES-unknown-1']} + } + action { + args: Array [] + command: 'lint' + extra: true + hidden: true + instruction: 'To add suppression comments for ALL files with this category run' + noun: 'Add suppression comments for ALL files with this category' + shortcut: undefined + commandFlags: Object {decisions: Array ['global-suppress-analyzeDependencies/cjsExportInES']} + } + ] + } + } + ] } ``` @@ -87,49 +87,49 @@ Object { ```javascript Object { - dependencies: Array [] - diagnostics: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object { - foo: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - } - exports: Array [ - local { - kind: 'value' - name: 'foo' - valueType: 'other' - loc: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - } - ] + dependencies: Array [] + diagnostics: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object { + foo: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + } + exports: Array [ + local { + kind: 'value' + name: 'foo' + valueType: 'other' + loc: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + } + ] } ``` @@ -137,36 +137,36 @@ Object { ```javascript Object { - diagnostics: Array [] - exports: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object {} - dependencies: Array [ - es { - kind: 'value' - all: false - async: false - names: Array [] - optional: false - source: 'bar' - loc: Object { - filename: 'unknown' - end: Object { - column: 12 - index: 12 - line: 1 - } - start: Object { - column: 7 - index: 7 - line: 1 - } - } - } - ] + diagnostics: Array [] + exports: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object {} + dependencies: Array [ + es { + kind: 'value' + all: false + async: false + names: Array [] + optional: false + source: 'bar' + loc: Object { + filename: 'unknown' + end: Object { + column: 12 + index: 12 + line: 1 + } + start: Object { + column: 7 + index: 7 + line: 1 + } + } + } + ] } ``` @@ -174,14 +174,14 @@ Object { ```javascript Object { - dependencies: Array [] - diagnostics: Array [] - exports: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object {} + dependencies: Array [] + diagnostics: Array [] + exports: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object {} } ``` @@ -189,82 +189,82 @@ Object { ```javascript Object { - diagnostics: Array [] - exports: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object { - bar: Object { - filename: 'unknown' - identifierName: 'bar' - end: Object { - column: 11 - index: 11 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - foo: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 9 - index: 34 - line: 2 - } - start: Object { - column: 6 - index: 31 - line: 2 - } - } - } - dependencies: Array [ - es { - kind: 'value' - all: false - async: false - optional: false - source: 'foo' - loc: Object { - filename: 'unknown' - end: Object { - column: 23 - index: 23 - line: 1 - } - start: Object { - column: 18 - index: 18 - line: 1 - } - } - names: Array [ - value { - name: 'bar' - loc: Object { - filename: 'unknown' - end: Object { - column: 11 - index: 11 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - ] - } - ] + diagnostics: Array [] + exports: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object { + bar: Object { + filename: 'unknown' + identifierName: 'bar' + end: Object { + column: 11 + index: 11 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + foo: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 9 + index: 34 + line: 2 + } + start: Object { + column: 6 + index: 31 + line: 2 + } + } + } + dependencies: Array [ + es { + kind: 'value' + all: false + async: false + optional: false + source: 'foo' + loc: Object { + filename: 'unknown' + end: Object { + column: 23 + index: 23 + line: 1 + } + start: Object { + column: 18 + index: 18 + line: 1 + } + } + names: Array [ + value { + name: 'bar' + loc: Object { + filename: 'unknown' + end: Object { + column: 11 + index: 11 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + ] + } + ] } ``` @@ -272,106 +272,106 @@ Object { ```javascript Object { - dependencies: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'cjs' - syntax: Array [] - topLevelLocalBindings: Object { - foo: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - } - diagnostics: Array [ - Object { - origins: Array [Object {category: 'js-parser'}] - description: Object { - category: 'parse/js' - message: PARTIAL_BLESSED_DIAGNOSTIC_MESSAGE {value: 'import and export can only appear in a module'} - advice: Array [ - log { - category: 'info' - text: 'Change the extension to .mjs to turn this file into a module' - } - log { - category: 'info' - text: 'Add "type": "module" to your ' - } - ] - } - location: Object { - filename: 'unknown' - mtime: undefined - sourceType: 'script' - end: Object { - column: 25 - index: 25 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - } - ] - exports: Array [ - local { - kind: 'value' - name: 'foo' - valueType: 'other' - loc: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - } - local { - kind: 'value' - name: 'bar' - valueType: 'other' - loc: Object { - filename: 'unknown' - end: Object { - column: 19 - index: 45 - line: 2 - } - start: Object { - column: 14 - index: 40 - line: 2 - } - } - } - local { - kind: 'value' - name: 'default' - loc: undefined - valueType: 'other' - } - ] + dependencies: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'cjs' + syntax: Array [] + topLevelLocalBindings: Object { + foo: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + } + diagnostics: Array [ + Object { + origins: Array [Object {category: 'js-parser'}] + description: Object { + category: 'parse/js' + message: PARTIAL_BLESSED_DIAGNOSTIC_MESSAGE {value: 'import and export can only appear in a module'} + advice: Array [ + log { + category: 'info' + text: 'Change the extension to .mjs to turn this file into a module' + } + log { + category: 'info' + text: 'Add "type": "module" to your ' + } + ] + } + location: Object { + filename: 'unknown' + mtime: undefined + sourceType: 'script' + end: Object { + column: 25 + index: 25 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + } + ] + exports: Array [ + local { + kind: 'value' + name: 'foo' + valueType: 'other' + loc: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + } + local { + kind: 'value' + name: 'bar' + valueType: 'other' + loc: Object { + filename: 'unknown' + end: Object { + column: 19 + index: 45 + line: 2 + } + start: Object { + column: 14 + index: 40 + line: 2 + } + } + } + local { + kind: 'value' + name: 'default' + loc: undefined + valueType: 'other' + } + ] } ``` @@ -379,72 +379,72 @@ Object { ```javascript Object { - diagnostics: Array [] - exports: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object { - yes: Object { - filename: 'unknown' - identifierName: 'yes' - end: Object { - column: 12 - index: 30 - line: 3 - } - start: Object { - column: 9 - index: 27 - line: 3 - } - } - } - dependencies: Array [ - es { - kind: 'value' - all: true - async: true - names: Array [] - optional: false - source: './foo' - loc: Object { - filename: 'unknown' - end: Object { - column: 14 - index: 14 - line: 1 - } - start: Object { - column: 7 - index: 7 - line: 1 - } - } - } - es { - kind: 'value' - all: true - async: true - names: Array [] - optional: false - source: './bar' - loc: Object { - filename: 'unknown' - end: Object { - column: 16 - index: 51 - line: 4 - } - start: Object { - column: 9 - index: 44 - line: 4 - } - } - } - ] + diagnostics: Array [] + exports: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object { + yes: Object { + filename: 'unknown' + identifierName: 'yes' + end: Object { + column: 12 + index: 30 + line: 3 + } + start: Object { + column: 9 + index: 27 + line: 3 + } + } + } + dependencies: Array [ + es { + kind: 'value' + all: true + async: true + names: Array [] + optional: false + source: './foo' + loc: Object { + filename: 'unknown' + end: Object { + column: 14 + index: 14 + line: 1 + } + start: Object { + column: 7 + index: 7 + line: 1 + } + } + } + es { + kind: 'value' + all: true + async: true + names: Array [] + optional: false + source: './bar' + loc: Object { + filename: 'unknown' + end: Object { + column: 16 + index: 51 + line: 4 + } + start: Object { + column: 9 + index: 44 + line: 4 + } + } + } + ] } ``` @@ -452,39 +452,39 @@ Object { ```javascript Object { - dependencies: Array [] - diagnostics: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'cjs' - syntax: Array [] - topLevelLocalBindings: Object {} - exports: Array [ - local { - kind: 'value' - name: 'yes' - valueType: 'other' - loc: Object { - filename: 'unknown' - end: Object { - column: 27 - index: 27 - line: 1 - } - start: Object { - column: 14 - index: 14 - line: 1 - } - } - } - local { - kind: 'value' - name: 'default' - loc: undefined - valueType: 'other' - } - ] + dependencies: Array [] + diagnostics: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'cjs' + syntax: Array [] + topLevelLocalBindings: Object {} + exports: Array [ + local { + kind: 'value' + name: 'yes' + valueType: 'other' + loc: Object { + filename: 'unknown' + end: Object { + column: 27 + index: 27 + line: 1 + } + start: Object { + column: 14 + index: 14 + line: 1 + } + } + } + local { + kind: 'value' + name: 'default' + loc: undefined + valueType: 'other' + } + ] } ``` @@ -492,39 +492,39 @@ Object { ```javascript Object { - dependencies: Array [] - diagnostics: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'cjs' - syntax: Array [] - topLevelLocalBindings: Object {} - exports: Array [ - local { - kind: 'value' - name: 'default' - valueType: 'other' - loc: Object { - filename: 'unknown' - end: Object { - column: 30 - index: 30 - line: 1 - } - start: Object { - column: 17 - index: 17 - line: 1 - } - } - } - local { - kind: 'value' - name: 'default' - loc: undefined - valueType: 'other' - } - ] + dependencies: Array [] + diagnostics: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'cjs' + syntax: Array [] + topLevelLocalBindings: Object {} + exports: Array [ + local { + kind: 'value' + name: 'default' + valueType: 'other' + loc: Object { + filename: 'unknown' + end: Object { + column: 30 + index: 30 + line: 1 + } + start: Object { + column: 17 + index: 17 + line: 1 + } + } + } + local { + kind: 'value' + name: 'default' + loc: undefined + valueType: 'other' + } + ] } ``` @@ -532,115 +532,115 @@ Object { ```javascript Object { - dependencies: Array [] - diagnostics: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object { - Bar: Object { - filename: 'unknown' - identifierName: 'Bar' - end: Object { - column: 16 - index: 64 - line: 3 - } - start: Object { - column: 13 - index: 61 - line: 3 - } - } - foo: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 19 - index: 42 - line: 2 - } - start: Object { - column: 16 - index: 39 - line: 2 - } - } - yes: Object { - filename: 'unknown' - identifierName: 'yes' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - } - exports: Array [ - local { - kind: 'value' - name: 'yes' - valueType: 'other' - loc: Object { - filename: 'unknown' - identifierName: 'yes' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - } - local { - kind: 'value' - name: 'foo' - valueType: 'function' - loc: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 19 - index: 42 - line: 2 - } - start: Object { - column: 16 - index: 39 - line: 2 - } - } - } - local { - kind: 'value' - name: 'Bar' - valueType: 'class' - loc: Object { - filename: 'unknown' - identifierName: 'Bar' - end: Object { - column: 16 - index: 64 - line: 3 - } - start: Object { - column: 13 - index: 61 - line: 3 - } - } - } - ] + dependencies: Array [] + diagnostics: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object { + Bar: Object { + filename: 'unknown' + identifierName: 'Bar' + end: Object { + column: 16 + index: 64 + line: 3 + } + start: Object { + column: 13 + index: 61 + line: 3 + } + } + foo: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 19 + index: 42 + line: 2 + } + start: Object { + column: 16 + index: 39 + line: 2 + } + } + yes: Object { + filename: 'unknown' + identifierName: 'yes' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + } + exports: Array [ + local { + kind: 'value' + name: 'yes' + valueType: 'other' + loc: Object { + filename: 'unknown' + identifierName: 'yes' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + } + local { + kind: 'value' + name: 'foo' + valueType: 'function' + loc: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 19 + index: 42 + line: 2 + } + start: Object { + column: 16 + index: 39 + line: 2 + } + } + } + local { + kind: 'value' + name: 'Bar' + valueType: 'class' + loc: Object { + filename: 'unknown' + identifierName: 'Bar' + end: Object { + column: 16 + index: 64 + line: 3 + } + start: Object { + column: 13 + index: 61 + line: 3 + } + } + } + ] } ``` @@ -648,33 +648,33 @@ Object { ```javascript Object { - dependencies: Array [] - diagnostics: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object {} - exports: Array [ - local { - kind: 'value' - name: 'default' - valueType: 'other' - loc: Object { - filename: 'unknown' - end: Object { - column: 20 - index: 20 - line: 1 - } - start: Object { - column: 15 - index: 15 - line: 1 - } - } - } - ] + dependencies: Array [] + diagnostics: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object {} + exports: Array [ + local { + kind: 'value' + name: 'default' + valueType: 'other' + loc: Object { + filename: 'unknown' + end: Object { + column: 20 + index: 20 + line: 1 + } + start: Object { + column: 15 + index: 15 + line: 1 + } + } + } + ] } ``` @@ -682,178 +682,178 @@ Object { ```javascript Object { - diagnostics: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object {} - exports: Array [ - external { - kind: 'value' - exported: 'foo' - imported: 'foo' - source: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 11 - index: 11 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - external { - kind: 'value' - exported: 'bar' - imported: 'bar' - source: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - } - external { - kind: 'value' - exported: 'no' - imported: 'default' - source: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 31 - index: 31 - line: 1 - } - start: Object { - column: 18 - index: 18 - line: 1 - } - } - } - external { - kind: 'value' - exported: 'noo' - imported: 'boo' - source: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 43 - index: 43 - line: 1 - } - start: Object { - column: 33 - index: 33 - line: 1 - } - } - } - ] - dependencies: Array [ - es { - kind: 'value' - all: false - async: false - optional: false - source: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 58 - index: 58 - line: 1 - } - start: Object { - column: 50 - index: 50 - line: 1 - } - } - names: Array [ - value { - name: 'foo' - loc: Object { - filename: 'unknown' - end: Object { - column: 11 - index: 11 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - value { - name: 'bar' - loc: Object { - filename: 'unknown' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - } - value { - name: 'default' - loc: Object { - filename: 'unknown' - end: Object { - column: 31 - index: 31 - line: 1 - } - start: Object { - column: 18 - index: 18 - line: 1 - } - } - } - value { - name: 'boo' - loc: Object { - filename: 'unknown' - end: Object { - column: 43 - index: 43 - line: 1 - } - start: Object { - column: 33 - index: 33 - line: 1 - } - } - } - ] - } - ] + diagnostics: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object {} + exports: Array [ + external { + kind: 'value' + exported: 'foo' + imported: 'foo' + source: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 11 + index: 11 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + external { + kind: 'value' + exported: 'bar' + imported: 'bar' + source: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + } + external { + kind: 'value' + exported: 'no' + imported: 'default' + source: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 31 + index: 31 + line: 1 + } + start: Object { + column: 18 + index: 18 + line: 1 + } + } + } + external { + kind: 'value' + exported: 'noo' + imported: 'boo' + source: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 43 + index: 43 + line: 1 + } + start: Object { + column: 33 + index: 33 + line: 1 + } + } + } + ] + dependencies: Array [ + es { + kind: 'value' + all: false + async: false + optional: false + source: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 58 + index: 58 + line: 1 + } + start: Object { + column: 50 + index: 50 + line: 1 + } + } + names: Array [ + value { + name: 'foo' + loc: Object { + filename: 'unknown' + end: Object { + column: 11 + index: 11 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + value { + name: 'bar' + loc: Object { + filename: 'unknown' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + } + value { + name: 'default' + loc: Object { + filename: 'unknown' + end: Object { + column: 31 + index: 31 + line: 1 + } + start: Object { + column: 18 + index: 18 + line: 1 + } + } + } + value { + name: 'boo' + loc: Object { + filename: 'unknown' + end: Object { + column: 43 + index: 43 + line: 1 + } + start: Object { + column: 33 + index: 33 + line: 1 + } + } + } + ] + } + ] } ``` @@ -861,54 +861,54 @@ Object { ```javascript Object { - diagnostics: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object {} - exports: Array [ - externalAll { - kind: 'value' - source: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 23 - index: 23 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - } - ] - dependencies: Array [ - es { - kind: 'value' - all: true - async: false - names: Array [] - optional: false - source: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 22 - index: 22 - line: 1 - } - start: Object { - column: 14 - index: 14 - line: 1 - } - } - } - ] + diagnostics: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object {} + exports: Array [ + externalAll { + kind: 'value' + source: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 23 + index: 23 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + } + ] + dependencies: Array [ + es { + kind: 'value' + all: true + async: false + names: Array [] + optional: false + source: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 22 + index: 22 + line: 1 + } + start: Object { + column: 14 + index: 14 + line: 1 + } + } + } + ] } ``` @@ -916,68 +916,68 @@ Object { ```javascript Object { - diagnostics: Array [] - exports: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object { - bar: Object { - filename: 'unknown' - identifierName: 'bar' - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 7 - index: 7 - line: 1 - } - } - } - dependencies: Array [ - es { - kind: 'value' - all: false - async: false - optional: false - source: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 24 - index: 24 - line: 1 - } - start: Object { - column: 16 - index: 16 - line: 1 - } - } - names: Array [ - value { - name: 'default' - loc: Object { - filename: 'unknown' - end: Object { - column: 10 - index: 10 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } - } - ] - } - ] + diagnostics: Array [] + exports: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object { + bar: Object { + filename: 'unknown' + identifierName: 'bar' + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 7 + index: 7 + line: 1 + } + } + } + dependencies: Array [ + es { + kind: 'value' + all: false + async: false + optional: false + source: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 24 + index: 24 + line: 1 + } + start: Object { + column: 16 + index: 16 + line: 1 + } + } + names: Array [ + value { + name: 'default' + loc: Object { + filename: 'unknown' + end: Object { + column: 10 + index: 10 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } + } + ] + } + ] } ``` @@ -985,158 +985,158 @@ Object { ```javascript Object { - diagnostics: Array [] - exports: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object { - bar: Object { - filename: 'unknown' - identifierName: 'bar' - end: Object { - column: 11 - index: 11 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - foo: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - lol: Object { - filename: 'unknown' - identifierName: 'lol' - end: Object { - column: 32 - index: 32 - line: 1 - } - start: Object { - column: 29 - index: 29 - line: 1 - } - } - to: Object { - filename: 'unknown' - identifierName: 'to' - end: Object { - column: 42 - index: 42 - line: 1 - } - start: Object { - column: 40 - index: 40 - line: 1 - } - } - } - dependencies: Array [ - es { - kind: 'value' - all: false - async: false - optional: false - source: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 57 - index: 57 - line: 1 - } - start: Object { - column: 49 - index: 49 - line: 1 - } - } - names: Array [ - value { - name: 'bar' - loc: Object { - filename: 'unknown' - end: Object { - column: 11 - index: 11 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - value { - name: 'foo' - loc: Object { - filename: 'unknown' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - } - value { - name: 'default' - loc: Object { - filename: 'unknown' - end: Object { - column: 32 - index: 32 - line: 1 - } - start: Object { - column: 18 - index: 18 - line: 1 - } - } - } - value { - name: 'ya' - loc: Object { - filename: 'unknown' - end: Object { - column: 42 - index: 42 - line: 1 - } - start: Object { - column: 34 - index: 34 - line: 1 - } - } - } - ] - } - ] + diagnostics: Array [] + exports: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object { + bar: Object { + filename: 'unknown' + identifierName: 'bar' + end: Object { + column: 11 + index: 11 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + foo: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + lol: Object { + filename: 'unknown' + identifierName: 'lol' + end: Object { + column: 32 + index: 32 + line: 1 + } + start: Object { + column: 29 + index: 29 + line: 1 + } + } + to: Object { + filename: 'unknown' + identifierName: 'to' + end: Object { + column: 42 + index: 42 + line: 1 + } + start: Object { + column: 40 + index: 40 + line: 1 + } + } + } + dependencies: Array [ + es { + kind: 'value' + all: false + async: false + optional: false + source: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 57 + index: 57 + line: 1 + } + start: Object { + column: 49 + index: 49 + line: 1 + } + } + names: Array [ + value { + name: 'bar' + loc: Object { + filename: 'unknown' + end: Object { + column: 11 + index: 11 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + value { + name: 'foo' + loc: Object { + filename: 'unknown' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + } + value { + name: 'default' + loc: Object { + filename: 'unknown' + end: Object { + column: 32 + index: 32 + line: 1 + } + start: Object { + column: 18 + index: 18 + line: 1 + } + } + } + value { + name: 'ya' + loc: Object { + filename: 'unknown' + end: Object { + column: 42 + index: 42 + line: 1 + } + start: Object { + column: 34 + index: 34 + line: 1 + } + } + } + ] + } + ] } ``` @@ -1144,36 +1144,36 @@ Object { ```javascript Object { - diagnostics: Array [] - exports: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object {} - dependencies: Array [ - es { - kind: 'value' - all: false - async: false - names: Array [] - optional: false - source: 'foobar' - loc: Object { - filename: 'unknown' - end: Object { - column: 29 - index: 29 - line: 1 - } - start: Object { - column: 21 - index: 21 - line: 1 - } - } - } - ] + diagnostics: Array [] + exports: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object {} + dependencies: Array [ + es { + kind: 'value' + all: false + async: false + names: Array [] + optional: false + source: 'foobar' + loc: Object { + filename: 'unknown' + end: Object { + column: 29 + index: 29 + line: 1 + } + start: Object { + column: 21 + index: 21 + line: 1 + } + } + } + ] } ``` @@ -1181,72 +1181,72 @@ Object { ```javascript Object { - dependencies: Array [] - diagnostics: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object {} - exports: Array [ - local { - kind: 'value' - name: 'foo' - valueType: 'other' - loc: Object { - filename: 'unknown' - identifierName: 'foo' - end: Object { - column: 11 - index: 11 - line: 1 - } - start: Object { - column: 8 - index: 8 - line: 1 - } - } - } - local { - kind: 'value' - name: 'bar' - valueType: 'other' - loc: Object { - filename: 'unknown' - identifierName: 'bar' - end: Object { - column: 16 - index: 16 - line: 1 - } - start: Object { - column: 13 - index: 13 - line: 1 - } - } - } - local { - kind: 'value' - name: 'no' - valueType: 'other' - loc: Object { - filename: 'unknown' - identifierName: 'yes' - end: Object { - column: 21 - index: 21 - line: 1 - } - start: Object { - column: 18 - index: 18 - line: 1 - } - } - } - ] + dependencies: Array [] + diagnostics: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object {} + exports: Array [ + local { + kind: 'value' + name: 'foo' + valueType: 'other' + loc: Object { + filename: 'unknown' + identifierName: 'foo' + end: Object { + column: 11 + index: 11 + line: 1 + } + start: Object { + column: 8 + index: 8 + line: 1 + } + } + } + local { + kind: 'value' + name: 'bar' + valueType: 'other' + loc: Object { + filename: 'unknown' + identifierName: 'bar' + end: Object { + column: 16 + index: 16 + line: 1 + } + start: Object { + column: 13 + index: 13 + line: 1 + } + } + } + local { + kind: 'value' + name: 'no' + valueType: 'other' + loc: Object { + filename: 'unknown' + identifierName: 'yes' + end: Object { + column: 21 + index: 21 + line: 1 + } + start: Object { + column: 18 + index: 18 + line: 1 + } + } + } + ] } ``` @@ -1254,72 +1254,72 @@ Object { ```javascript Object { - diagnostics: Array [] - exports: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object { - yeah: Object { - filename: 'unknown' - identifierName: 'yeah' - end: Object { - column: 13 - index: 42 - line: 3 - } - start: Object { - column: 9 - index: 38 - line: 3 - } - } - } - dependencies: Array [ - es { - kind: 'value' - all: false - async: false - names: Array [] - optional: false - source: 'foo' - loc: Object { - filename: 'unknown' - end: Object { - column: 26 - index: 26 - line: 1 - } - start: Object { - column: 21 - index: 21 - line: 1 - } - } - } - cjs { - kind: 'value' - all: true - async: false - names: Array [] - optional: false - source: 'bar' - loc: Object { - filename: 'unknown' - end: Object { - column: 16 - index: 63 - line: 4 - } - start: Object { - column: 2 - index: 49 - line: 4 - } - } - } - ] + diagnostics: Array [] + exports: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object { + yeah: Object { + filename: 'unknown' + identifierName: 'yeah' + end: Object { + column: 13 + index: 42 + line: 3 + } + start: Object { + column: 9 + index: 38 + line: 3 + } + } + } + dependencies: Array [ + es { + kind: 'value' + all: false + async: false + names: Array [] + optional: false + source: 'foo' + loc: Object { + filename: 'unknown' + end: Object { + column: 26 + index: 26 + line: 1 + } + start: Object { + column: 21 + index: 21 + line: 1 + } + } + } + cjs { + kind: 'value' + all: true + async: false + names: Array [] + optional: false + source: 'bar' + loc: Object { + filename: 'unknown' + end: Object { + column: 16 + index: 63 + line: 4 + } + start: Object { + column: 2 + index: 49 + line: 4 + } + } + } + ] } ``` @@ -1327,26 +1327,26 @@ Object { ```javascript Object { - dependencies: Array [] - diagnostics: Array [] - exports: Array [] - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object {} - firstTopAwaitLocation: Object { - filename: 'unknown' - end: Object { - column: 14 - index: 14 - line: 1 - } - start: Object { - column: 0 - index: 0 - line: 1 - } - } + dependencies: Array [] + diagnostics: Array [] + exports: Array [] + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object {} + firstTopAwaitLocation: Object { + filename: 'unknown' + end: Object { + column: 14 + index: 14 + line: 1 + } + start: Object { + column: 0 + index: 0 + line: 1 + } + } } ``` @@ -1354,36 +1354,36 @@ Object { ```javascript Object { - dependencies: Array [] - diagnostics: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'cjs' - syntax: Array [] - exports: Array [ - local { - kind: 'value' - name: 'default' - loc: undefined - valueType: 'other' - } - ] - topLevelLocalBindings: Object { - yes: Object { - filename: 'unknown' - identifierName: 'yes' - end: Object { - column: 12 - index: 59 - line: 6 - } - start: Object { - column: 9 - index: 56 - line: 6 - } - } - } + dependencies: Array [] + diagnostics: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'cjs' + syntax: Array [] + exports: Array [ + local { + kind: 'value' + name: 'default' + loc: undefined + valueType: 'other' + } + ] + topLevelLocalBindings: Object { + yes: Object { + filename: 'unknown' + identifierName: 'yes' + end: Object { + column: 12 + index: 59 + line: 6 + } + start: Object { + column: 9 + index: 56 + line: 6 + } + } + } } ``` @@ -1391,13 +1391,13 @@ Object { ```javascript Object { - dependencies: Array [] - diagnostics: Array [] - exports: Array [] - firstTopAwaitLocation: undefined - importFirstUsage: Array [] - moduleType: 'es' - syntax: Array [] - topLevelLocalBindings: Object {} + dependencies: Array [] + diagnostics: Array [] + exports: Array [] + firstTopAwaitLocation: undefined + importFirstUsage: Array [] + moduleType: 'es' + syntax: Array [] + topLevelLocalBindings: Object {} } ``` diff --git a/packages/@romejs/js-compiler/api/analyzeDependencies.test.ts b/packages/@romejs/js-compiler/api/analyzeDependencies.test.ts index 1dfc4304dee..a246f21a09b 100644 --- a/packages/@romejs/js-compiler/api/analyzeDependencies.test.ts +++ b/packages/@romejs/js-compiler/api/analyzeDependencies.test.ts @@ -14,55 +14,55 @@ import {createUnknownFilePath} from '@romejs/path'; import {dedent} from '@romejs/string-utils'; async function testAnalyzeDeps(input: string, sourceType: ConstSourceType) { - return await analyzeDependencies({ - options: {}, - ast: parseJS({input, sourceType, path: createUnknownFilePath('unknown')}), - sourceText: input, - project: { - folder: undefined, - config: DEFAULT_PROJECT_CONFIG, - }, - }); + return await analyzeDependencies({ + options: {}, + ast: parseJS({input, sourceType, path: createUnknownFilePath('unknown')}), + sourceText: input, + project: { + folder: undefined, + config: DEFAULT_PROJECT_CONFIG, + }, + }); } test( - "discovers require('module') call", - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + "discovers require('module') call", + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` import * as foo from 'foo'; function yeah() { require('bar'); } `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'ignores require(dynamic) call', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'ignores require(dynamic) call', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` require(dynamic); `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'ignores require() call if shadowed', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'ignores require() call if shadowed', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` { function require() {} require('yes'); @@ -73,268 +73,268 @@ test( require('yes'); } `, - 'script', - ), - ); - }, + 'script', + ), + ); + }, ); test( - "discovers async import('foo')", - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + "discovers async import('foo')", + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` import('./foo'); function yes() { import('./bar'); } `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'discovers local export specifiers', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers local export specifiers', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` export {foo, bar, yes as no}; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'discovers export declarations', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers export declarations', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` export const yes = ''; export function foo() {} export class Bar {} `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'discovers export default', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers export default', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` export default 'yes'; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'discovers export from', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers export from', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` export {foo, bar, default as no, boo as noo} from 'foobar'; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'discovers export star', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers export star', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` export * from 'foobar'; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'discovers import star', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers import star', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` import * as bar from 'foobar'; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'discovers import specifiers', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers import specifiers', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` import {bar, foo, default as lol, ya as to} from 'foobar'; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'discovers import default', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers import default', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` import bar from 'foobar'; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'discovers commonjs exports', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers commonjs exports', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` exports.yes = function() {}; `, - 'script', - ), - ); - }, + 'script', + ), + ); + }, ); test( - 'discovers commonjs module.exports', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers commonjs module.exports', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` module.exports = function() {}; `, - 'script', - ), - ); - }, + 'script', + ), + ); + }, ); test( - 'discovers top level await', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'discovers top level await', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` await foobar(); `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'correctly identifies a file with es imports as es', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'correctly identifies a file with es imports as es', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` import 'bar'; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'correctly identifies a file with es exports as es', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'correctly identifies a file with es exports as es', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` export const foo = 'bar'; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'correctly identifies a file with cjs exports as cjs', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'correctly identifies a file with cjs exports as cjs', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` exports.foo = 'bar'; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'correctly identifies a file with no imports or exports as unknown', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'correctly identifies a file with no imports or exports as unknown', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` foo(); `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); test( - 'disallow mix of es and cjs exports', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'disallow mix of es and cjs exports', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` export const foo = 'bar'; exports.bar = 'foo'; `, - 'script', - ), - ); - }, + 'script', + ), + ); + }, ); test( - 'defines topLevelLocalBindings', - async (t) => { - t.snapshot( - await testAnalyzeDeps( - dedent` + 'defines topLevelLocalBindings', + async (t) => { + t.snapshot( + await testAnalyzeDeps( + dedent` import {bar} from 'foo'; const foo = 'bar'; `, - 'module', - ), - ); - }, + 'module', + ), + ); + }, ); diff --git a/packages/@romejs/js-compiler/api/analyzeDependencies/index.ts b/packages/@romejs/js-compiler/api/analyzeDependencies/index.ts index 856b074323a..3ecf52fb2cb 100644 --- a/packages/@romejs/js-compiler/api/analyzeDependencies/index.ts +++ b/packages/@romejs/js-compiler/api/analyzeDependencies/index.ts @@ -9,297 +9,297 @@ import {AnyNode, ConstImportModuleKind} from '@romejs/js-ast'; import {SourceLocation} from '@romejs/parser-core'; import {TransformRequest} from '../../types'; import { - CJSExportRecord, - CJSVarRefRecord, - ESExportRecord, - EscapedCJSRefRecord, - ExportRecord, - ImportRecord, - ImportUsageRecord, - TopLevelAwaitRecord, + CJSExportRecord, + CJSVarRefRecord, + ESExportRecord, + EscapedCJSRefRecord, + ExportRecord, + ImportRecord, + ImportUsageRecord, + TopLevelAwaitRecord, } from './records'; import {Cache, CompilerContext} from '@romejs/js-compiler'; import transform from '../../methods/transform'; import visitors from './visitors/index'; import { - AnalyzeDependency, - AnalyzeDependencyImportFirstUsage, - AnalyzeDependencyName, - AnalyzeDependencyResult, - AnalyzeDependencyTopLevelLocalBindings, - AnalyzeModuleType, - AnyAnalyzeExport, + AnalyzeDependency, + AnalyzeDependencyImportFirstUsage, + AnalyzeDependencyName, + AnalyzeDependencyResult, + AnalyzeDependencyTopLevelLocalBindings, + AnalyzeModuleType, + AnyAnalyzeExport, } from '@romejs/core'; import {descriptions} from '@romejs/diagnostics'; const analyzeCache: Cache = new Cache(); export default async function analyzeDependencies( - req: TransformRequest, + req: TransformRequest, ): Promise { - let {ast, project} = req; - - const query = Cache.buildQuery(req); - const cached: undefined | AnalyzeDependencyResult = analyzeCache.get(query); - if (cached) { - return cached; - } - - const context = new CompilerContext({ - ref: req.ref, - sourceText: req.sourceText, - ast, - project, - origin: { - category: 'analyzeDependencies', - }, - }); - ({ast} = await transform({...req, stage: 'pre'})); - context.reduce(ast, visitors); - - // - const importFirstUsage: AnalyzeDependencyImportFirstUsage = []; - const seenImportFirstUsage: Set = new Set(); - - // Extract records - const exports: Array = []; - const dependenciesBySource: Map = new Map(); - - const esValueExports: Array = []; - const cjsExports: Array = []; - let firstTopAwaitLocation: undefined | SourceLocation; - - // TODO description - let hasCJSRef = false; - - // Whether we have a default export, used to automatically add one for CJS - let hasDefaultExport = false; - - // Find the import sources that are only used as a type - const sourcesUsedAsType: Set = new Set(); - const sourcesUsedAsValue: Set = new Set(); - for (const record of context.records) { - let data; - - if (record instanceof ImportUsageRecord) { - data = record.data; - } - - // This has to be a separate if or else TS wont refine it... - if (record instanceof ExportRecord && record.data.type !== 'local') { - data = record.data; - } - - if (data !== undefined) { - const {kind, source} = data; - if (kind === 'type') { - sourcesUsedAsType.add(source); - } else { - sourcesUsedAsValue.add(source); - } - } - } - for (const source of sourcesUsedAsValue) { - sourcesUsedAsType.delete(source); - } - - // Process rest of the records - for (const record of context.records) { - if (record instanceof EscapedCJSRefRecord) { - exports.push({ - type: 'local', - loc: record.node.loc, - kind: 'value', - valueType: 'other', - name: '*', - }); - } - - if (record instanceof ImportRecord) { - let {data} = record; - - // If this source was only ever used as a type then convert us to a value - if ( - data.type === 'es' && - data.kind === 'value' && - sourcesUsedAsType.has(data.source) - ) { - const names: Array = []; - - for (const name of data.names) { - names.push({ - ...name, - kind: 'type', - }); - } - - data = {...data, kind: 'type', names}; - } - - // If we have multiple import records for this file, then merge them together - const existing = dependenciesBySource.get(data.source); - if (existing === undefined) { - dependenciesBySource.set(data.source, data); - } else { - let kind: ConstImportModuleKind; - if (data.kind === existing.kind) { - kind = data.kind; - } else { - kind = 'value'; - } - - const combinedRecord: AnalyzeDependency = { - type: data.type === 'es' && existing.type === 'es' ? 'es' : 'cjs', - kind, - optional: existing.optional && data.optional, - async: existing.async || data.async, - source: data.source, - all: existing.all || data.all, - names: [...existing.names, ...data.names], - loc: existing.loc || data.loc, - }; - - // Map ordering is by insertion time, so in the case where the previous import was a type import - // then we don't want to place our combined record in that position, it should be at the end. - // Inserting a type import statement at the top of the file shouldn't change the execution order - // if it was imported later - if (existing.kind === 'type' && data.kind === 'value') { - dependenciesBySource.delete(data.source); - } - - dependenciesBySource.set(data.source, combinedRecord); - } - } else if (record instanceof ExportRecord) { - exports.push(record.data); - } else if (record instanceof CJSVarRefRecord) { - hasCJSRef = true; - } else if (record instanceof CJSExportRecord) { - cjsExports.push(record.node); - } else if (record instanceof ESExportRecord) { - // No point checking for ES imported in CJS because it would have been a syntax error - if (record.kind === 'value') { - esValueExports.push(record.node); - } - } else if (record instanceof TopLevelAwaitRecord) { - if (firstTopAwaitLocation === undefined) { - firstTopAwaitLocation = record.loc; - } - } else if ( - record instanceof ImportUsageRecord && - record.isTop && - record.data.kind === 'value' - ) { - // Track the first reference to a value import that's not in a function - // This is used to detect module cycles - const {data} = record; - const key = `${data.source}:${data.imported}`; - if (seenImportFirstUsage.has(key)) { - continue; - } - - seenImportFirstUsage.add(key); - importFirstUsage.push(data); - } - } - - // Build dependencies - const dependencies: Array = Array.from( - dependenciesBySource.values(), - ); - - // Infer the module type - let moduleType: AnalyzeModuleType = ast.sourceType === 'script' ? 'cjs' : 'es'; - - // Infer module type in legacy mode - if (project.config.bundler.mode === 'legacy') { - if (cjsExports.length > 0) { - moduleType = 'cjs'; - } else if (esValueExports.length > 0) { - moduleType = 'es'; - } else if (hasCJSRef) { - moduleType = 'cjs'; - } else { - moduleType = 'unknown'; - } - } - - // - for (const record of context.records) { - if (record instanceof CJSVarRefRecord) { - if (project.config.bundler.mode === 'modern' && moduleType === 'es') { - /*context.addNodeDiagnostic(record.node, { + let {ast, project} = req; + + const query = Cache.buildQuery(req); + const cached: undefined | AnalyzeDependencyResult = analyzeCache.get(query); + if (cached) { + return cached; + } + + const context = new CompilerContext({ + ref: req.ref, + sourceText: req.sourceText, + ast, + project, + origin: { + category: 'analyzeDependencies', + }, + }); + ({ast} = await transform({...req, stage: 'pre'})); + context.reduce(ast, visitors); + + // + const importFirstUsage: AnalyzeDependencyImportFirstUsage = []; + const seenImportFirstUsage: Set = new Set(); + + // Extract records + const exports: Array = []; + const dependenciesBySource: Map = new Map(); + + const esValueExports: Array = []; + const cjsExports: Array = []; + let firstTopAwaitLocation: undefined | SourceLocation; + + // TODO description + let hasCJSRef = false; + + // Whether we have a default export, used to automatically add one for CJS + let hasDefaultExport = false; + + // Find the import sources that are only used as a type + const sourcesUsedAsType: Set = new Set(); + const sourcesUsedAsValue: Set = new Set(); + for (const record of context.records) { + let data; + + if (record instanceof ImportUsageRecord) { + data = record.data; + } + + // This has to be a separate if or else TS wont refine it... + if (record instanceof ExportRecord && record.data.type !== 'local') { + data = record.data; + } + + if (data !== undefined) { + const {kind, source} = data; + if (kind === 'type') { + sourcesUsedAsType.add(source); + } else { + sourcesUsedAsValue.add(source); + } + } + } + for (const source of sourcesUsedAsValue) { + sourcesUsedAsType.delete(source); + } + + // Process rest of the records + for (const record of context.records) { + if (record instanceof EscapedCJSRefRecord) { + exports.push({ + type: 'local', + loc: record.node.loc, + kind: 'value', + valueType: 'other', + name: '*', + }); + } + + if (record instanceof ImportRecord) { + let {data} = record; + + // If this source was only ever used as a type then convert us to a value + if ( + data.type === 'es' && + data.kind === 'value' && + sourcesUsedAsType.has(data.source) + ) { + const names: Array = []; + + for (const name of data.names) { + names.push({ + ...name, + kind: 'type', + }); + } + + data = {...data, kind: 'type', names}; + } + + // If we have multiple import records for this file, then merge them together + const existing = dependenciesBySource.get(data.source); + if (existing === undefined) { + dependenciesBySource.set(data.source, data); + } else { + let kind: ConstImportModuleKind; + if (data.kind === existing.kind) { + kind = data.kind; + } else { + kind = 'value'; + } + + const combinedRecord: AnalyzeDependency = { + type: data.type === 'es' && existing.type === 'es' ? 'es' : 'cjs', + kind, + optional: existing.optional && data.optional, + async: existing.async || data.async, + source: data.source, + all: existing.all || data.all, + names: [...existing.names, ...data.names], + loc: existing.loc || data.loc, + }; + + // Map ordering is by insertion time, so in the case where the previous import was a type import + // then we don't want to place our combined record in that position, it should be at the end. + // Inserting a type import statement at the top of the file shouldn't change the execution order + // if it was imported later + if (existing.kind === 'type' && data.kind === 'value') { + dependenciesBySource.delete(data.source); + } + + dependenciesBySource.set(data.source, combinedRecord); + } + } else if (record instanceof ExportRecord) { + exports.push(record.data); + } else if (record instanceof CJSVarRefRecord) { + hasCJSRef = true; + } else if (record instanceof CJSExportRecord) { + cjsExports.push(record.node); + } else if (record instanceof ESExportRecord) { + // No point checking for ES imported in CJS because it would have been a syntax error + if (record.kind === 'value') { + esValueExports.push(record.node); + } + } else if (record instanceof TopLevelAwaitRecord) { + if (firstTopAwaitLocation === undefined) { + firstTopAwaitLocation = record.loc; + } + } else if ( + record instanceof ImportUsageRecord && + record.isTop && + record.data.kind === 'value' + ) { + // Track the first reference to a value import that's not in a function + // This is used to detect module cycles + const {data} = record; + const key = `${data.source}:${data.imported}`; + if (seenImportFirstUsage.has(key)) { + continue; + } + + seenImportFirstUsage.add(key); + importFirstUsage.push(data); + } + } + + // Build dependencies + const dependencies: Array = Array.from( + dependenciesBySource.values(), + ); + + // Infer the module type + let moduleType: AnalyzeModuleType = ast.sourceType === 'script' ? 'cjs' : 'es'; + + // Infer module type in legacy mode + if (project.config.bundler.mode === 'legacy') { + if (cjsExports.length > 0) { + moduleType = 'cjs'; + } else if (esValueExports.length > 0) { + moduleType = 'es'; + } else if (hasCJSRef) { + moduleType = 'cjs'; + } else { + moduleType = 'unknown'; + } + } + + // + for (const record of context.records) { + if (record instanceof CJSVarRefRecord) { + if (project.config.bundler.mode === 'modern' && moduleType === 'es') { + /*context.addNodeDiagnostic(record.node, { category: 'analyzeDependencies', message: `CommonJS variable ${ record.node.name } is not available in an ES module`, });*/ - } - } else if (record instanceof CJSExportRecord) { - if (moduleType === 'es') { - context.addNodeDiagnostic( - record.node, - descriptions.ANALYZE_DEPENDENCIES.CJS_EXPORT_IN_ES, - ); - } - } - } - - // Add an implicit default import for CJS if there is none - if (moduleType === 'cjs' && !hasDefaultExport) { - exports.push({ - type: 'local', - loc: undefined, - kind: 'value', - valueType: 'other', - name: 'default', - }); - } - - const topLevelLocalBindings: AnalyzeDependencyTopLevelLocalBindings = {}; - - // Get all top level bindings - for (const [name, binding] of context.getRootScope().evaluate(ast).getOwnBindings()) { - topLevelLocalBindings[name] = binding.node.loc; - } - - const res: AnalyzeDependencyResult = { - topLevelLocalBindings, - moduleType, - firstTopAwaitLocation, - exports, - dependencies, - importFirstUsage, - syntax: ast.syntax, - diagnostics: [...ast.diagnostics, ...context.diagnostics.getDiagnostics()], - }; - analyzeCache.set(query, res); - return res; + } + } else if (record instanceof CJSExportRecord) { + if (moduleType === 'es') { + context.addNodeDiagnostic( + record.node, + descriptions.ANALYZE_DEPENDENCIES.CJS_EXPORT_IN_ES, + ); + } + } + } + + // Add an implicit default import for CJS if there is none + if (moduleType === 'cjs' && !hasDefaultExport) { + exports.push({ + type: 'local', + loc: undefined, + kind: 'value', + valueType: 'other', + name: 'default', + }); + } + + const topLevelLocalBindings: AnalyzeDependencyTopLevelLocalBindings = {}; + + // Get all top level bindings + for (const [name, binding] of context.getRootScope().evaluate(ast).getOwnBindings()) { + topLevelLocalBindings[name] = binding.node.loc; + } + + const res: AnalyzeDependencyResult = { + topLevelLocalBindings, + moduleType, + firstTopAwaitLocation, + exports, + dependencies, + importFirstUsage, + syntax: ast.syntax, + diagnostics: [...ast.diagnostics, ...context.diagnostics.getDiagnostics()], + }; + analyzeCache.set(query, res); + return res; } export function mergeAnalyzeDependencies( - main: AnalyzeDependencyResult, - second: AnalyzeDependencyResult, + main: AnalyzeDependencyResult, + second: AnalyzeDependencyResult, ): AnalyzeDependencyResult { - const exports: Array = [...main.exports]; - - // Take only local type exports - for (const exp of second.exports) { - if (exp.type === 'local' && exp.kind === 'type') { - exports.push(exp); - } - - // Ensure that all external exports are only reachable with `type` - if (exp.type === 'external' || exp.type === 'externalAll') { - exports.push({ - ...exp, - kind: 'type', - }); - } - } - - return { - ...main, - exports, - diagnostics: [...main.diagnostics, ...second.diagnostics], - }; + const exports: Array = [...main.exports]; + + // Take only local type exports + for (const exp of second.exports) { + if (exp.type === 'local' && exp.kind === 'type') { + exports.push(exp); + } + + // Ensure that all external exports are only reachable with `type` + if (exp.type === 'external' || exp.type === 'externalAll') { + exports.push({ + ...exp, + kind: 'type', + }); + } + } + + return { + ...main, + exports, + diagnostics: [...main.diagnostics, ...second.diagnostics], + }; } diff --git a/packages/@romejs/js-compiler/api/analyzeDependencies/records.ts b/packages/@romejs/js-compiler/api/analyzeDependencies/records.ts index f229734a87f..5b2e4be2a6f 100644 --- a/packages/@romejs/js-compiler/api/analyzeDependencies/records.ts +++ b/packages/@romejs/js-compiler/api/analyzeDependencies/records.ts @@ -6,94 +6,94 @@ */ import { - AnyNode, - ConstExportModuleKind, - ReferenceIdentifier, + AnyNode, + ConstExportModuleKind, + ReferenceIdentifier, } from '@romejs/js-ast'; import {SourceLocation} from '@romejs/parser-core'; import { - AnalyzeDependency, - AnalyzeDependencyImportUsageItem, - AnyAnalyzeExport, + AnalyzeDependency, + AnalyzeDependencyImportUsageItem, + AnyAnalyzeExport, } from '@romejs/core'; import {Record} from '@romejs/js-compiler'; export class ImportRecord extends Record { - constructor(data: AnalyzeDependency) { - super(); - this.data = data; - } + constructor(data: AnalyzeDependency) { + super(); + this.data = data; + } - data: AnalyzeDependency; + data: AnalyzeDependency; } export class ExportRecord extends Record { - constructor(data: AnyAnalyzeExport) { - super(); - this.data = data; - } + constructor(data: AnyAnalyzeExport) { + super(); + this.data = data; + } - data: AnyAnalyzeExport; + data: AnyAnalyzeExport; } // Whenever we encounter a reference to CJS module or exports export class EscapedCJSRefRecord extends Record { - constructor(node: AnyNode) { - super(); - this.node = node; - } + constructor(node: AnyNode) { + super(); + this.node = node; + } - node: AnyNode; + node: AnyNode; } // Whenever we encounter a exports or module.exports assignment export class CJSExportRecord extends Record { - constructor(node: AnyNode) { - super(); - this.node = node; - } + constructor(node: AnyNode) { + super(); + this.node = node; + } - node: AnyNode; + node: AnyNode; } export class CJSVarRefRecord extends Record { - constructor(node: ReferenceIdentifier) { - super(); - this.node = node; - } + constructor(node: ReferenceIdentifier) { + super(); + this.node = node; + } - node: ReferenceIdentifier; + node: ReferenceIdentifier; } export class ESExportRecord extends Record { - constructor(kind: ConstExportModuleKind, node: AnyNode) { - super(); - this.node = node; - this.kind = kind; - } - - node: AnyNode; - kind: ConstExportModuleKind; + constructor(kind: ConstExportModuleKind, node: AnyNode) { + super(); + this.node = node; + this.kind = kind; + } + + node: AnyNode; + kind: ConstExportModuleKind; } // Whenever we encounter a top level await export class TopLevelAwaitRecord extends Record { - constructor(loc: SourceLocation) { - super(); - this.loc = loc; - } + constructor(loc: SourceLocation) { + super(); + this.loc = loc; + } - loc: SourceLocation; + loc: SourceLocation; } // Whenever we encounter the first reference to an import export class ImportUsageRecord extends Record { - constructor(isTop: boolean, data: AnalyzeDependencyImportUsageItem) { - super(); - this.isTop = isTop; - this.data = data; - } - - isTop: boolean; - data: AnalyzeDependencyImportUsageItem; + constructor(isTop: boolean, data: AnalyzeDependencyImportUsageItem) { + super(); + this.isTop = isTop; + this.data = data; + } + + isTop: boolean; + data: AnalyzeDependencyImportUsageItem; } diff --git a/packages/@romejs/js-compiler/api/analyzeDependencies/utils.ts b/packages/@romejs/js-compiler/api/analyzeDependencies/utils.ts index 2c15b5d940b..147c5c04d61 100644 --- a/packages/@romejs/js-compiler/api/analyzeDependencies/utils.ts +++ b/packages/@romejs/js-compiler/api/analyzeDependencies/utils.ts @@ -6,245 +6,245 @@ */ import { - AnyNode, - BindingIdentifier, - ConstExportModuleKind, - ConstImportModuleKind, - ConstProgramSyntax, - ReferenceIdentifier, + AnyNode, + BindingIdentifier, + ConstExportModuleKind, + ConstImportModuleKind, + ConstProgramSyntax, + ReferenceIdentifier, } from '@romejs/js-ast'; import {SourceLocation} from '@romejs/parser-core'; import { - ClassBinding, - FunctionBinding, - Path, - Scope, - TypeBinding, + ClassBinding, + FunctionBinding, + Path, + Scope, + TypeBinding, } from '@romejs/js-compiler'; import { - AnalyzeDependency, - AnalyzeDependencyName, - AnalyzeDependencyResult, - AnalyzeExportValueType, - AnyAnalyzeExport, + AnalyzeDependency, + AnalyzeDependencyName, + AnalyzeDependencyResult, + AnalyzeExportValueType, + AnyAnalyzeExport, } from '@romejs/core'; export function isOptional(path: Path): boolean { - for (const {node} of path.ancestryPaths) { - if (node.type === 'TryStatement') { - return true; - } - } + for (const {node} of path.ancestryPaths) { + if (node.type === 'TryStatement') { + return true; + } + } - return false; + return false; } export function isTypeKind(kind: undefined | ConstImportModuleKind): boolean { - return kind === 'type' || kind === 'typeof'; + return kind === 'type' || kind === 'typeof'; } export function getImportKind( - kind: undefined | ConstImportModuleKind, + kind: undefined | ConstImportModuleKind, ): ConstImportModuleKind { - return kind === undefined ? 'value' : kind; + return kind === undefined ? 'value' : kind; } export function getExportKind( - kind: undefined | ConstExportModuleKind, + kind: undefined | ConstExportModuleKind, ): ConstExportModuleKind { - return kind === undefined ? 'value' : kind; + return kind === undefined ? 'value' : kind; } export function maybeTypeBinding( - kind: ConstExportModuleKind, - scope: Scope, - id: BindingIdentifier | ReferenceIdentifier, + kind: ConstExportModuleKind, + scope: Scope, + id: BindingIdentifier | ReferenceIdentifier, ): ConstExportModuleKind { - const binding = scope.getBinding(id.name); - if (kind === 'value' && binding instanceof TypeBinding) { - return 'type'; - } else { - return kind; - } + const binding = scope.getBinding(id.name); + if (kind === 'value' && binding instanceof TypeBinding) { + return 'type'; + } else { + return kind; + } } export function getKindWithSpecifiers( - rawKind: undefined | ConstImportModuleKind, - specifierKinds: Array, + rawKind: undefined | ConstImportModuleKind, + specifierKinds: Array, ): ConstImportModuleKind { - const kind: ConstImportModuleKind = getImportKind(rawKind); - if (isTypeKind(kind) || specifierKinds.length === 0) { - return kind; - } - - for (const specifierKind of specifierKinds) { - if (specifierKind === 'value') { - return 'value'; - } - } - return 'type'; + const kind: ConstImportModuleKind = getImportKind(rawKind); + if (isTypeKind(kind) || specifierKinds.length === 0) { + return kind; + } + + for (const specifierKind of specifierKinds) { + if (specifierKind === 'value') { + return 'value'; + } + } + return 'type'; } // We use this to have an easy way to identify the actual runtime type of an import // This is useful as we needs this as Flow allows you to `import type` classes which // are considered values export function getAnalyzeExportValueType( - scope: Scope, - node: undefined | AnyNode, + scope: Scope, + node: undefined | AnyNode, ): AnalyzeExportValueType { - if (node === undefined) { - return 'other'; - } - - if (node.type === 'Identifier') { - const binding = scope.getBinding(node.name); - - if (binding instanceof FunctionBinding) { - return 'function'; - } - - if (binding instanceof ClassBinding) { - return 'class'; - } - - if (binding instanceof TypeBinding) { - const {typeKind} = binding; - switch (typeKind) { - case 'function': - case 'class': - return typeKind; - } - } - } - - if (node.type === 'FunctionDeclaration') { - return 'function'; - } - - if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') { - return 'class'; - } - - return 'other'; + if (node === undefined) { + return 'other'; + } + + if (node.type === 'Identifier') { + const binding = scope.getBinding(node.name); + + if (binding instanceof FunctionBinding) { + return 'function'; + } + + if (binding instanceof ClassBinding) { + return 'class'; + } + + if (binding instanceof TypeBinding) { + const {typeKind} = binding; + switch (typeKind) { + case 'function': + case 'class': + return typeKind; + } + } + } + + if (node.type === 'FunctionDeclaration') { + return 'function'; + } + + if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') { + return 'class'; + } + + return 'other'; } // Resolve a export declaration to it's binding node if one exists export function getDeclarationLoc( - scope: Scope, - node: AnyNode, + scope: Scope, + node: AnyNode, ): undefined | SourceLocation { - if (node.type === 'ReferenceIdentifier') { - const binding = scope.getBinding(node.name); - if (binding !== undefined) { - return binding.node.loc; - } - } - - return node.loc; + if (node.type === 'ReferenceIdentifier') { + const binding = scope.getBinding(node.name); + if (binding !== undefined) { + return binding.node.loc; + } + } + + return node.loc; } function arraySame( - a: Array, - b: Array, - callback: (a: T, b: T) => boolean, + a: Array, + b: Array, + callback: (a: T, b: T) => boolean, ): boolean { - if (a.length !== b.length) { - return false; - } + if (a.length !== b.length) { + return false; + } - for (let i = 0; i < a.length; i++) { - if (!callback(a[i], b[i])) { - return false; - } - } + for (let i = 0; i < a.length; i++) { + if (!callback(a[i], b[i])) { + return false; + } + } - return true; + return true; } function syntaxSame(a: ConstProgramSyntax, b: ConstProgramSyntax): boolean { - return a === b; + return a === b; } function exportsSame(a: AnyAnalyzeExport, b: AnyAnalyzeExport): boolean { - if (a.type !== b.type) { - return false; - } - - if (a.kind !== b.kind) { - return false; - } - - switch (a.type) { - case 'local': - return b.type === 'local' && a.name === b.name; - - case 'external': - return ( - b.type === 'external' && - a.imported === b.imported && - a.exported === b.exported && - a.source === b.source - ); - - case 'externalAll': - return b.type === 'externalAll' && a.source === b.source; - - case 'externalNamespace': - return ( - b.type === 'externalNamespace' && - a.source === b.source && - a.exported === b.exported - ); - } + if (a.type !== b.type) { + return false; + } + + if (a.kind !== b.kind) { + return false; + } + + switch (a.type) { + case 'local': + return b.type === 'local' && a.name === b.name; + + case 'external': + return ( + b.type === 'external' && + a.imported === b.imported && + a.exported === b.exported && + a.source === b.source + ); + + case 'externalAll': + return b.type === 'externalAll' && a.source === b.source; + + case 'externalNamespace': + return ( + b.type === 'externalNamespace' && + a.source === b.source && + a.exported === b.exported + ); + } } function dependencyNameSame( - a: AnalyzeDependencyName, - b: AnalyzeDependencyName, + a: AnalyzeDependencyName, + b: AnalyzeDependencyName, ): boolean { - return a.kind === b.kind && a.name === b.name; + return a.kind === b.kind && a.name === b.name; } function dependenciesSame(a: AnalyzeDependency, b: AnalyzeDependency): boolean { - return ( - a.all === b.all && - a.async === b.async && - a.optional === b.optional && - a.source === b.source && - a.type === b.type && - arraySame(a.names, b.names, dependencyNameSame) - ); + return ( + a.all === b.all && + a.async === b.async && + a.optional === b.optional && + a.source === b.source && + a.type === b.type && + arraySame(a.names, b.names, dependencyNameSame) + ); } // Check if the shape of two analyzeDependencyResults are equal. Ignoring location information export function areAnalyzeDependencyResultsEqual( - a: AnalyzeDependencyResult, - b: AnalyzeDependencyResult, + a: AnalyzeDependencyResult, + b: AnalyzeDependencyResult, ): boolean { - if ( - (a.firstTopAwaitLocation === undefined && - b.firstTopAwaitLocation !== undefined) || - (b.firstTopAwaitLocation === undefined && - a.firstTopAwaitLocation !== undefined) - ) { - return false; - } - - if (a.moduleType !== b.moduleType) { - return false; - } - - if (!arraySame(a.syntax, b.syntax, syntaxSame)) { - return false; - } - - if (!arraySame(a.exports, b.exports, exportsSame)) { - return false; - } - - if (!arraySame(a.dependencies, b.dependencies, dependenciesSame)) { - return false; - } - - return true; + if ( + (a.firstTopAwaitLocation === undefined && + b.firstTopAwaitLocation !== undefined) || + (b.firstTopAwaitLocation === undefined && + a.firstTopAwaitLocation !== undefined) + ) { + return false; + } + + if (a.moduleType !== b.moduleType) { + return false; + } + + if (!arraySame(a.syntax, b.syntax, syntaxSame)) { + return false; + } + + if (!arraySame(a.exports, b.exports, exportsSame)) { + return false; + } + + if (!arraySame(a.dependencies, b.dependencies, dependenciesSame)) { + return false; + } + + return true; } diff --git a/packages/@romejs/js-compiler/api/analyzeDependencies/visitors/cjs.ts b/packages/@romejs/js-compiler/api/analyzeDependencies/visitors/cjs.ts index 40abd60df08..8fad079cfc9 100644 --- a/packages/@romejs/js-compiler/api/analyzeDependencies/visitors/cjs.ts +++ b/packages/@romejs/js-compiler/api/analyzeDependencies/visitors/cjs.ts @@ -8,199 +8,199 @@ import {objectExpression} from '@romejs/js-ast'; import {Path} from '@romejs/js-compiler'; import { - doesNodeMatchPattern, - getNodeReferenceParts, - getRequireSource, + doesNodeMatchPattern, + getNodeReferenceParts, + getRequireSource, } from '@romejs/js-ast-utils'; import { - CJSExportRecord, - CJSVarRefRecord, - EscapedCJSRefRecord, - ExportRecord, - ImportRecord, + CJSExportRecord, + CJSVarRefRecord, + EscapedCJSRefRecord, + ExportRecord, + ImportRecord, } from '../records'; import { - getAnalyzeExportValueType, - getDeclarationLoc, - isOptional, + getAnalyzeExportValueType, + getDeclarationLoc, + isOptional, } from '../utils'; export default { - name: 'analyzeDependenciesCJS', - enter(path: Path) { - const {node, parent, scope, context} = path; - - // Handle require() - if (node.type === 'CallExpression') { - const {callee, arguments: args} = node; - - const isRequire: boolean = - callee.type === 'ReferenceIdentifier' && - callee.name === 'require' && - path.scope.hasBinding('require') === false; - const sourceArg = args[0]; - - if (isRequire && args.length === 1 && sourceArg.type === 'StringLiteral') { - context.record( - new ImportRecord({ - type: 'cjs', - kind: 'value', - optional: isOptional(path), - loc: node.loc, - source: sourceArg.value, - names: [], - all: true, - async: false, - }), - ); - } - } - - // Detect assignments to exports and module.exports as definitely being an CJS module - if (node.type === 'AssignmentExpression') { - const isModuleExports = - path.scope.getBinding('module') === undefined && - (doesNodeMatchPattern(node.left, 'module.exports') || - doesNodeMatchPattern(node.left, 'module.exports.**')); - const isExports = - path.scope.getBinding('exports') === undefined && - (doesNodeMatchPattern(node.left, 'exports') || - doesNodeMatchPattern(node.left, 'exports.**')); - - if (isModuleExports || isExports) { - context.record(new CJSExportRecord(node)); - } - - if (isModuleExports) { - const {right} = node; - - if (objectExpression.is(right)) { - context.record( - new ExportRecord({ - type: 'local', - loc: getDeclarationLoc(scope, node.right), - valueType: getAnalyzeExportValueType(scope, node.right), - kind: 'value', - name: 'default', - }), - ); - - for (const prop of right.properties) { - // Don't allow spread, unknown, or computed properties - if ( - prop.type === 'SpreadProperty' || - (prop.key.type === 'ComputedPropertyKey' && - prop.key.value.type !== 'StringLiteral') - ) { - context.record(new EscapedCJSRefRecord(prop)); - continue; - } - - const key = prop.key.value; - let name: string; - if (key.type === 'Identifier') { - name = key.name; - } else if (key.type === 'StringLiteral') { - name = key.value; - } else { - // Unknown key literal - context.record(new EscapedCJSRefRecord(key)); - continue; - } - - let target = prop.type === 'ObjectMethod' ? prop : prop.value; - - context.record( - new ExportRecord({ - type: 'local', - loc: getDeclarationLoc(scope, target), - valueType: getAnalyzeExportValueType(scope, target), - kind: 'value', - name, - }), - ); - } - } else { - const source = getRequireSource(node.right, scope); - if (source === undefined) { - context.record( - new ExportRecord({ - type: 'local', - loc: getDeclarationLoc(scope, node.right), - valueType: getAnalyzeExportValueType(scope, node.right), - kind: 'value', - name: 'default', - }), - ); - } else { - context.record( - new ExportRecord({ - type: 'externalAll', - loc: getDeclarationLoc(scope, node.right), - kind: 'value', - source, - }), - ); - - context.record( - new ExportRecord({ - type: 'external', - kind: 'value', - loc: getDeclarationLoc(scope, node.right), - imported: 'default', - exported: 'default', - source, - }), - ); - } - } - } - - if (isExports) { - const {parts} = getNodeReferenceParts(node.left); - - if (parts.length >= 2) { - // parts[0] is exports - const name = parts[1].value; - - context.record( - new ExportRecord({ - type: 'local', - loc: getDeclarationLoc(scope, node.right), - valueType: getAnalyzeExportValueType(scope, node.right), - kind: 'value', - name, - }), - ); - } - } - } - - if (node.type === 'ReferenceIdentifier') { - const binding = path.scope.getBinding(node.name); - - // Detect references to exports and module - if (binding === undefined) { - if ( - node.name === '__filename' || - node.name === '__dirname' || - node.name === 'require' || - node.name === 'module' || - node.name === 'exports' - ) { - context.record(new CJSVarRefRecord(node)); - } - - if (node.name === 'module' || node.name === 'exports') { - const inMemberExpression = - parent.type === 'MemberExpression' && parent.object === node; - if (!inMemberExpression) { - context.record(new EscapedCJSRefRecord(node)); - } - } - } - } - - return node; - }, + name: 'analyzeDependenciesCJS', + enter(path: Path) { + const {node, parent, scope, context} = path; + + // Handle require() + if (node.type === 'CallExpression') { + const {callee, arguments: args} = node; + + const isRequire: boolean = + callee.type === 'ReferenceIdentifier' && + callee.name === 'require' && + path.scope.hasBinding('require') === false; + const sourceArg = args[0]; + + if (isRequire && args.length === 1 && sourceArg.type === 'StringLiteral') { + context.record( + new ImportRecord({ + type: 'cjs', + kind: 'value', + optional: isOptional(path), + loc: node.loc, + source: sourceArg.value, + names: [], + all: true, + async: false, + }), + ); + } + } + + // Detect assignments to exports and module.exports as definitely being an CJS module + if (node.type === 'AssignmentExpression') { + const isModuleExports = + path.scope.getBinding('module') === undefined && + (doesNodeMatchPattern(node.left, 'module.exports') || + doesNodeMatchPattern(node.left, 'module.exports.**')); + const isExports = + path.scope.getBinding('exports') === undefined && + (doesNodeMatchPattern(node.left, 'exports') || + doesNodeMatchPattern(node.left, 'exports.**')); + + if (isModuleExports || isExports) { + context.record(new CJSExportRecord(node)); + } + + if (isModuleExports) { + const {right} = node; + + if (objectExpression.is(right)) { + context.record( + new ExportRecord({ + type: 'local', + loc: getDeclarationLoc(scope, node.right), + valueType: getAnalyzeExportValueType(scope, node.right), + kind: 'value', + name: 'default', + }), + ); + + for (const prop of right.properties) { + // Don't allow spread, unknown, or computed properties + if ( + prop.type === 'SpreadProperty' || + (prop.key.type === 'ComputedPropertyKey' && + prop.key.value.type !== 'StringLiteral') + ) { + context.record(new EscapedCJSRefRecord(prop)); + continue; + } + + const key = prop.key.value; + let name: string; + if (key.type === 'Identifier') { + name = key.name; + } else if (key.type === 'StringLiteral') { + name = key.value; + } else { + // Unknown key literal + context.record(new EscapedCJSRefRecord(key)); + continue; + } + + let target = prop.type === 'ObjectMethod' ? prop : prop.value; + + context.record( + new ExportRecord({ + type: 'local', + loc: getDeclarationLoc(scope, target), + valueType: getAnalyzeExportValueType(scope, target), + kind: 'value', + name, + }), + ); + } + } else { + const source = getRequireSource(node.right, scope); + if (source === undefined) { + context.record( + new ExportRecord({ + type: 'local', + loc: getDeclarationLoc(scope, node.right), + valueType: getAnalyzeExportValueType(scope, node.right), + kind: 'value', + name: 'default', + }), + ); + } else { + context.record( + new ExportRecord({ + type: 'externalAll', + loc: getDeclarationLoc(scope, node.right), + kind: 'value', + source, + }), + ); + + context.record( + new ExportRecord({ + type: 'external', + kind: 'value', + loc: getDeclarationLoc(scope, node.right), + imported: 'default', + exported: 'default', + source, + }), + ); + } + } + } + + if (isExports) { + const {parts} = getNodeReferenceParts(node.left); + + if (parts.length >= 2) { + // parts[0] is exports + const name = parts[1].value; + + context.record( + new ExportRecord({ + type: 'local', + loc: getDeclarationLoc(scope, node.right), + valueType: getAnalyzeExportValueType(scope, node.right), + kind: 'value', + name, + }), + ); + } + } + } + + if (node.type === 'ReferenceIdentifier') { + const binding = path.scope.getBinding(node.name); + + // Detect references to exports and module + if (binding === undefined) { + if ( + node.name === '__filename' || + node.name === '__dirname' || + node.name === 'require' || + node.name === 'module' || + node.name === 'exports' + ) { + context.record(new CJSVarRefRecord(node)); + } + + if (node.name === 'module' || node.name === 'exports') { + const inMemberExpression = + parent.type === 'MemberExpression' && parent.object === node; + if (!inMemberExpression) { + context.record(new EscapedCJSRefRecord(node)); + } + } + } + } + + return node; + }, }; diff --git a/packages/@romejs/js-compiler/api/analyzeDependencies/visitors/es.ts b/packages/@romejs/js-compiler/api/analyzeDependencies/visitors/es.ts index 6615bd2d703..438d3036cce 100644 --- a/packages/@romejs/js-compiler/api/analyzeDependencies/visitors/es.ts +++ b/packages/@romejs/js-compiler/api/analyzeDependencies/visitors/es.ts @@ -9,325 +9,325 @@ import {ConstExportModuleKind, ConstImportModuleKind} from '@romejs/js-ast'; import {ImportBinding, Path} from '@romejs/js-compiler'; import {AnalyzeDependencyName} from '@romejs/core'; import { - getBindingIdentifiers, - getImportSpecifiers, - isFunctionNode, - isInTypeAnnotation, + getBindingIdentifiers, + getImportSpecifiers, + isFunctionNode, + isInTypeAnnotation, } from '@romejs/js-ast-utils'; import { - ESExportRecord, - ExportRecord, - ImportRecord, - ImportUsageRecord, - TopLevelAwaitRecord, + ESExportRecord, + ExportRecord, + ImportRecord, + ImportUsageRecord, + TopLevelAwaitRecord, } from '../records'; import { - getAnalyzeExportValueType, - getDeclarationLoc, - getExportKind, - getImportKind, - getKindWithSpecifiers, - isOptional, - maybeTypeBinding, + getAnalyzeExportValueType, + getDeclarationLoc, + getExportKind, + getImportKind, + getKindWithSpecifiers, + isOptional, + maybeTypeBinding, } from '../utils'; export default { - name: 'analyzeDependenciesES', - enter(path: Path) { - const {node, scope, context} = path; - - // import('./bar'); - if (node.type === 'ImportCall' && node.argument.type === 'StringLiteral') { - context.record( - new ImportRecord({ - type: 'es', - async: true, - kind: 'value', - names: [], - loc: node.argument.loc, - source: node.argument.value, - optional: isOptional(path), - all: true, - }), - ); - } - - // Local bindings exports: - // export const foo - // export function foo() {} - // export {}; - if (node.type === 'ExportLocalDeclaration') { - const valueType = getAnalyzeExportValueType(scope, node.declaration); - for (const id of getBindingIdentifiers(node)) { - const kind = maybeTypeBinding(getExportKind(node.exportKind), scope, id); - context.record( - new ExportRecord({ - type: 'local', - valueType, - kind, - loc: getDeclarationLoc(scope, id), - name: id.name, - }), - ); - } - - const {specifiers} = node; - if (specifiers !== undefined) { - for (const specifier of specifiers) { - const kind: ConstExportModuleKind = maybeTypeBinding( - getExportKind(specifier.exportKind || node.exportKind), - scope, - specifier.local, - ); - - context.record( - new ExportRecord({ - type: 'local', - loc: getDeclarationLoc(scope, specifier.local), - valueType: getAnalyzeExportValueType(scope, specifier.local), - kind, - name: specifier.exported.name, - }), - ); - } - } - } - - // export default - if (node.type === 'ExportDefaultDeclaration') { - context.record( - new ExportRecord({ - type: 'local', - loc: getDeclarationLoc(scope, node.declaration), - valueType: getAnalyzeExportValueType(scope, node.declaration), - kind: 'value', - name: 'default', - }), - ); - } - - // External binding exports: - // export {} from ''; - if (node.type === 'ExportExternalDeclaration') { - const {source} = node; - const specifiersKinds: Array = []; - const exportedNames: Array = []; - - const {namedSpecifiers, defaultSpecifier, namespaceSpecifier} = node; - - if (defaultSpecifier !== undefined) { - context.record( - new ExportRecord({ - type: 'external', - kind: 'value', - loc: defaultSpecifier.loc, - imported: 'default', - exported: defaultSpecifier.exported.name, - source: source.value, - }), - ); - } - - if (namespaceSpecifier !== undefined) { - context.record( - new ExportRecord({ - type: 'externalNamespace', - kind: 'value', - loc: namespaceSpecifier.loc, - exported: namespaceSpecifier.exported.name, - source: source.value, - }), - ); - } - - for (const specifier of namedSpecifiers) { - const kind = getImportKind(specifier.exportKind || node.exportKind); - specifiersKinds.push(kind); - - exportedNames.push({ - name: specifier.local.name, - kind, - loc: specifier.loc, - }); - - context.record( - new ExportRecord({ - type: 'external', - kind, - loc: specifier.loc, - imported: specifier.local.name, - exported: specifier.exported.name, - source: source.value, - }), - ); - } - - context.record( - new ImportRecord({ - type: 'es', - async: false, - kind: getKindWithSpecifiers(node.exportKind, specifiersKinds), - names: exportedNames, - loc: source.loc, - source: source.value, - optional: isOptional(path), - all: false, - }), - ); - } - - // TS: import A = require('B'); - if ( - node.type === 'TSImportEqualsDeclaration' && - node.moduleReference.type === 'TSExternalModuleReference' - ) { - context.record( - new ImportRecord({ - type: 'cjs', - kind: 'value', - optional: isOptional(path), - loc: node.loc, - source: node.moduleReference.expression.value, - names: [], - all: true, - async: false, - }), - ); - } - - // export * from ''; - if (node.type === 'ExportAllDeclaration') { - context.record( - new ImportRecord({ - type: 'es', - async: false, - kind: getExportKind(node.exportKind), - optional: isOptional(path), - loc: node.source.loc, - names: [], - source: node.source.value, - all: true, - }), - ); - - context.record( - new ExportRecord({ - type: 'externalAll', - loc: node.loc, - kind: getExportKind(node.exportKind), - source: node.source.value, - }), - ); - } - - if ( - node.type === 'ExportAllDeclaration' || - node.type === 'ExportDefaultDeclaration' || - node.type === 'ExportLocalDeclaration' - ) { - context.record(new ESExportRecord(getExportKind(node.exportKind), node)); - } - - // import {} from ''; - - // import * as foo from ''; - if (node.type === 'ImportDeclaration') { - let hasNamespaceSpecifier = false; - const specifierKinds: Array = []; - const names: Array = []; - - for (const specifier of getImportSpecifiers(node)) { - if (specifier.type === 'ImportNamespaceSpecifier') { - hasNamespaceSpecifier = true; - break; - } - - const kind: ConstImportModuleKind = getImportKind(node.importKind); - specifierKinds.push(kind); - - if (specifier.type === 'ImportDefaultSpecifier') { - names.push({ - kind, - loc: specifier.loc, - name: 'default', - }); - } - - if (specifier.type === 'ImportSpecifier') { - names.push({ - kind, - loc: specifier.loc, - name: specifier.imported.name, - }); - } - } - - context.record( - new ImportRecord({ - type: 'es', - async: false, - kind: getKindWithSpecifiers(node.importKind, specifierKinds), - loc: node.source.loc, - optional: isOptional(path), - source: node.source.value, - all: hasNamespaceSpecifier, - names, - }), - ); - } - - // Detect top level await - if ( - node.type === 'AwaitExpression' && - path.findAncestry((path) => isFunctionNode(path.node)) === undefined - ) { - const {loc} = node; - if (loc === undefined) { - throw new Error('loc is undefined on AwaitExpression we want to mark'); - } - context.record(new TopLevelAwaitRecord(loc)); - } - - if (node.type === 'ReferenceIdentifier') { - const binding = path.scope.getBinding(node.name); - - // Mark references to imports outside of functions - if (binding !== undefined && binding instanceof ImportBinding) { - const {meta} = binding; - - // We can skip this if it's referencing a namespace - if (meta.type !== 'name') { - return node; - } - - // These are nodes that will defer the execution of code outside the init path - - // (They could still be triggered with an actual function call but this is just for some basic analysis) - const deferredExecution = path.findAncestry((path) => - isFunctionNode(path.node) || path.node.type === 'ClassProperty' - ); - const isTop = deferredExecution === undefined; - - let kind: ConstImportModuleKind = getImportKind(meta.kind); - if (isInTypeAnnotation(path)) { - kind = 'type'; - } - - context.record( - new ImportUsageRecord( - isTop, - { - kind, - loc: node.loc, - local: node.name, - imported: meta.imported, - source: meta.source, - }, - ), - ); - } - } - - return node; - }, + name: 'analyzeDependenciesES', + enter(path: Path) { + const {node, scope, context} = path; + + // import('./bar'); + if (node.type === 'ImportCall' && node.argument.type === 'StringLiteral') { + context.record( + new ImportRecord({ + type: 'es', + async: true, + kind: 'value', + names: [], + loc: node.argument.loc, + source: node.argument.value, + optional: isOptional(path), + all: true, + }), + ); + } + + // Local bindings exports: + // export const foo + // export function foo() {} + // export {}; + if (node.type === 'ExportLocalDeclaration') { + const valueType = getAnalyzeExportValueType(scope, node.declaration); + for (const id of getBindingIdentifiers(node)) { + const kind = maybeTypeBinding(getExportKind(node.exportKind), scope, id); + context.record( + new ExportRecord({ + type: 'local', + valueType, + kind, + loc: getDeclarationLoc(scope, id), + name: id.name, + }), + ); + } + + const {specifiers} = node; + if (specifiers !== undefined) { + for (const specifier of specifiers) { + const kind: ConstExportModuleKind = maybeTypeBinding( + getExportKind(specifier.exportKind || node.exportKind), + scope, + specifier.local, + ); + + context.record( + new ExportRecord({ + type: 'local', + loc: getDeclarationLoc(scope, specifier.local), + valueType: getAnalyzeExportValueType(scope, specifier.local), + kind, + name: specifier.exported.name, + }), + ); + } + } + } + + // export default + if (node.type === 'ExportDefaultDeclaration') { + context.record( + new ExportRecord({ + type: 'local', + loc: getDeclarationLoc(scope, node.declaration), + valueType: getAnalyzeExportValueType(scope, node.declaration), + kind: 'value', + name: 'default', + }), + ); + } + + // External binding exports: + // export {} from ''; + if (node.type === 'ExportExternalDeclaration') { + const {source} = node; + const specifiersKinds: Array = []; + const exportedNames: Array = []; + + const {namedSpecifiers, defaultSpecifier, namespaceSpecifier} = node; + + if (defaultSpecifier !== undefined) { + context.record( + new ExportRecord({ + type: 'external', + kind: 'value', + loc: defaultSpecifier.loc, + imported: 'default', + exported: defaultSpecifier.exported.name, + source: source.value, + }), + ); + } + + if (namespaceSpecifier !== undefined) { + context.record( + new ExportRecord({ + type: 'externalNamespace', + kind: 'value', + loc: namespaceSpecifier.loc, + exported: namespaceSpecifier.exported.name, + source: source.value, + }), + ); + } + + for (const specifier of namedSpecifiers) { + const kind = getImportKind(specifier.exportKind || node.exportKind); + specifiersKinds.push(kind); + + exportedNames.push({ + name: specifier.local.name, + kind, + loc: specifier.loc, + }); + + context.record( + new ExportRecord({ + type: 'external', + kind, + loc: specifier.loc, + imported: specifier.local.name, + exported: specifier.exported.name, + source: source.value, + }), + ); + } + + context.record( + new ImportRecord({ + type: 'es', + async: false, + kind: getKindWithSpecifiers(node.exportKind, specifiersKinds), + names: exportedNames, + loc: source.loc, + source: source.value, + optional: isOptional(path), + all: false, + }), + ); + } + + // TS: import A = require('B'); + if ( + node.type === 'TSImportEqualsDeclaration' && + node.moduleReference.type === 'TSExternalModuleReference' + ) { + context.record( + new ImportRecord({ + type: 'cjs', + kind: 'value', + optional: isOptional(path), + loc: node.loc, + source: node.moduleReference.expression.value, + names: [], + all: true, + async: false, + }), + ); + } + + // export * from ''; + if (node.type === 'ExportAllDeclaration') { + context.record( + new ImportRecord({ + type: 'es', + async: false, + kind: getExportKind(node.exportKind), + optional: isOptional(path), + loc: node.source.loc, + names: [], + source: node.source.value, + all: true, + }), + ); + + context.record( + new ExportRecord({ + type: 'externalAll', + loc: node.loc, + kind: getExportKind(node.exportKind), + source: node.source.value, + }), + ); + } + + if ( + node.type === 'ExportAllDeclaration' || + node.type === 'ExportDefaultDeclaration' || + node.type === 'ExportLocalDeclaration' + ) { + context.record(new ESExportRecord(getExportKind(node.exportKind), node)); + } + + // import {} from ''; + + // import * as foo from ''; + if (node.type === 'ImportDeclaration') { + let hasNamespaceSpecifier = false; + const specifierKinds: Array = []; + const names: Array = []; + + for (const specifier of getImportSpecifiers(node)) { + if (specifier.type === 'ImportNamespaceSpecifier') { + hasNamespaceSpecifier = true; + break; + } + + const kind: ConstImportModuleKind = getImportKind(node.importKind); + specifierKinds.push(kind); + + if (specifier.type === 'ImportDefaultSpecifier') { + names.push({ + kind, + loc: specifier.loc, + name: 'default', + }); + } + + if (specifier.type === 'ImportSpecifier') { + names.push({ + kind, + loc: specifier.loc, + name: specifier.imported.name, + }); + } + } + + context.record( + new ImportRecord({ + type: 'es', + async: false, + kind: getKindWithSpecifiers(node.importKind, specifierKinds), + loc: node.source.loc, + optional: isOptional(path), + source: node.source.value, + all: hasNamespaceSpecifier, + names, + }), + ); + } + + // Detect top level await + if ( + node.type === 'AwaitExpression' && + path.findAncestry((path) => isFunctionNode(path.node)) === undefined + ) { + const {loc} = node; + if (loc === undefined) { + throw new Error('loc is undefined on AwaitExpression we want to mark'); + } + context.record(new TopLevelAwaitRecord(loc)); + } + + if (node.type === 'ReferenceIdentifier') { + const binding = path.scope.getBinding(node.name); + + // Mark references to imports outside of functions + if (binding !== undefined && binding instanceof ImportBinding) { + const {meta} = binding; + + // We can skip this if it's referencing a namespace + if (meta.type !== 'name') { + return node; + } + + // These are nodes that will defer the execution of code outside the init path + + // (They could still be triggered with an actual function call but this is just for some basic analysis) + const deferredExecution = path.findAncestry((path) => + isFunctionNode(path.node) || path.node.type === 'ClassProperty' + ); + const isTop = deferredExecution === undefined; + + let kind: ConstImportModuleKind = getImportKind(meta.kind); + if (isInTypeAnnotation(path)) { + kind = 'type'; + } + + context.record( + new ImportUsageRecord( + isTop, + { + kind, + loc: node.loc, + local: node.name, + imported: meta.imported, + source: meta.source, + }, + ), + ); + } + } + + return node; + }, }; diff --git a/packages/@romejs/js-compiler/api/compile.ts b/packages/@romejs/js-compiler/api/compile.ts index a6fb46cce46..6beaff1344d 100644 --- a/packages/@romejs/js-compiler/api/compile.ts +++ b/packages/@romejs/js-compiler/api/compile.ts @@ -13,84 +13,84 @@ import {CompileRequest} from '../types'; import transform from '../methods/transform'; export type CompileResult = { - mappings: Mappings; - diagnostics: Diagnostics; - suppressions: DiagnosticSuppressions; - cacheDependencies: Array; - compiledCode: string; - sourceText: string; + mappings: Mappings; + diagnostics: Diagnostics; + suppressions: DiagnosticSuppressions; + cacheDependencies: Array; + compiledCode: string; + sourceText: string; }; const compileCache: Cache = new Cache(); export default async function compile( - req: CompileRequest, + req: CompileRequest, ): Promise { - const {sourceText, ast} = req; + const {sourceText, ast} = req; - const query = Cache.buildQuery(req); - const cached: undefined | CompileResult = compileCache.get(query); - if (cached) { - return cached; - } + const query = Cache.buildQuery(req); + const cached: undefined | CompileResult = compileCache.get(query); + if (cached) { + return cached; + } - const { - ast: transformedAst, - diagnostics, - suppressions, - cacheDependencies, - } = await transform(req); + const { + ast: transformedAst, + diagnostics, + suppressions, + cacheDependencies, + } = await transform(req); - const formatted = formatJS( - transformedAst, - { - typeAnnotations: false, - indent: req.stage === 'compileForBundle' ? 1 : 0, - sourceMaps: true, - sourceText, - }, - ); + const formatted = formatJS( + transformedAst, + { + typeAnnotations: false, + indent: req.stage === 'compileForBundle' ? 1 : 0, + sourceMaps: true, + sourceText, + }, + ); - if (req.inputSourceMap !== undefined) { - const inputSourceMap = SourceMapConsumer.fromJSON(req.inputSourceMap); - const mappings: Mappings = []; + if (req.inputSourceMap !== undefined) { + const inputSourceMap = SourceMapConsumer.fromJSON(req.inputSourceMap); + const mappings: Mappings = []; - for (const mapping of formatted.mappings) { - const actual = inputSourceMap.exactOriginalPositionFor( - mapping.original.line, - mapping.original.column, - ); + for (const mapping of formatted.mappings) { + const actual = inputSourceMap.exactOriginalPositionFor( + mapping.original.line, + mapping.original.column, + ); - if (actual !== undefined) { - if ( - mapping.original.line !== actual.line || - mapping.original.column !== actual.column - ) { - mappings.push({ - ...mapping, - original: { - line: actual.line, - column: actual.column, - }, - }); - } else { - mappings.push(mapping); - } - } - } + if (actual !== undefined) { + if ( + mapping.original.line !== actual.line || + mapping.original.column !== actual.column + ) { + mappings.push({ + ...mapping, + original: { + line: actual.line, + column: actual.column, + }, + }); + } else { + mappings.push(mapping); + } + } + } - formatted.mappings = mappings; - } + formatted.mappings = mappings; + } - const res: CompileResult = { - compiledCode: formatted.code, - mappings: formatted.mappings, - diagnostics: [...ast.diagnostics, ...diagnostics], - cacheDependencies, - suppressions, - sourceText, - }; + const res: CompileResult = { + compiledCode: formatted.code, + mappings: formatted.mappings, + diagnostics: [...ast.diagnostics, ...diagnostics], + cacheDependencies, + suppressions, + sourceText, + }; - compileCache.set(query, res); - return res; + compileCache.set(query, res); + return res; } diff --git a/packages/@romejs/js-compiler/api/createHook.ts b/packages/@romejs/js-compiler/api/createHook.ts index 55b3e11931b..b8ccd9766de 100644 --- a/packages/@romejs/js-compiler/api/createHook.ts +++ b/packages/@romejs/js-compiler/api/createHook.ts @@ -8,36 +8,36 @@ import {Path, TransformExitResult} from '@romejs/js-compiler'; export type HookDescriptor = { - name: string; - initialState: State extends void ? never : State; - call?: ( - path: Path, - state: State, - arg: CallArg, - ) => { - bubble?: boolean; - value: CallReturn; - state: State; - }; - exit?: (path: Path, state: State) => TransformExitResult; + name: string; + initialState: State extends void ? never : State; + call?: ( + path: Path, + state: State, + arg: CallArg, + ) => { + bubble?: boolean; + value: CallReturn; + state: State; + }; + exit?: (path: Path, state: State) => TransformExitResult; }; // rome-ignore lint/noExplicitAny export type AnyHookDescriptor = HookDescriptor; export type HookInstance = { - // rome-ignore lint/noExplicitAny - state: any; - // rome-ignore lint/noExplicitAny - descriptor: HookDescriptor; + // rome-ignore lint/noExplicitAny + state: any; + // rome-ignore lint/noExplicitAny + descriptor: HookDescriptor; }; export default function createHook< - State = void, - CallArg = void, - CallReturn = void + State = void, + CallArg = void, + CallReturn = void >( - descriptor: HookDescriptor, + descriptor: HookDescriptor, ): HookDescriptor { - return descriptor; + return descriptor; } diff --git a/packages/@romejs/js-compiler/index.ts b/packages/@romejs/js-compiler/index.ts index ca72bfc68a9..38e08ed2746 100644 --- a/packages/@romejs/js-compiler/index.ts +++ b/packages/@romejs/js-compiler/index.ts @@ -19,8 +19,8 @@ export * from './lint/decisions'; export {default as lint} from './lint/index'; export {default as compile} from './api/compile'; export { - default as analyzeDependencies, - mergeAnalyzeDependencies, + default as analyzeDependencies, + mergeAnalyzeDependencies, } from './api/analyzeDependencies/index'; // scope @@ -34,8 +34,8 @@ export {areAnalyzeDependencyResultsEqual} from './api/analyzeDependencies/utils' export {getPrefixedNamespace as getPrefixedBundleNamespace} from './transforms/compileForBundle/_utils'; export {default as createHook} from './api/createHook'; export { - extractSuppressionsFromProgram, - matchesSuppression, + extractSuppressionsFromProgram, + matchesSuppression, } from './suppressions'; // types diff --git a/packages/@romejs/js-compiler/lib/Cache.ts b/packages/@romejs/js-compiler/lib/Cache.ts index 76a5d62e77c..8b0e96026d6 100644 --- a/packages/@romejs/js-compiler/lib/Cache.ts +++ b/packages/@romejs/js-compiler/lib/Cache.ts @@ -10,66 +10,66 @@ import {Program} from '@romejs/js-ast'; import {JSONObject} from '@romejs/codec-json'; type CacheQuery = { - key: string; - ast: Program; + key: string; + ast: Program; }; let projectIdCounter = 0; const projectToId: WeakMap = new WeakMap(); export default class Cache { - constructor() { - this.cache = new WeakMap(); - } + constructor() { + this.cache = new WeakMap(); + } - cache: WeakMap>; + cache: WeakMap>; - static buildQuery( - req: TransformRequest, - additionalOptions?: JSONObject, - ): CacheQuery { - const {ast, project, options} = req; - const keyParts: Array = []; + static buildQuery( + req: TransformRequest, + additionalOptions?: JSONObject, + ): CacheQuery { + const {ast, project, options} = req; + const keyParts: Array = []; - let projectId = projectToId.get(project); - if (projectId === undefined) { - projectId = projectIdCounter++; - projectToId.set(project, projectId); - } + let projectId = projectToId.get(project); + if (projectId === undefined) { + projectId = projectIdCounter++; + projectToId.set(project, projectId); + } - // Add project config cache counter - keyParts.push(String(projectId)); + // Add project config cache counter + keyParts.push(String(projectId)); - // Add options if they exist - const extra = { - ...options, - ...additionalOptions, - }; - if (Object.keys(extra).length > 0) { - keyParts.push(JSON.stringify(extra)); - } + // Add options if they exist + const extra = { + ...options, + ...additionalOptions, + }; + if (Object.keys(extra).length > 0) { + keyParts.push(JSON.stringify(extra)); + } - return { - ast, - key: keyParts.join(';'), - }; - } + return { + ast, + key: keyParts.join(';'), + }; + } - get(query: CacheQuery): undefined | Result { - const astCache = this.cache.get(query.ast); - if (astCache) { - return astCache.get(query.key); - } else { - return undefined; - } - } + get(query: CacheQuery): undefined | Result { + const astCache = this.cache.get(query.ast); + if (astCache) { + return astCache.get(query.key); + } else { + return undefined; + } + } - set(query: CacheQuery, value: Result) { - let astCache = this.cache.get(query.ast); - if (astCache === undefined) { - astCache = new Map(); - this.cache.set(query.ast, astCache); - } - astCache.set(query.key, value); - } + set(query: CacheQuery, value: Result) { + let astCache = this.cache.get(query.ast); + if (astCache === undefined) { + astCache = new Map(); + this.cache.set(query.ast, astCache); + } + astCache.set(query.key, value); + } } diff --git a/packages/@romejs/js-compiler/lib/CompilerContext.ts b/packages/@romejs/js-compiler/lib/CompilerContext.ts index b1ef7268cc2..276641b029c 100644 --- a/packages/@romejs/js-compiler/lib/CompilerContext.ts +++ b/packages/@romejs/js-compiler/lib/CompilerContext.ts @@ -6,44 +6,44 @@ */ import { - AnyComment, - AnyNode, - ConstSourceType, - Program, - program, + AnyComment, + AnyNode, + ConstSourceType, + Program, + program, } from '@romejs/js-ast'; import { - SourceLocation, - extractSourceLocationRangeFromNodes, + SourceLocation, + extractSourceLocationRangeFromNodes, } from '@romejs/parser-core'; import { - CompilerOptions, - PathOptions, - TransformExitResult, - TransformVisitors, - Transforms, + CompilerOptions, + PathOptions, + TransformExitResult, + TransformVisitors, + Transforms, } from '@romejs/js-compiler'; import { - Diagnostic, - DiagnosticCategory, - DiagnosticDescription, - DiagnosticLocation, - DiagnosticOrigin, - DiagnosticSuppressions, - DiagnosticsProcessor, + Diagnostic, + DiagnosticCategory, + DiagnosticDescription, + DiagnosticLocation, + DiagnosticOrigin, + DiagnosticSuppressions, + DiagnosticsProcessor, } from '@romejs/diagnostics'; import Record from './Record'; import {RootScope} from '../scope/Scope'; import reduce from '../methods/reduce'; import {UnknownFilePath, createUnknownFilePath} from '@romejs/path'; import { - LintCompilerOptionsDecision, - TransformProjectDefinition, - TransformVisitor, + LintCompilerOptionsDecision, + TransformProjectDefinition, + TransformVisitor, } from '../types'; import { - extractSuppressionsFromProgram, - matchesSuppression, + extractSuppressionsFromProgram, + matchesSuppression, } from '../suppressions'; import CommentsConsumer from '@romejs/js-parser/CommentsConsumer'; import {ob1Get0} from '@romejs/ob1'; @@ -54,505 +54,492 @@ import {REDUCE_REMOVE} from '../constants'; import {FileReference} from '@romejs/core'; import {DEFAULT_PROJECT_CONFIG} from '@romejs/project'; import { - buildLintDecisionAdviceAction, - buildLintDecisionGlobalString, - buildLintDecisionString, - deriveDecisionPositionKey, + buildLintDecisionAdviceAction, + buildLintDecisionGlobalString, + buildLintDecisionString, + deriveDecisionPositionKey, } from '../lint/decisions'; export type ContextArg = { - ast: Program; - suppressions?: DiagnosticSuppressions; - ref?: FileReference; - sourceText?: string; - project?: TransformProjectDefinition; - frozen?: boolean; - options?: CompilerOptions; - origin?: DiagnosticOrigin; + ast: Program; + suppressions?: DiagnosticSuppressions; + ref?: FileReference; + sourceText?: string; + project?: TransformProjectDefinition; + frozen?: boolean; + options?: CompilerOptions; + origin?: DiagnosticOrigin; }; type AddDiagnosticResult = { - loc: undefined | DiagnosticLocation; - diagnostic: undefined | Diagnostic; - suppressed: boolean; + loc: undefined | DiagnosticLocation; + diagnostic: undefined | Diagnostic; + suppressed: boolean; }; // We only want a Context to create diagnostics that belong to itself type ContextDiagnostic = Omit & { - marker?: string; + marker?: string; }; type DiagnosticTarget = - | undefined - | { - loc?: SourceLocation; - } - | Array<{ - loc?: SourceLocation; - }>; + | undefined + | { + loc?: SourceLocation; + } + | Array<{ + loc?: SourceLocation; + }>; function getFormattedCodeFromExitResult(result: TransformExitResult): string { - if (Array.isArray(result)) { - // TODO? - return ''; - } else if (result === REDUCE_REMOVE) { - return ''; - } else { - return formatJS(result).code; - } + if (Array.isArray(result)) { + // TODO? + return ''; + } else if (result === REDUCE_REMOVE) { + return ''; + } else { + return formatJS(result).code; + } } export default class CompilerContext { - constructor(arg: ContextArg) { - const { - ast, - origin, - ref, - frozen = false, - options = {}, - sourceText = '', - project = { - folder: undefined, - config: DEFAULT_PROJECT_CONFIG, - }, - suppressions, - } = arg; - - this.records = []; - - this.path = createUnknownFilePath(ast.filename); - this.filename = ast.filename; - this.sourceText = sourceText; - this.displayFilename = - ref === undefined ? ast.filename : ref.relative.join(); - this.frozen = frozen; - this.mtime = ast.mtime; - this.project = project; - this.options = options; - this.origin = origin; - this.cacheDependencies = new Set(); - this.sourceType = ast.sourceType; - this.rootScope = new RootScope(this, ast); - - this.comments = new CommentsConsumer(ast.comments); - this.diagnostics = new DiagnosticsProcessor(); - - if (suppressions === undefined) { - const {suppressions, diagnostics} = extractSuppressionsFromProgram( - this, - ast, - ); - this.suppressions = suppressions; - this.diagnostics.addDiagnostics(diagnostics); - } else { - this.suppressions = suppressions; - } - } - - displayFilename: string; - filename: string; - path: UnknownFilePath; - project: TransformProjectDefinition; - sourceText: string; - - comments: CommentsConsumer; - sourceType: ConstSourceType; - cacheDependencies: Set; - records: Array; - diagnostics: DiagnosticsProcessor; - suppressions: DiagnosticSuppressions; - frozen: boolean; - rootScope: undefined | RootScope; - mtime: undefined | number; - origin: undefined | DiagnosticOrigin; - options: CompilerOptions; - - async normalizeTransforms(transforms: Transforms): Promise { - return Promise.all( - transforms.map(async (visitor) => { - if (typeof visitor === 'function') { - return await visitor(this); - } else { - return visitor; - } - }), - ); - } - - getComments(ids: undefined | Array): Array { - return this.comments.getCommentsFromIds(ids); - } - - hasLocSuppression( - loc: undefined | DiagnosticLocation, - category: DiagnosticCategory, - ): boolean { - if (loc === undefined) { - return false; - } - - for (const suppression of this.suppressions) { - if ( - suppression.category === category && - matchesSuppression(loc, suppression) - ) { - return true; - } - } - - return false; - } - - getRootScope(): RootScope { - const {rootScope} = this; - if (rootScope === undefined) { - throw new Error('Expected root scope'); - } - return rootScope; - } - - getCacheDependencies(): Array { - return Array.from(this.cacheDependencies); - } - - addCacheDependency(filename: string) { - this.cacheDependencies.add(filename); - } - - reduceRoot( - ast: Program, - visitors: TransformVisitor | TransformVisitors, - pathOpts?: PathOptions, - ): Program { - return program.assert( - reduce( - ast, - [...hookVisitors, ...(Array.isArray(visitors) ? visitors : [visitors])], - this, - pathOpts, - ), - ); - } - - reduce( - ast: AnyNode, - visitors: TransformVisitor | TransformVisitors, - pathOpts?: PathOptions, - ): TransformExitResult { - return reduce( - ast, - Array.isArray(visitors) ? visitors : [visitors], - this, - pathOpts, - ); - } - - record(record: Record) { - this.records.push(record); - } - - hasLintDecisions(): boolean { - const {lint} = this.options; - return lint !== undefined && lint.hasDecisions === true; - } - - getLintDecisions(key: undefined | string): Array { - const {lint} = this.options; - if (lint === undefined) { - return []; - } - - const {globalDecisions = []} = lint; - - if (key === undefined) { - return globalDecisions; - } - - const {decisionsByPosition} = lint; - if (decisionsByPosition === undefined) { - return globalDecisions; - } - - return [...globalDecisions, ...(decisionsByPosition[key] || [])]; - } - - addFixableDiagnostic( - nodes: { - target?: AnyNode | Array; - old: Old; - fixed?: New; - suggestions?: Array<{ - description: string; - title: string; - fixed: New; - }>; - }, - description: DiagnosticDescription, - diag: ContextDiagnostic = {}, - ): TransformExitResult { - const {old, fixed: defaultFixed, suggestions} = nodes; - const target = nodes.target === undefined ? nodes.old : nodes.target; - - const {category} = description; - const advice = [...description.advice]; - const loc = this.getLoc(target); - const oldCode = - loc === undefined - ? '' - : this.sourceText.slice( - ob1Get0(loc.start.index), - ob1Get0(loc.end.index), - ); - - let fixed: undefined | New = defaultFixed; - - // Add recommended fix - if (defaultFixed !== undefined) { - advice.push({ - type: 'log', - category: 'info', - text: 'Recommended fix', - }); - - advice.push({ - type: 'diff', - diff: stringDiff(oldCode, getFormattedCodeFromExitResult(defaultFixed)), - }); - if (loc === undefined) { - advice.push({ - type: 'log', - category: 'error', - text: 'Unable to find target location', - }); - } else { - advice.push( - buildLintDecisionAdviceAction({ - filename: this.displayFilename, - decision: buildLintDecisionString({ - action: 'fix', - filename: this.displayFilename, - category, - start: loc.start, - }), - shortcut: 'f', - noun: 'Apply fix', - instruction: 'To apply this fix run', - }), - ); - - advice.push( - buildLintDecisionAdviceAction({ - extra: true, - noun: 'Apply fix for ALL files with this category', - instruction: 'To apply fix for ALL files with this category run', - decision: buildLintDecisionGlobalString('fix', category), - }), - ); - } - } - - if (suggestions !== undefined) { - // If we have lint decisions then find the fix that corresponds with this suggestion - if (this.hasLintDecisions()) { - const decisions = this.getLintDecisions( - deriveDecisionPositionKey('fix', loc), - ); - for (const decision of decisions) { - if ( - decision.category === category && - decision.action === 'fix' && - decision.id !== undefined - ) { - const suggestion = suggestions[decision.id]; - if (suggestion !== undefined) { - fixed = suggestion.fixed; - } - } - } - } - - // Add advice suggestions - let index = 0; - for (const suggestion of suggestions) { - const num = index + 1; - - const titlePrefix = - suggestions.length === 1 ? 'Suggested fix' : `Suggested fix #${num}`; - advice.push({ - type: 'log', - category: 'none', - text: `${titlePrefix}: ${suggestion.title}`, - }); - - advice.push({ - type: 'diff', - diff: stringDiff( - oldCode, - getFormattedCodeFromExitResult(suggestion.fixed), - ), - }); - - advice.push({ - type: 'log', - category: 'info', - text: suggestion.description, - }); - - if (loc === undefined) { - advice.push({ - type: 'log', - category: 'error', - text: 'Unable to find target location', - }); - } else { - advice.push( - buildLintDecisionAdviceAction({ - noun: suggestions.length === 1 - ? 'Apply suggested fix' - : `Apply suggested fix "${suggestion.title}"`, - shortcut: String(num), - instruction: 'To apply this fix run', - filename: this.displayFilename, - decision: buildLintDecisionString({ - filename: this.displayFilename, - action: 'fix', - category, - start: loc.start, - id: index, - }), - }), - ); - } - - index++; - } - } - - const {suppressed} = this.addLocDiagnostic( - loc, - { - ...description, - advice, - }, - { - ...diag, - fixable: true, - }, - ); - - if (suppressed || fixed === undefined) { - return old; - } - - return fixed; - } - - addLocDiagnostic( - loc: undefined | DiagnosticLocation, - description: DiagnosticDescription, - contextDiag: ContextDiagnostic = {}, - ): AddDiagnosticResult { - let origins: Array = []; - if (this.origin !== undefined) { - origins.push(this.origin); - } - if (contextDiag.origins !== undefined) { - origins = origins.concat(contextDiag.origins); - } - - if (loc !== undefined && loc.filename !== this.filename) { - throw new Error( - `Trying to add a location from ${loc.filename} on a Context from ${this.path}`, - ); - } - - const {category, advice = []} = description; - if (loc !== undefined && loc.start !== undefined) { - advice.push( - buildLintDecisionAdviceAction({ - noun: 'Add suppression comment', - shortcut: 's', - instruction: 'To suppress this error run', - filename: this.displayFilename, - decision: buildLintDecisionString({ - filename: this.displayFilename, - action: 'suppress', - category, - start: loc.start, - }), - }), - ); - - advice.push( - buildLintDecisionAdviceAction({ - extra: true, - noun: 'Add suppression comments for ALL files with this category', - instruction: 'To add suppression comments for ALL files with this category run', - decision: buildLintDecisionGlobalString('suppress', category), - }), - ); - } - - const {marker, ...diag} = contextDiag; - const diagnostic = this.diagnostics.addDiagnostic({ - ...diag, - description: { - ...description, - advice, - }, - location: { - marker, - mtime: this.mtime, - filename: this.filename, - start: loc === undefined ? undefined : loc.start, - end: loc === undefined ? undefined : loc.end, - language: 'js', - sourceType: this.sourceType, - }, - origins, - }); - - let suppressed = this.hasLocSuppression(loc, description.category); - - // If we've been passed lint decisions then consider it suppressed unless we have been specifically told to fix it - const diagCategory = description.category; - if (this.hasLintDecisions()) { - suppressed = true; - - const decisions = this.getLintDecisions( - deriveDecisionPositionKey('fix', loc), - ); - for (const {category, action} of decisions) { - if (category === diagCategory && action === 'fix') { - suppressed = false; - } - } - } - - return { - loc, - diagnostic, - suppressed, - }; - } - - getLoc(node: DiagnosticTarget): undefined | SourceLocation { - if (node === undefined) { - return undefined; - } - - if (Array.isArray(node)) { - return extractSourceLocationRangeFromNodes(node); - } else { - return node.loc; - } - } - - addNodeDiagnostic( - node: DiagnosticTarget, - description: DiagnosticDescription, - diag: ContextDiagnostic = {}, - ): AddDiagnosticResult { - return this.addLocDiagnostic(this.getLoc(node), description, diag); - } + constructor(arg: ContextArg) { + const { + ast, + origin, + ref, + frozen = false, + options = {}, + sourceText = '', + project = { + folder: undefined, + config: DEFAULT_PROJECT_CONFIG, + }, + suppressions, + } = arg; + + this.records = []; + + this.path = createUnknownFilePath(ast.filename); + this.filename = ast.filename; + this.sourceText = sourceText; + this.displayFilename = ref === undefined ? ast.filename : ref.relative.join(); + this.frozen = frozen; + this.mtime = ast.mtime; + this.project = project; + this.options = options; + this.origin = origin; + this.cacheDependencies = new Set(); + this.sourceType = ast.sourceType; + this.rootScope = new RootScope(this, ast); + + this.comments = new CommentsConsumer(ast.comments); + this.diagnostics = new DiagnosticsProcessor(); + + if (suppressions === undefined) { + const {suppressions, diagnostics} = extractSuppressionsFromProgram(this, ast); + this.suppressions = suppressions; + this.diagnostics.addDiagnostics(diagnostics); + } else { + this.suppressions = suppressions; + } + } + + displayFilename: string; + filename: string; + path: UnknownFilePath; + project: TransformProjectDefinition; + sourceText: string; + + comments: CommentsConsumer; + sourceType: ConstSourceType; + cacheDependencies: Set; + records: Array; + diagnostics: DiagnosticsProcessor; + suppressions: DiagnosticSuppressions; + frozen: boolean; + rootScope: undefined | RootScope; + mtime: undefined | number; + origin: undefined | DiagnosticOrigin; + options: CompilerOptions; + + async normalizeTransforms(transforms: Transforms): Promise { + return Promise.all( + transforms.map(async (visitor) => { + if (typeof visitor === 'function') { + return await visitor(this); + } else { + return visitor; + } + }), + ); + } + + getComments(ids: undefined | Array): Array { + return this.comments.getCommentsFromIds(ids); + } + + hasLocSuppression( + loc: undefined | DiagnosticLocation, + category: DiagnosticCategory, + ): boolean { + if (loc === undefined) { + return false; + } + + for (const suppression of this.suppressions) { + if (suppression.category === category && matchesSuppression(loc, suppression)) { + return true; + } + } + + return false; + } + + getRootScope(): RootScope { + const {rootScope} = this; + if (rootScope === undefined) { + throw new Error('Expected root scope'); + } + return rootScope; + } + + getCacheDependencies(): Array { + return Array.from(this.cacheDependencies); + } + + addCacheDependency(filename: string) { + this.cacheDependencies.add(filename); + } + + reduceRoot( + ast: Program, + visitors: TransformVisitor | TransformVisitors, + pathOpts?: PathOptions, + ): Program { + return program.assert( + reduce( + ast, + [...hookVisitors, ...(Array.isArray(visitors) ? visitors : [visitors])], + this, + pathOpts, + ), + ); + } + + reduce( + ast: AnyNode, + visitors: TransformVisitor | TransformVisitors, + pathOpts?: PathOptions, + ): TransformExitResult { + return reduce( + ast, + Array.isArray(visitors) ? visitors : [visitors], + this, + pathOpts, + ); + } + + record(record: Record) { + this.records.push(record); + } + + hasLintDecisions(): boolean { + const {lint} = this.options; + return lint !== undefined && lint.hasDecisions === true; + } + + getLintDecisions(key: undefined | string): Array { + const {lint} = this.options; + if (lint === undefined) { + return []; + } + + const {globalDecisions = []} = lint; + + if (key === undefined) { + return globalDecisions; + } + + const {decisionsByPosition} = lint; + if (decisionsByPosition === undefined) { + return globalDecisions; + } + + return [...globalDecisions, ...(decisionsByPosition[key] || [])]; + } + + addFixableDiagnostic( + nodes: { + target?: AnyNode | Array; + old: Old; + fixed?: New; + suggestions?: Array<{ + description: string; + title: string; + fixed: New; + }>; + }, + description: DiagnosticDescription, + diag: ContextDiagnostic = {}, + ): TransformExitResult { + const {old, fixed: defaultFixed, suggestions} = nodes; + const target = nodes.target === undefined ? nodes.old : nodes.target; + + const {category} = description; + const advice = [...description.advice]; + const loc = this.getLoc(target); + const oldCode = + loc === undefined + ? '' + : this.sourceText.slice(ob1Get0(loc.start.index), ob1Get0(loc.end.index)); + + let fixed: undefined | New = defaultFixed; + + // Add recommended fix + if (defaultFixed !== undefined) { + advice.push({ + type: 'log', + category: 'info', + text: 'Recommended fix', + }); + + advice.push({ + type: 'diff', + diff: stringDiff(oldCode, getFormattedCodeFromExitResult(defaultFixed)), + }); + if (loc === undefined) { + advice.push({ + type: 'log', + category: 'error', + text: 'Unable to find target location', + }); + } else { + advice.push( + buildLintDecisionAdviceAction({ + filename: this.displayFilename, + decision: buildLintDecisionString({ + action: 'fix', + filename: this.displayFilename, + category, + start: loc.start, + }), + shortcut: 'f', + noun: 'Apply fix', + instruction: 'To apply this fix run', + }), + ); + + advice.push( + buildLintDecisionAdviceAction({ + extra: true, + noun: 'Apply fix for ALL files with this category', + instruction: 'To apply fix for ALL files with this category run', + decision: buildLintDecisionGlobalString('fix', category), + }), + ); + } + } + + if (suggestions !== undefined) { + // If we have lint decisions then find the fix that corresponds with this suggestion + if (this.hasLintDecisions()) { + const decisions = this.getLintDecisions( + deriveDecisionPositionKey('fix', loc), + ); + for (const decision of decisions) { + if ( + decision.category === category && + decision.action === 'fix' && + decision.id !== undefined + ) { + const suggestion = suggestions[decision.id]; + if (suggestion !== undefined) { + fixed = suggestion.fixed; + } + } + } + } + + // Add advice suggestions + let index = 0; + for (const suggestion of suggestions) { + const num = index + 1; + + const titlePrefix = + suggestions.length === 1 ? 'Suggested fix' : `Suggested fix #${num}`; + advice.push({ + type: 'log', + category: 'none', + text: `${titlePrefix}: ${suggestion.title}`, + }); + + advice.push({ + type: 'diff', + diff: stringDiff(oldCode, getFormattedCodeFromExitResult(suggestion.fixed)), + }); + + advice.push({ + type: 'log', + category: 'info', + text: suggestion.description, + }); + + if (loc === undefined) { + advice.push({ + type: 'log', + category: 'error', + text: 'Unable to find target location', + }); + } else { + advice.push( + buildLintDecisionAdviceAction({ + noun: suggestions.length === 1 + ? 'Apply suggested fix' + : `Apply suggested fix "${suggestion.title}"`, + shortcut: String(num), + instruction: 'To apply this fix run', + filename: this.displayFilename, + decision: buildLintDecisionString({ + filename: this.displayFilename, + action: 'fix', + category, + start: loc.start, + id: index, + }), + }), + ); + } + + index++; + } + } + + const {suppressed} = this.addLocDiagnostic( + loc, + { + ...description, + advice, + }, + { + ...diag, + fixable: true, + }, + ); + + if (suppressed || fixed === undefined) { + return old; + } + + return fixed; + } + + addLocDiagnostic( + loc: undefined | DiagnosticLocation, + description: DiagnosticDescription, + contextDiag: ContextDiagnostic = {}, + ): AddDiagnosticResult { + let origins: Array = []; + if (this.origin !== undefined) { + origins.push(this.origin); + } + if (contextDiag.origins !== undefined) { + origins = origins.concat(contextDiag.origins); + } + + if (loc !== undefined && loc.filename !== this.filename) { + throw new Error( + `Trying to add a location from ${loc.filename} on a Context from ${this.path}`, + ); + } + + const {category, advice = []} = description; + if (loc !== undefined && loc.start !== undefined) { + advice.push( + buildLintDecisionAdviceAction({ + noun: 'Add suppression comment', + shortcut: 's', + instruction: 'To suppress this error run', + filename: this.displayFilename, + decision: buildLintDecisionString({ + filename: this.displayFilename, + action: 'suppress', + category, + start: loc.start, + }), + }), + ); + + advice.push( + buildLintDecisionAdviceAction({ + extra: true, + noun: 'Add suppression comments for ALL files with this category', + instruction: 'To add suppression comments for ALL files with this category run', + decision: buildLintDecisionGlobalString('suppress', category), + }), + ); + } + + const {marker, ...diag} = contextDiag; + const diagnostic = this.diagnostics.addDiagnostic({ + ...diag, + description: { + ...description, + advice, + }, + location: { + marker, + mtime: this.mtime, + filename: this.filename, + start: loc === undefined ? undefined : loc.start, + end: loc === undefined ? undefined : loc.end, + language: 'js', + sourceType: this.sourceType, + }, + origins, + }); + + let suppressed = this.hasLocSuppression(loc, description.category); + + // If we've been passed lint decisions then consider it suppressed unless we have been specifically told to fix it + const diagCategory = description.category; + if (this.hasLintDecisions()) { + suppressed = true; + + const decisions = this.getLintDecisions( + deriveDecisionPositionKey('fix', loc), + ); + for (const {category, action} of decisions) { + if (category === diagCategory && action === 'fix') { + suppressed = false; + } + } + } + + return { + loc, + diagnostic, + suppressed, + }; + } + + getLoc(node: DiagnosticTarget): undefined | SourceLocation { + if (node === undefined) { + return undefined; + } + + if (Array.isArray(node)) { + return extractSourceLocationRangeFromNodes(node); + } else { + return node.loc; + } + } + + addNodeDiagnostic( + node: DiagnosticTarget, + description: DiagnosticDescription, + diag: ContextDiagnostic = {}, + ): AddDiagnosticResult { + return this.addLocDiagnostic(this.getLoc(node), description, diag); + } } diff --git a/packages/@romejs/js-compiler/lib/DiagnosticsDuplicateHelper.ts b/packages/@romejs/js-compiler/lib/DiagnosticsDuplicateHelper.ts index 3370d805441..eb5aaf00e77 100644 --- a/packages/@romejs/js-compiler/lib/DiagnosticsDuplicateHelper.ts +++ b/packages/@romejs/js-compiler/lib/DiagnosticsDuplicateHelper.ts @@ -6,70 +6,70 @@ */ import { - DiagnosticCategory, - DiagnosticDescription, - DiagnosticLocation, - buildDuplicateLocationAdvice, + DiagnosticCategory, + DiagnosticDescription, + DiagnosticLocation, + buildDuplicateLocationAdvice, } from '@romejs/diagnostics'; import CompilerContext from './CompilerContext'; type DescriptionFactory = (key: string) => DiagnosticDescription; export class DiagnosticsDuplicateHelper { - constructor(context: CompilerContext, descriptionFactory: DescriptionFactory) { - this.context = context; - this.category = descriptionFactory('').category; - this.descriptionFactory = descriptionFactory; - this.locations = new Map(); - } + constructor(context: CompilerContext, descriptionFactory: DescriptionFactory) { + this.context = context; + this.category = descriptionFactory('').category; + this.descriptionFactory = descriptionFactory; + this.locations = new Map(); + } - category: DiagnosticCategory; - descriptionFactory: DescriptionFactory; - locations: Map>; - context: CompilerContext; + category: DiagnosticCategory; + descriptionFactory: DescriptionFactory; + locations: Map>; + context: CompilerContext; - addLocation( - key: string, - location: undefined | DiagnosticLocation, - ): { - duplicate: boolean; - } { - const isSuppressed = this.context.hasLocSuppression(location, this.category); - if (isSuppressed) { - // If this location has had it's diagnostic suppressed then we don't want to return - // that it was a duplicate even if there's multiple occurences - return {duplicate: false}; - } + addLocation( + key: string, + location: undefined | DiagnosticLocation, + ): { + duplicate: boolean; + } { + const isSuppressed = this.context.hasLocSuppression(location, this.category); + if (isSuppressed) { + // If this location has had it's diagnostic suppressed then we don't want to return + // that it was a duplicate even if there's multiple occurences + return {duplicate: false}; + } - let locations = this.locations.get(key); - if (locations === undefined) { - locations = []; - this.locations.set(key, locations); - } - locations.push(location); - return {duplicate: locations.length > 1}; - } + let locations = this.locations.get(key); + if (locations === undefined) { + locations = []; + this.locations.set(key, locations); + } + locations.push(location); + return {duplicate: locations.length > 1}; + } - process() { - for (const [key, locations] of this.locations) { - if (locations.length <= 1) { - continue; - } + process() { + for (const [key, locations] of this.locations) { + if (locations.length <= 1) { + continue; + } - const description = this.descriptionFactory(key); - const firstLocation = locations[0]; - const restLocations = locations.slice(1); + const description = this.descriptionFactory(key); + const firstLocation = locations[0]; + const restLocations = locations.slice(1); - this.context.addLocDiagnostic( - firstLocation, - { - ...description, - advice: [ - ...description.advice, - ...buildDuplicateLocationAdvice(restLocations), - ], - }, - ); - } - } + this.context.addLocDiagnostic( + firstLocation, + { + ...description, + advice: [ + ...description.advice, + ...buildDuplicateLocationAdvice(restLocations), + ], + }, + ); + } + } } diff --git a/packages/@romejs/js-compiler/lib/Path.ts b/packages/@romejs/js-compiler/lib/Path.ts index 6bcb012e7cf..ce846272c41 100644 --- a/packages/@romejs/js-compiler/lib/Path.ts +++ b/packages/@romejs/js-compiler/lib/Path.ts @@ -7,269 +7,269 @@ import {AnyNode, MOCK_PARENT} from '@romejs/js-ast'; import { - CompilerContext, - Scope, - TransformVisitor, - TransformVisitors, + CompilerContext, + Scope, + TransformVisitor, + TransformVisitors, } from '@romejs/js-compiler'; import { - AnyHookDescriptor, - HookDescriptor, - HookInstance, + AnyHookDescriptor, + HookDescriptor, + HookInstance, } from '../api/createHook'; import reduce from '../methods/reduce'; import {TransformExitResult} from '../types'; export type PathOptions = { - ancestryPaths?: Array; - nodeKey?: string; - listKey?: number; - parentScope?: Scope; - scope?: Scope; - noArrays?: boolean; - noScopeCreation?: boolean; - hooks?: Handlers; - isMock?: boolean; + ancestryPaths?: Array; + nodeKey?: string; + listKey?: number; + parentScope?: Scope; + scope?: Scope; + noArrays?: boolean; + noScopeCreation?: boolean; + hooks?: Handlers; + isMock?: boolean; }; type Handlers = Array; export default class Path { - constructor(node: AnyNode, context: CompilerContext, opts: PathOptions) { - const ancestryPaths = opts.ancestryPaths || []; - this.ancestryPaths = ancestryPaths; - - if (node === MOCK_PARENT) { - this.parentPath = this; - } else if (ancestryPaths.length === 0) { - this.parentPath = new Path( - MOCK_PARENT, - context, - { - isMock: true, - }, - ); - } else { - this.parentPath = ancestryPaths[0]; - } - - this.node = node; - this.parent = this.parentPath.node; - this.context = context; - - const parentScope = - opts.parentScope === undefined ? context.getRootScope() : opts.parentScope; - - let scope = opts.scope; - if (scope === undefined) { - if (opts.noScopeCreation === true) { - scope = parentScope; - } else { - scope = parentScope.evaluate(node, this.parent, true); - } - } - this.scope = scope; - - this.nodeKey = opts.nodeKey; - this.listKey = opts.listKey; - - this.isMock = opts.isMock === true; - this.opts = opts; - - this.hooks = opts.hooks === undefined ? [] : opts.hooks; - } - - context: CompilerContext; - node: AnyNode; - parent: AnyNode; - scope: Scope; - hooks: Handlers; - opts: PathOptions; - isMock: boolean; - - ancestryPaths: Array; - parentPath: Path; - - nodeKey: undefined | string; - listKey: undefined | number; - - callHook( - // rome-ignore lint/noExplicitAny - descriptor: HookDescriptor, - arg: CallArg, - optionalRet?: CallReturn, - requiredDepth?: number, - ): CallReturn { - const hook = this.findHook(descriptor, requiredDepth); - if (hook === undefined) { - if (optionalRet === undefined) { - throw new Error(`No ${descriptor.name} hook found`); - } else { - return optionalRet; - } - } - if (descriptor.call === undefined) { - throw new Error("Hook doesn't have a call method"); - } - - const {depth, ref} = hook; - const {state, value, bubble} = descriptor.call(this, ref.state, arg); - ref.state = state; - - if (bubble === true) { - return this.callHook(descriptor, arg, value, depth + 1); - } else { - return value; - } - } - - provideHook( - // rome-ignore lint/noExplicitAny - descriptor: HookDescriptor, - state?: State, - ): AnyNode { - this.hooks.push({ - state: { - ...descriptor.initialState, - ...state, - }, - descriptor, - }); - - return this.node; - } - - findHook( - descriptor: AnyHookDescriptor, - requiredDepth: number = 0, - ): - | undefined - | { - ref: HookInstance; - depth: number; - } { - let depth = 0; - for (const {hooks} of this.ancestryPaths) { - for (const hook of hooks) { - if (hook.descriptor === descriptor) { - if (depth === requiredDepth) { - return {ref: hook, depth}; - } else { - depth++; - } - } - } - } - return undefined; - } - - findAncestry(callback: (path: Path) => boolean): undefined | Path { - for (const path of this.ancestryPaths) { - if (callback(path)) { - return path; - } - } - return undefined; - } - - getChildPath(key: string): Path { - // rome-ignore lint/noExplicitAny - const node = (this.node as any)[key]; - if (node === undefined) { - throw new Error( - `Attempted to get child path for ${key} but no such node existed`, - ); - } - - return new Path( - node, - this.context, - { - parentScope: this.scope, - ancestryPaths: this.ancestryPaths.concat([this]), - nodeKey: key, - }, - ); - } - - getChildPaths(key: string): Array { - // rome-ignore lint/noExplicitAny - const nodes = (this.node as any)[key]; - - if (nodes === undefined) { - throw new Error( - `Attempted to get child paths for ${key} but no such node existed`, - ); - } - - if (!Array.isArray(nodes)) { - throw new Error(`Expected child nodes for ${key} to be an array`); - } - - const ancestryPaths = this.ancestryPaths.concat([this]); - - return nodes.map((node: AnyNode, i: number) => { - return new Path( - node, - this.context, - { - parentScope: this.scope, - ancestryPaths, - listKey: i, - nodeKey: key, - }, - ); - }); - } - - getPathKeys(): Array { - const parts = []; - - let path: undefined | Path = this; - while (path !== undefined && !path.isMock) { - if (path.listKey !== undefined) { - parts.push(String(path.listKey)); - } - if (path.nodeKey !== undefined) { - parts.push(path.nodeKey); - } - path = path.parentPath; - } - - return parts.reverse(); - } - - fork(newNode: AnyNode): Path { - return new Path(newNode, this.context, this.getPathOptions()); - } - - getPathOptions(): PathOptions { - return { - ...this.opts, - hooks: this.hooks, - parentScope: this.scope === undefined ? undefined : this.scope.parentScope, - }; - } - - traverse(name: string, callback: (path: Path) => void) { - this.reduce({ - name, - enter(path: Path) { - callback(path); - return path.node; - }, - }); - } - - reduce( - visitors: TransformVisitor | TransformVisitors, - opts?: Partial, - ): TransformExitResult { - return reduce( - this.node, - Array.isArray(visitors) ? visitors : [visitors], - this.context, - {...this.getPathOptions(), ...opts}, - ); - } + constructor(node: AnyNode, context: CompilerContext, opts: PathOptions) { + const ancestryPaths = opts.ancestryPaths || []; + this.ancestryPaths = ancestryPaths; + + if (node === MOCK_PARENT) { + this.parentPath = this; + } else if (ancestryPaths.length === 0) { + this.parentPath = new Path( + MOCK_PARENT, + context, + { + isMock: true, + }, + ); + } else { + this.parentPath = ancestryPaths[0]; + } + + this.node = node; + this.parent = this.parentPath.node; + this.context = context; + + const parentScope = + opts.parentScope === undefined ? context.getRootScope() : opts.parentScope; + + let scope = opts.scope; + if (scope === undefined) { + if (opts.noScopeCreation === true) { + scope = parentScope; + } else { + scope = parentScope.evaluate(node, this.parent, true); + } + } + this.scope = scope; + + this.nodeKey = opts.nodeKey; + this.listKey = opts.listKey; + + this.isMock = opts.isMock === true; + this.opts = opts; + + this.hooks = opts.hooks === undefined ? [] : opts.hooks; + } + + context: CompilerContext; + node: AnyNode; + parent: AnyNode; + scope: Scope; + hooks: Handlers; + opts: PathOptions; + isMock: boolean; + + ancestryPaths: Array; + parentPath: Path; + + nodeKey: undefined | string; + listKey: undefined | number; + + callHook( + // rome-ignore lint/noExplicitAny + descriptor: HookDescriptor, + arg: CallArg, + optionalRet?: CallReturn, + requiredDepth?: number, + ): CallReturn { + const hook = this.findHook(descriptor, requiredDepth); + if (hook === undefined) { + if (optionalRet === undefined) { + throw new Error(`No ${descriptor.name} hook found`); + } else { + return optionalRet; + } + } + if (descriptor.call === undefined) { + throw new Error("Hook doesn't have a call method"); + } + + const {depth, ref} = hook; + const {state, value, bubble} = descriptor.call(this, ref.state, arg); + ref.state = state; + + if (bubble === true) { + return this.callHook(descriptor, arg, value, depth + 1); + } else { + return value; + } + } + + provideHook( + // rome-ignore lint/noExplicitAny + descriptor: HookDescriptor, + state?: State, + ): AnyNode { + this.hooks.push({ + state: { + ...descriptor.initialState, + ...state, + }, + descriptor, + }); + + return this.node; + } + + findHook( + descriptor: AnyHookDescriptor, + requiredDepth: number = 0, + ): + | undefined + | { + ref: HookInstance; + depth: number; + } { + let depth = 0; + for (const {hooks} of this.ancestryPaths) { + for (const hook of hooks) { + if (hook.descriptor === descriptor) { + if (depth === requiredDepth) { + return {ref: hook, depth}; + } else { + depth++; + } + } + } + } + return undefined; + } + + findAncestry(callback: (path: Path) => boolean): undefined | Path { + for (const path of this.ancestryPaths) { + if (callback(path)) { + return path; + } + } + return undefined; + } + + getChildPath(key: string): Path { + // rome-ignore lint/noExplicitAny + const node = (this.node as any)[key]; + if (node === undefined) { + throw new Error( + `Attempted to get child path for ${key} but no such node existed`, + ); + } + + return new Path( + node, + this.context, + { + parentScope: this.scope, + ancestryPaths: this.ancestryPaths.concat([this]), + nodeKey: key, + }, + ); + } + + getChildPaths(key: string): Array { + // rome-ignore lint/noExplicitAny + const nodes = (this.node as any)[key]; + + if (nodes === undefined) { + throw new Error( + `Attempted to get child paths for ${key} but no such node existed`, + ); + } + + if (!Array.isArray(nodes)) { + throw new Error(`Expected child nodes for ${key} to be an array`); + } + + const ancestryPaths = this.ancestryPaths.concat([this]); + + return nodes.map((node: AnyNode, i: number) => { + return new Path( + node, + this.context, + { + parentScope: this.scope, + ancestryPaths, + listKey: i, + nodeKey: key, + }, + ); + }); + } + + getPathKeys(): Array { + const parts = []; + + let path: undefined | Path = this; + while (path !== undefined && !path.isMock) { + if (path.listKey !== undefined) { + parts.push(String(path.listKey)); + } + if (path.nodeKey !== undefined) { + parts.push(path.nodeKey); + } + path = path.parentPath; + } + + return parts.reverse(); + } + + fork(newNode: AnyNode): Path { + return new Path(newNode, this.context, this.getPathOptions()); + } + + getPathOptions(): PathOptions { + return { + ...this.opts, + hooks: this.hooks, + parentScope: this.scope === undefined ? undefined : this.scope.parentScope, + }; + } + + traverse(name: string, callback: (path: Path) => void) { + this.reduce({ + name, + enter(path: Path) { + callback(path); + return path.node; + }, + }); + } + + reduce( + visitors: TransformVisitor | TransformVisitors, + opts?: Partial, + ): TransformExitResult { + return reduce( + this.node, + Array.isArray(visitors) ? visitors : [visitors], + this.context, + {...this.getPathOptions(), ...opts}, + ); + } } diff --git a/packages/@romejs/js-compiler/lint/decisions.ts b/packages/@romejs/js-compiler/lint/decisions.ts index 669ab9f2322..e7a7448539f 100644 --- a/packages/@romejs/js-compiler/lint/decisions.ts +++ b/packages/@romejs/js-compiler/lint/decisions.ts @@ -7,15 +7,15 @@ import {Position} from '@romejs/parser-core'; import { - DiagnosticAdviceAction, - DiagnosticCategory, - DiagnosticDescriptionOptionalCategory, - DiagnosticLocation, - descriptions, + DiagnosticAdviceAction, + DiagnosticCategory, + DiagnosticDescriptionOptionalCategory, + DiagnosticLocation, + descriptions, } from '@romejs/diagnostics'; import { - LintCompilerOptionsDecision, - LintCompilerOptionsDecisionAction, + LintCompilerOptionsDecision, + LintCompilerOptionsDecisionAction, } from '../types'; import {ob1Get0, ob1Get1} from '@romejs/ob1'; import {AbsoluteFilePath} from '@romejs/path'; @@ -23,186 +23,186 @@ import {LinterCompilerOptionsPerFile} from '@romejs/core/master/linter/Linter'; import {escapeSplit} from '@romejs/string-utils'; type UnexpectedDecision = ( - description: DiagnosticDescriptionOptionalCategory, + description: DiagnosticDescriptionOptionalCategory, ) => void; function validateAction( - raw: string, - unexpected: UnexpectedDecision, + raw: string, + unexpected: UnexpectedDecision, ): undefined | LintCompilerOptionsDecisionAction { - if (raw === 'fix' || raw === 'suppress' || raw === 'ignore') { - return raw; - } else { - unexpected(descriptions.LINT_COMMAND.INVALID_DECISION_ACTION(raw)); - return undefined; - } + if (raw === 'fix' || raw === 'suppress' || raw === 'ignore') { + return raw; + } else { + unexpected(descriptions.LINT_COMMAND.INVALID_DECISION_ACTION(raw)); + return undefined; + } } export function deriveDecisionPositionKey( - action: LintCompilerOptionsDecisionAction, - loc: undefined | DiagnosticLocation, + action: LintCompilerOptionsDecisionAction, + loc: undefined | DiagnosticLocation, ): undefined | string { - if (loc === undefined) { - return undefined; - } - - const {start} = loc; - if (start === undefined) { - return undefined; - } - - if (action === 'suppress') { - return `${ob1Get1(start.line)}`; - } else { - return `${ob1Get1(start.line)}:${ob1Get0(start.column)}`; - } + if (loc === undefined) { + return undefined; + } + + const {start} = loc; + if (start === undefined) { + return undefined; + } + + if (action === 'suppress') { + return `${ob1Get1(start.line)}`; + } else { + return `${ob1Get1(start.line)}:${ob1Get0(start.column)}`; + } } export function parseDecisionStrings( - decisions: Array, - cwd: AbsoluteFilePath, - unexpected: UnexpectedDecision, + decisions: Array, + cwd: AbsoluteFilePath, + unexpected: UnexpectedDecision, ): { - lintCompilerOptionsPerFile: LinterCompilerOptionsPerFile; - globalDecisions: Array; + lintCompilerOptionsPerFile: LinterCompilerOptionsPerFile; + globalDecisions: Array; } { - const lintCompilerOptionsPerFile: LinterCompilerOptionsPerFile = {}; - const globalDecisions: Array = []; - - function parseGlobalDecision(parts: Array, i: number) { - if (parts.length !== 2) { - unexpected(descriptions.LINT_COMMAND.INVALID_DECISION_PART_COUNT(i)); - } - - const [rawAction, rawCategory] = parts; - - const action = validateAction(rawAction, unexpected); - if (action === undefined) { - return; - } - - const category = (rawCategory as DiagnosticCategory); - globalDecisions.push({category, action}); - } - - function parseLineDecision(parts: Array, i: number) { - if (parts.length < 4 || parts.length > 5) { - unexpected(descriptions.LINT_COMMAND.INVALID_DECISION_PART_COUNT(i)); - } - - const [rawAction, rawCategory, rawFilename, pos, id] = parts; - - const action = validateAction(rawAction, unexpected); - if (action === undefined) { - return; - } - - const category = (rawCategory as DiagnosticCategory); - const resolvedFilename = cwd.resolve(rawFilename).join(); - - let compilerOptions = lintCompilerOptionsPerFile[resolvedFilename]; - if (compilerOptions === undefined) { - compilerOptions = { - hasDecisions: true, - globalDecisions: [], - decisionsByPosition: {}, - }; - lintCompilerOptionsPerFile[resolvedFilename] = compilerOptions; - } - - let decisionsForPosition = compilerOptions.decisionsByPosition[pos]; - if (decisionsForPosition === undefined) { - decisionsForPosition = []; - compilerOptions.decisionsByPosition[pos] = decisionsForPosition; - } - - decisionsForPosition.push({ - action, - category, - id: id === undefined ? undefined : Number(id), - }); - } - - for (let i = 0; i < decisions.length; i++) { - const segment = decisions[i]; - const parts = escapeSplit(segment, '-'); - - if (parts[0] === 'global') { - parseGlobalDecision(parts.slice(1), i); - } else { - parseLineDecision(parts, i); - } - } - - return {lintCompilerOptionsPerFile, globalDecisions}; + const lintCompilerOptionsPerFile: LinterCompilerOptionsPerFile = {}; + const globalDecisions: Array = []; + + function parseGlobalDecision(parts: Array, i: number) { + if (parts.length !== 2) { + unexpected(descriptions.LINT_COMMAND.INVALID_DECISION_PART_COUNT(i)); + } + + const [rawAction, rawCategory] = parts; + + const action = validateAction(rawAction, unexpected); + if (action === undefined) { + return; + } + + const category = (rawCategory as DiagnosticCategory); + globalDecisions.push({category, action}); + } + + function parseLineDecision(parts: Array, i: number) { + if (parts.length < 4 || parts.length > 5) { + unexpected(descriptions.LINT_COMMAND.INVALID_DECISION_PART_COUNT(i)); + } + + const [rawAction, rawCategory, rawFilename, pos, id] = parts; + + const action = validateAction(rawAction, unexpected); + if (action === undefined) { + return; + } + + const category = (rawCategory as DiagnosticCategory); + const resolvedFilename = cwd.resolve(rawFilename).join(); + + let compilerOptions = lintCompilerOptionsPerFile[resolvedFilename]; + if (compilerOptions === undefined) { + compilerOptions = { + hasDecisions: true, + globalDecisions: [], + decisionsByPosition: {}, + }; + lintCompilerOptionsPerFile[resolvedFilename] = compilerOptions; + } + + let decisionsForPosition = compilerOptions.decisionsByPosition[pos]; + if (decisionsForPosition === undefined) { + decisionsForPosition = []; + compilerOptions.decisionsByPosition[pos] = decisionsForPosition; + } + + decisionsForPosition.push({ + action, + category, + id: id === undefined ? undefined : Number(id), + }); + } + + for (let i = 0; i < decisions.length; i++) { + const segment = decisions[i]; + const parts = escapeSplit(segment, '-'); + + if (parts[0] === 'global') { + parseGlobalDecision(parts.slice(1), i); + } else { + parseLineDecision(parts, i); + } + } + + return {lintCompilerOptionsPerFile, globalDecisions}; } function escapeFilename(filename: string): string { - return filename.replace(/-/, '\\-'); + return filename.replace(/-/, '\\-'); } export function buildLintDecisionGlobalString( - action: LintCompilerOptionsDecisionAction, - category: DiagnosticCategory, + action: LintCompilerOptionsDecisionAction, + category: DiagnosticCategory, ): string { - return `global-${action}-${category}`; + return `global-${action}-${category}`; } export function buildLintDecisionString( - { - filename, - action, - category, - start, - id, - }: { - filename: string; - action: LintCompilerOptionsDecisionAction; - category: DiagnosticCategory; - start: Position; - id?: number; - }, + { + filename, + action, + category, + start, + id, + }: { + filename: string; + action: LintCompilerOptionsDecisionAction; + category: DiagnosticCategory; + start: Position; + id?: number; + }, ): string { - const escapedFilename = escapeFilename(filename); - const pos = deriveDecisionPositionKey(action, {start}); + const escapedFilename = escapeFilename(filename); + const pos = deriveDecisionPositionKey(action, {start}); - const parts = [action, category, escapedFilename, pos]; + const parts = [action, category, escapedFilename, pos]; - if (id !== undefined) { - parts.push(String(id)); - } + if (id !== undefined) { + parts.push(String(id)); + } - return parts.join('-'); + return parts.join('-'); } export function buildLintDecisionAdviceAction( - { - noun, - instruction, - filename, - shortcut, - decision, - extra, - }: { - extra?: boolean; - shortcut?: string; - noun: string; - instruction: string; - filename?: string; - decision: string; - }, + { + noun, + instruction, + filename, + shortcut, + decision, + extra, + }: { + extra?: boolean; + shortcut?: string; + noun: string; + instruction: string; + filename?: string; + decision: string; + }, ): DiagnosticAdviceAction { - return { - type: 'action', - extra, - hidden: true, - command: 'lint', - shortcut, - args: filename === undefined ? [] : [escapeFilename(filename)], - noun, - instruction, - commandFlags: { - decisions: [decision], - }, - }; + return { + type: 'action', + extra, + hidden: true, + command: 'lint', + shortcut, + args: filename === undefined ? [] : [escapeFilename(filename)], + noun, + instruction, + commandFlags: { + decisions: [decision], + }, + }; } diff --git a/packages/@romejs/js-compiler/lint/index.test.ts b/packages/@romejs/js-compiler/lint/index.test.ts index 2cd6dcb7908..cd4441a1436 100644 --- a/packages/@romejs/js-compiler/lint/index.test.ts +++ b/packages/@romejs/js-compiler/lint/index.test.ts @@ -9,22 +9,22 @@ import {test} from 'rome'; import {testLint} from './rules/testHelpers'; test( - 'format disabled in project config should not regenerate the file', - async (t) => { - // Intentionally weird formatting - await testLint(t, 'foobar ( "yes" );', {category: undefined}); - }, + 'format disabled in project config should not regenerate the file', + async (t) => { + // Intentionally weird formatting + await testLint(t, 'foobar ( "yes" );', {category: undefined}); + }, ); test( - 'format enabled in project config should result in regenerated file', - async (t) => { - await testLint( - t, - 'foobar ( "yes" );', - { - category: undefined, - }, - ); - }, + 'format enabled in project config should result in regenerated file', + async (t) => { + await testLint( + t, + 'foobar ( "yes" );', + { + category: undefined, + }, + ); + }, ); diff --git a/packages/@romejs/js-compiler/lint/index.ts b/packages/@romejs/js-compiler/lint/index.ts index 8dfaa6a70c5..6f8f154634d 100644 --- a/packages/@romejs/js-compiler/lint/index.ts +++ b/packages/@romejs/js-compiler/lint/index.ts @@ -13,70 +13,70 @@ import {formatJS} from '@romejs/js-formatter'; import {addSuppressions} from './suppressions'; export type LintResult = { - diagnostics: Diagnostics; - suppressions: DiagnosticSuppressions; - src: string; + diagnostics: Diagnostics; + suppressions: DiagnosticSuppressions; + src: string; }; const lintCache: Cache = new Cache(); export default async function lint(req: LintRequest): Promise { - const {ast, sourceText, project, applyFixes, options} = req; + const {ast, sourceText, project, applyFixes, options} = req; - const query = Cache.buildQuery(req, {applyFixes}); - const cached = lintCache.get(query); - if (cached) { - return cached; - } + const query = Cache.buildQuery(req, {applyFixes}); + const cached = lintCache.get(query); + if (cached) { + return cached; + } - // Perform autofixes - const formatContext = new CompilerContext({ - ref: req.ref, - sourceText: req.sourceText, - options, - ast, - project, - frozen: false, - origin: { - category: 'lint', - }, - }); + // Perform autofixes + const formatContext = new CompilerContext({ + ref: req.ref, + sourceText: req.sourceText, + options, + ast, + project, + frozen: false, + origin: { + category: 'lint', + }, + }); - let formatAst = ast; - if (applyFixes) { - formatAst = formatContext.reduceRoot(ast, lintTransforms); - formatAst = addSuppressions(formatContext, formatAst); - } - const formattedCode = formatJS( - formatAst, - { - typeAnnotations: true, - sourceMaps: true, - format: 'pretty', - sourceText, - }, - ).code; + let formatAst = ast; + if (applyFixes) { + formatAst = formatContext.reduceRoot(ast, lintTransforms); + formatAst = addSuppressions(formatContext, formatAst); + } + const formattedCode = formatJS( + formatAst, + { + typeAnnotations: true, + sourceMaps: true, + format: 'pretty', + sourceText, + }, + ).code; - // Run lints (could be with the autofixed AST) - const context = new CompilerContext({ - ref: req.ref, - sourceText: req.sourceText, - ast, - project, - options, - origin: { - category: 'lint', - }, - frozen: true, - }); - context.reduceRoot(ast, lintTransforms); + // Run lints (could be with the autofixed AST) + const context = new CompilerContext({ + ref: req.ref, + sourceText: req.sourceText, + ast, + project, + options, + origin: { + category: 'lint', + }, + frozen: true, + }); + context.reduceRoot(ast, lintTransforms); - const diagnostics = context.diagnostics.getDiagnostics(); - const result: LintResult = { - suppressions: context.suppressions, - diagnostics: [...ast.diagnostics, ...diagnostics], - src: formattedCode, - }; - lintCache.set(query, result); - return result; + const diagnostics = context.diagnostics.getDiagnostics(); + const result: LintResult = { + suppressions: context.suppressions, + diagnostics: [...ast.diagnostics, ...diagnostics], + src: formattedCode, + }; + lintCache.set(query, result); + return result; } diff --git a/packages/@romejs/js-compiler/lint/rules/index.ts b/packages/@romejs/js-compiler/lint/rules/index.ts index a8449cddc31..06024ead156 100644 --- a/packages/@romejs/js-compiler/lint/rules/index.ts +++ b/packages/@romejs/js-compiler/lint/rules/index.ts @@ -70,66 +70,66 @@ import unusedVariables from './regular/unusedVariables'; import voidDomElementsNoChildren from './react/voidDomElementsNoChildren'; export const lintTransforms = [ - camelCase, - caseSingleStatement, - defaultExportSameBasename, - doubleEquals, - duplicateImportSource, - emptyBlocks, - emptyMatches, - getterReturn, - importDefaultBasename, - inconsiderateLanguage, - jsxA11YHTMLHasLang, - jsxA11YIframeHasTitle, - jsxA11YImgRedundantAlt, - jsxKey, - jsxNoCommentText, - negationElse, - noArguments, - noAsyncPromiseExecutor, - noCatchAssign, - noChildrenProp, - noCommaOperator, - noCompareNegZero, - noCondAssign, - noDanger, - noDangerWithChildren, - noDebugger, - noDelete, - noDeleteVars, - noDupeArgs, - noDuplicateCase, - noDuplicateGroupNamesInRegularExpressions, - noDuplicateKeys, - noEmptyCharacterClass, - noExplicitAny, - noExtraBooleanCast, - noFindDOMNode, - noFunctionAssign, - noImportAssign, - noLabelVar, - noMultipleSpacesInRegularExpressionLiterals, - noPosixInRegularExpression, - noReferenceToNonExistingGroup, - noSetterReturn, - noShadowRestrictedNames, - noShorthandArrayType, - noTemplateCurlyInString, - noUnsafeFinally, - noVar, - preferBlockStatements, - preferFunctionDeclarations, - preferTemplate, - preferWhile, - reactInJsxScope, - restrictedGlobals, - singleVarDeclarator, - sortImportExportSpecifiers, - sparseArray, - stylePropObject, - undeclaredVariables, - unsafeNegation, - unusedVariables, - voidDomElementsNoChildren, + camelCase, + caseSingleStatement, + defaultExportSameBasename, + doubleEquals, + duplicateImportSource, + emptyBlocks, + emptyMatches, + getterReturn, + importDefaultBasename, + inconsiderateLanguage, + jsxA11YHTMLHasLang, + jsxA11YIframeHasTitle, + jsxA11YImgRedundantAlt, + jsxKey, + jsxNoCommentText, + negationElse, + noArguments, + noAsyncPromiseExecutor, + noCatchAssign, + noChildrenProp, + noCommaOperator, + noCompareNegZero, + noCondAssign, + noDanger, + noDangerWithChildren, + noDebugger, + noDelete, + noDeleteVars, + noDupeArgs, + noDuplicateCase, + noDuplicateGroupNamesInRegularExpressions, + noDuplicateKeys, + noEmptyCharacterClass, + noExplicitAny, + noExtraBooleanCast, + noFindDOMNode, + noFunctionAssign, + noImportAssign, + noLabelVar, + noMultipleSpacesInRegularExpressionLiterals, + noPosixInRegularExpression, + noReferenceToNonExistingGroup, + noSetterReturn, + noShadowRestrictedNames, + noShorthandArrayType, + noTemplateCurlyInString, + noUnsafeFinally, + noVar, + preferBlockStatements, + preferFunctionDeclarations, + preferTemplate, + preferWhile, + reactInJsxScope, + restrictedGlobals, + singleVarDeclarator, + sortImportExportSpecifiers, + sparseArray, + stylePropObject, + undeclaredVariables, + unsafeNegation, + unusedVariables, + voidDomElementsNoChildren, ]; diff --git a/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.test.md b/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.test.md index ca38bade8de..c5eba7eb527 100644 --- a/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.test.md +++ b/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.test.md @@ -122,8 +122,8 @@ ✖ html elements must have a lang prop. - - ^^^^^^^^^^^^^^^^^^^^^^^ + + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -134,7 +134,7 @@ ### `4: formatted` ``` -; +; ``` @@ -146,8 +146,8 @@ ✖ html elements must have a lang prop. - - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + ^^^^^^^^^^^^^^^^^^^^^^^^^^ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -158,7 +158,7 @@ ### `5: formatted` ``` -; +; ``` @@ -170,8 +170,8 @@ ✖ html elements must have a lang prop. - - ^^^^^^^^^^^^^^^^^^^^^^^^^^ + + ^^^^^^^^^^^^^^^^^^^^^^^^^ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -182,7 +182,7 @@ ### `6: formatted` ``` -; +; ``` @@ -194,8 +194,8 @@ ✖ html elements must have a lang prop. - - ^^^^^^^^^^^^^^^^^^^^^^^^^ + + ^^^^^^^^^^^^^^^^^^^^^^^ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -206,31 +206,21 @@ ### `7: formatted` ``` -; +; ``` ### `8` ``` - - unknown:1 lint/jsxA11yHTMLHasLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ✖ html elements must have a lang prop. - - - ^^^^^^^^^^^^^^^^^^^^^^^ - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -✖ Found 1 problem +✔ No known problems! ``` ### `8: formatted` ``` -; +; ``` @@ -244,6 +234,6 @@ ### `9: formatted` ``` -; +; ``` diff --git a/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.test.ts b/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.test.ts index e533df12431..3a7f6ac88aa 100644 --- a/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.test.ts +++ b/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.test.ts @@ -10,25 +10,25 @@ import {test} from 'rome'; import {testLintMultiple} from '../testHelpers'; test( - 'require a lang attribute on JSX elements', - async (t) => { - await testLintMultiple( - t, - [ - // INVALID - '', - '', - '', - '', - '', - '', - '', - '', - // VALID - '', - '', - ], - {category: 'lint/jsxA11yHTMLHasLang'}, - ); - }, + 'require a lang attribute on JSX elements', + async (t) => { + await testLintMultiple( + t, + [ + // INVALID + '', + '', + '', + '', + '', + '', + '', + '', + // VALID + '', + '', + ], + {category: 'lint/jsxA11yHTMLHasLang'}, + ); + }, ); diff --git a/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.ts b/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.ts index d372cfffd74..0ca17849d74 100644 --- a/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.ts +++ b/packages/@romejs/js-compiler/lint/rules/react/jsxA11yHTMLHasLang.ts @@ -10,38 +10,38 @@ import {AnyNode} from '@romejs/js-ast'; import {Path} from '@romejs/js-compiler'; function jsxHTMLMissingLang(node: AnyNode) { - return ( - node.type === 'JSXElement' && - node.name.type === 'JSXIdentifier' && - node.name.name === 'html' && - !node.attributes.some((attribute) => - attribute.type === 'JSXAttribute' && - attribute.name.name === 'lang' && - attribute.value && - ((attribute.value.type === 'StringLiteral' && - attribute.value.value.length > 0) || - (attribute.value.type === 'JSXExpressionContainer' && - ((attribute.value.expression.type === 'ReferenceIdentifier' && - attribute.value.expression.name !== 'undefined') || - (attribute.value.expression.type === 'StringLiteral' && - attribute.value.expression.value.length > 0)))) - ) - ); + return ( + node.type === 'JSXElement' && + node.name.type === 'JSXIdentifier' && + node.name.name === 'html' && + !node.attributes.some((attribute) => + attribute.type === 'JSXAttribute' && + attribute.name.name === 'lang' && + attribute.value && + ((attribute.value.type === 'StringLiteral' && + attribute.value.value.length > 0) || + (attribute.value.type === 'JSXExpressionContainer' && + ((attribute.value.expression.type === 'ReferenceIdentifier' && + attribute.value.expression.name !== 'undefined') || + (attribute.value.expression.type === 'StringLiteral' && + attribute.value.expression.value.length > 0)))) + ) + ); } export default { - name: 'jsxA11yHTMLHasLang', + name: 'jsxA11yHTMLHasLang', - enter(path: Path): AnyNode { - const {node} = path; + enter(path: Path): AnyNode { + const {node} = path; - if (jsxHTMLMissingLang(node)) { - path.context.addNodeDiagnostic( - node, - descriptions.LINT.REACT_JSX_A11Y_HTML_HAS_LANG, - ); - } + if (jsxHTMLMissingLang(node)) { + path.context.addNodeDiagnostic( + node, + descriptions.LINT.REACT_JSX_A11Y_HTML_HAS_LANG, + ); + } - return node; - }, + return node; + }, }; diff --git a/packages/@romejs/js-compiler/lint/rules/react/jsxA11yIframeHasTitle.test.ts b/packages/@romejs/js-compiler/lint/rules/react/jsxA11yIframeHasTitle.test.ts index 53f8168628e..91e7ef4fc68 100644 --- a/packages/@romejs/js-compiler/lint/rules/react/jsxA11yIframeHasTitle.test.ts +++ b/packages/@romejs/js-compiler/lint/rules/react/jsxA11yIframeHasTitle.test.ts @@ -10,26 +10,26 @@ import {test} from 'rome'; import {testLintMultiple} from '../testHelpers'; test( - 'require a title attribute on