Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: stdin urls on the command line #6345

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions packages/cspell/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
5 changes: 3 additions & 2 deletions packages/cspell/src/app/util/constants.ts
Original file line number Diff line number Diff line change
@@ -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;
4 changes: 2 additions & 2 deletions packages/cspell/src/app/util/fileHelper.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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);
});
Expand Down
37 changes: 20 additions & 17 deletions packages/cspell/src/app/util/fileHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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({
Expand All @@ -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 {
Expand All @@ -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(
Expand Down Expand Up @@ -149,9 +149,7 @@ export function readFile(filename: string, encoding: BufferEncoding = UTF8): Pro
export async function findFiles(globPatterns: string[], options: GlobOptions): Promise<string[]> {
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();
Expand Down Expand Up @@ -205,7 +203,12 @@ export async function readFileListFile(listFile: string): Promise<string[]> {
}
}

function isStdin(filename: string): boolean {
return filename === STDIN || isStdinUrl(filename);
}

export async function isFile(filename: string): Promise<boolean> {
if (isStdin(filename)) return true;
try {
const stat = await fsp.stat(filename);
return stat.isFile();
Expand Down
31 changes: 31 additions & 0 deletions packages/cspell/src/app/util/stdinUrl.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
28 changes: 28 additions & 0 deletions packages/cspell/src/app/util/stdinUrl.ts
Original file line number Diff line number Diff line change
@@ -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 ? '' : '/');
}