diff --git a/packages/cspell/package.json b/packages/cspell/package.json index 2f769549a288..47cd9a796246 100644 --- a/packages/cspell/package.json +++ b/packages/cspell/package.json @@ -43,7 +43,6 @@ "build": "tsc -p . && pnpm run build:api", "build:api": "rollup -c rollup.config.mjs", "build:esm": "tsc -p .", - "build:lib": "tsc -b src/lib/tsconfig.json -f", "build:readme": "pnpm build:readme:help", "build:readme:help": "pnpm build:readme:help:lint && pnpm build:readme:help:trace && inject-markdown README.md && prettier -w README.md", "build:readme:help:lint": "./bin.mjs lint --help > static/help-lint.txt", @@ -52,8 +51,8 @@ "coverage": "vitest run --coverage", "test:watch": "vitest", "test": "vitest run", - "watch": "tsc -b . -w -f", - "compile": "tsc -b . -f", + "watch": "tsc -p . -w", + "compile": "tsc -p .", "test-watch": "vitest", "version": "node ./tools/patch-version.mjs && git add .", "prepublishOnly": "pnpm run clean-build", diff --git a/packages/cspell/src/app/util/constants.ts b/packages/cspell/src/app/util/constants.ts index 21b38901f2d4..1139b4e669e4 100644 --- a/packages/cspell/src/app/util/constants.ts +++ b/packages/cspell/src/app/util/constants.ts @@ -1,4 +1,5 @@ export const UTF8 = 'utf8' as const; export const STDIN = 'stdin' as const; -export const STDINProtocol = 'stdin://' as const; -export const FileProtocol = 'file://' as const; +export const STDINProtocol = 'stdin:' as const; +export const STDINUrlPrefix = 'stdin://' as const; +export const FileUrlPrefix = 'file://' as const; diff --git a/packages/cspell/src/app/util/fileHelper.test.ts b/packages/cspell/src/app/util/fileHelper.test.ts index 04be51ec18ea..0655210b11a1 100644 --- a/packages/cspell/src/app/util/fileHelper.test.ts +++ b/packages/cspell/src/app/util/fileHelper.test.ts @@ -1,5 +1,5 @@ import * as path from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { fileURLToPath, pathToFileURL } from 'node:url'; import getStdin from 'get-stdin'; import { afterEach, describe, expect, test, vi } from 'vitest'; @@ -119,7 +119,7 @@ describe('fileHelper', () => { ${'not_found'} | ${__dirname} | ${path.join(__dirname, 'not_found')} ${'not_found'} | ${undefined} | ${path.resolve('not_found')} ${'stdin'} | ${undefined} | ${'stdin://'} - ${'stdin://source.ts'} | ${undefined} | ${'stdin://' + path.resolve('source.ts')} + ${'stdin://source.ts'} | ${undefined} | ${pathToFileURL('source.ts').href.replace(/^file:/, 'stdin:')} `('resolveFilename $filename $cwd', async ({ filename, cwd, expected }) => { expect(resolveFilename(filename, cwd)).toBe(expected); }); diff --git a/packages/cspell/src/app/util/fileHelper.ts b/packages/cspell/src/app/util/fileHelper.ts index ebf51e5d15d5..bfd1359a3afc 100644 --- a/packages/cspell/src/app/util/fileHelper.ts +++ b/packages/cspell/src/app/util/fileHelper.ts @@ -11,11 +11,12 @@ import { fileToDocument, isBinaryFile as isUriBinaryFile } from 'cspell-lib'; import getStdin from 'get-stdin'; import { asyncAwait, asyncFlatten, asyncMap, asyncPipe, mergeAsyncIterables } from './async.js'; -import { FileProtocol, STDIN, STDINProtocol, UTF8 } from './constants.js'; +import { FileUrlPrefix, STDIN, STDINProtocol, STDINUrlPrefix, UTF8 } from './constants.js'; import { IOError, toApplicationError, toError } from './errors.js'; import type { GlobOptions } from './glob.js'; import { globP } from './glob.js'; import { readStdin } from './stdin.js'; +import { isStdinUrl, resolveStdinUrl } from './stdinUrl.js'; import { clean } from './util.js'; export interface ConfigInfo { @@ -67,7 +68,7 @@ export function fileInfoToDocument( languageId = languageId || undefined; locale = locale || undefined; - const uri = filenameToUrlString(filename); + const uri = filenameToUrl(filename); if (uri.href.startsWith(STDINProtocol)) { return clean({ @@ -81,18 +82,17 @@ export function fileInfoToDocument( return fileToDocument(uri.href, text, languageId, locale); } -export function filenameToUrlString(filename: string, cwd = '.'): URL { +export function filenameToUrl(filename: string, cwd = '.'): URL { const cwdURL = toFileDirURL(cwd); if (filename === STDIN) return new URL('stdin:///'); - if (filename.startsWith(STDINProtocol)) { - const filePath = filename.slice(STDINProtocol.length); - return toFileURL(filePath, cwdURL); + if (isStdinUrl(filename)) { + return new URL(resolveStdinUrl(filename, cwd)); } return toFileURL(filename, cwdURL); } export function filenameToUri(filename: string, cwd?: string): URL { - return toURL(filenameToUrlString(filename, cwd)); + return toURL(filenameToUrl(filename, cwd)); } export function isBinaryFile(filename: string, cwd?: string): boolean { @@ -107,15 +107,15 @@ export interface ReadFileInfoResult extends FileInfo { export function resolveFilename(filename: string, cwd?: string): string { cwd = cwd || process.cwd(); - if (filename === STDIN) return STDINProtocol; - if (filename.startsWith(FileProtocol)) { - const url = new URL(filename.slice(FileProtocol.length), pathToFileURL(cwd + path.sep)); + if (filename === STDIN) return STDINUrlPrefix; + if (filename.startsWith(FileUrlPrefix)) { + const url = new URL(filename.slice(FileUrlPrefix.length), pathToFileURL(cwd + path.sep)); return fileURLToPath(url); } - const scheme = filename.startsWith(STDINProtocol) ? STDINProtocol : ''; - const pathname = filename.slice(scheme.length); - - return scheme + path.resolve(cwd, pathname); + if (isStdinUrl(filename)) { + return resolveStdinUrl(filename, cwd); + } + return path.resolve(cwd, filename); } export function readFileInfo( @@ -149,9 +149,7 @@ export function readFile(filename: string, encoding: BufferEncoding = UTF8): Pro export async function findFiles(globPatterns: string[], options: GlobOptions): Promise { const stdin: string[] = []; const globPats = globPatterns.filter((filename) => - filename !== STDIN && !filename.startsWith(STDINProtocol) && !filename.startsWith(FileProtocol) - ? true - : (stdin.push(filename), false), + !isStdin(filename) && !filename.startsWith(FileUrlPrefix) ? true : (stdin.push(filename), false), ); const globResults = globPats.length ? await globP(globPats, options) : []; const cwd = options.cwd || process.cwd(); @@ -205,7 +203,12 @@ export async function readFileListFile(listFile: string): Promise { } } +function isStdin(filename: string): boolean { + return filename === STDIN || isStdinUrl(filename); +} + export async function isFile(filename: string): Promise { + if (isStdin(filename)) return true; try { const stat = await fsp.stat(filename); return stat.isFile(); diff --git a/packages/cspell/src/app/util/stdinUrl.test.ts b/packages/cspell/src/app/util/stdinUrl.test.ts new file mode 100644 index 000000000000..c99b80dbecaa --- /dev/null +++ b/packages/cspell/src/app/util/stdinUrl.test.ts @@ -0,0 +1,31 @@ +import Path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { describe, expect, test } from 'vitest'; + +import { isStdinUrl, resolveStdinUrl } from './stdinUrl.js'; + +const filenameURL = new URL(import.meta.url); +const __filename = fileURLToPath(import.meta.url); +const __dirname = Path.dirname(__filename); +const dirUrl = new URL('./', import.meta.url); +const stdinUrl = dirUrl.href.replace(/^file:/, 'stdin:'); + +describe('stdinUrl', () => { + test('isStdinUrl', () => { + expect(isStdinUrl('stdin://')).toBe(true); + expect(isStdinUrl('stdin:')).toBe(true); + expect(isStdinUrl('stdin://foo')).toBe(true); + }); + + test.each` + url | expected + ${'stdin://'} | ${stdinUrl} + ${'stdin:package.json'} | ${new URL('package.json', stdinUrl).href} + ${'stdin:./package.json'} | ${new URL('package.json', stdinUrl).href} + ${'stdin:' + __filename} | ${new URL(filenameURL.pathname, stdinUrl).href} + ${'stdin://' + __filename} | ${new URL(filenameURL.pathname, stdinUrl).href} + `('resolveStdinUrl', ({ url, expected }) => { + expect(resolveStdinUrl(url, __dirname)).toBe(expected); + }); +}); diff --git a/packages/cspell/src/app/util/stdinUrl.ts b/packages/cspell/src/app/util/stdinUrl.ts new file mode 100644 index 000000000000..374270954e50 --- /dev/null +++ b/packages/cspell/src/app/util/stdinUrl.ts @@ -0,0 +1,28 @@ +import assert from 'node:assert'; +import Path from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import { STDINProtocol } from './constants.js'; + +export function isStdinUrl(url: string | URL): boolean { + if (url instanceof URL) { + return url.protocol === STDINProtocol; + } + return url.startsWith(STDINProtocol); +} + +/** + * Normalize and resolve a stdin url. + * @param url - stdin url to resolve. + * @param cwd - file path to resolve relative paths against. + * @returns + */ +export function resolveStdinUrl(url: string, cwd: string): string { + assert(url.startsWith(STDINProtocol), `Expected url to start with ${STDINProtocol}`); + const path = url + .slice(STDINProtocol.length) + .replace(/^\/\//, '') + .replace(/^\/([a-z]:)/i, '$1'); + const fileUrl = pathToFileURL(Path.resolve(cwd, path)); + return fileUrl.toString().replace(/^file:/, STDINProtocol) + (path ? '' : '/'); +}