Typecheck JS, webpack caching, zip/egg support (#1937)

This commit is contained in:
Jake Bailey 2021-06-02 12:18:10 -07:00 committed by GitHub
parent 45390e6fbe
commit e52d1e96e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 459 additions and 134 deletions

View File

@ -63,5 +63,13 @@
}
],
"@typescript-eslint/no-use-before-define": 0
}
},
"overrides": [
{
"files": ["**/*.js"],
"rules": {
"@typescript-eslint/no-var-requires": 0
}
}
]
}

2
.gitignore vendored
View File

@ -117,3 +117,5 @@ dist
*.vsix
junit.xml
.webpack_cache/

View File

@ -6,12 +6,12 @@
// the change will cause problems with merging and the updateDeps script.
const detectIndent = require('detect-indent');
const fsExtra = require('fs-extra');
const { promises: fsAsync } = require('fs');
const util = require('util');
const glob = util.promisify(require('glob'));
async function findPackageLocks() {
const lernaFile = await fsExtra.readFile('lerna.json', 'utf-8');
const lernaFile = await fsAsync.readFile('lerna.json', 'utf-8');
/** @type {{ packages: string[] }} */
const lernaConfig = JSON.parse(lernaFile);
@ -26,7 +26,7 @@ async function main() {
let ok = true;
for (const filepath of locks) {
const input = await fsExtra.readFile(filepath, 'utf-8');
const input = await fsAsync.readFile(filepath, 'utf-8');
const indent = detectIndent(input);
if (indent.indent !== ' ') {

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-var-requires */
//@ts-check
const fsExtra = require('fs-extra');
const { promises: fsAsync } = require('fs');
const ncu = require('npm-check-updates');
const PQueue = require('p-queue').default;
const path = require('path');
@ -9,8 +9,20 @@ const util = require('util');
const glob = util.promisify(require('glob'));
const exec = util.promisify(require('child_process').exec);
/** @type {(path: string, options?: import('fs').RmDirOptions & { force?: boolean }) => Promise<void> | undefined} */
const node14rm = /** @type {any} */ (fsAsync).rm;
/** @type {(path: string) => Promise<void>} */
async function rmdir(path) {
if (node14rm) {
// Avoid deprecation warning when on Node v14+, which have deprecated recursive rmdir in favor of rm.
return node14rm(path, { recursive: true, force: true });
}
return fsAsync.rmdir(path, { recursive: true });
}
async function findPackages() {
const lernaFile = await fsExtra.readFile('lerna.json', 'utf-8');
const lernaFile = await fsAsync.readFile('lerna.json', 'utf-8');
/** @type {{ packages: string[] }} */
const lernaConfig = JSON.parse(lernaFile);
@ -42,8 +54,8 @@ async function updatePackage(packageFile, transitive, reject = undefined) {
if (transitive) {
console.log(`${packageName}: removing package-lock.json and node_modules`);
await fsExtra.remove(path.join(packagePath, 'package-lock.json'));
await fsExtra.remove(path.join(packagePath, 'node_modules'));
await fsAsync.unlink(path.join(packagePath, 'package-lock.json'));
await rmdir(path.join(packagePath, 'node_modules'));
}
await queue.add(async () => {

View File

@ -1,11 +1,13 @@
//@ts-check
const fs = require('fs');
const path = require('path');
const glob = require('glob');
/**
* Builds a faked resource path for production source maps in webpack.
*
* @param package {string} The name of the package where webpack is running.
* @param {string} packageName The name of the package where webpack is running.
*/
function monorepoResourceNameMapper(package) {
function monorepoResourceNameMapper(packageName) {
/**@type {(info: {resourcePath: string}) => string} */
const mapper = (info) => {
const parts = [];
@ -17,7 +19,7 @@ function monorepoResourceNameMapper(package) {
}
if (part === '.') {
parts.push(package);
parts.push(packageName);
break;
}
@ -29,6 +31,44 @@ function monorepoResourceNameMapper(package) {
return mapper;
}
/**
* Returns the list of node_modules folders for the entire monorepo.
*
* @param {string} workspaceRoot
*/
function managedPaths(workspaceRoot) {
const contents = fs.readFileSync(path.join(workspaceRoot, 'lerna.json'), 'utf-8');
/** @type {{ packages: string[] }} */
const data = JSON.parse(contents);
const patterns = data.packages;
const paths = [path.resolve(workspaceRoot, 'node_modules')];
paths.push(
...patterns
.flatMap((p) => glob.sync(p, { cwd: workspaceRoot }))
.map((p) => path.resolve(workspaceRoot, p, 'node_modules'))
);
return paths;
}
/**
* Builds a webpack caching config, given the calling module's __dirname and __filename.
* @param {string} dirname __dirname
* @param {string} filename __filename
*/
function cacheConfig(dirname, filename) {
return {
type: /** @type {'filesystem'} */ ('filesystem'),
cacheDirectory: path.resolve(dirname, '.webpack_cache'),
buildDependencies: {
config: [filename],
},
managedPaths: managedPaths(path.resolve(dirname, '..', '..')),
};
}
module.exports = {
monorepoResourceNameMapper,
cacheConfig,
};

9
package-lock.json generated
View File

@ -2501,15 +2501,6 @@
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
"dev": true
},
"@types/fs-extra": {
"version": "9.0.11",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz",
"integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",

View File

@ -20,7 +20,6 @@
"check:lockindent": "node ./build/checkLockIndent.js"
},
"devDependencies": {
"@types/fs-extra": "^9.0.11",
"@types/glob": "^7.1.3",
"@types/node": "^12.20.13",
"@types/yargs": "^16.0.1",
@ -30,7 +29,6 @@
"eslint": "^7.26.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"fs-extra": "^9.1.0",
"glob": "^7.1.7",
"lerna": "^3.22.1",
"npm-check-updates": "^11.5.12",

View File

@ -5,6 +5,7 @@
*/
module.exports = {
testEnvironment: 'node',
roots: ['<rootDir>/src/tests'],
transform: {
'^.+\\.tsx?$': 'ts-jest',

View File

@ -714,6 +714,11 @@
"integrity": "sha512-4eOPXyn5DmP64MCMF8ePDvdlvlzt2a+F8ZaVjqmh2yFCpGjc1kI3kGnCFYX9SCsGTjQcWIyVZ86IHCEyjy/MNg==",
"dev": true
},
"@types/emscripten": {
"version": "1.39.4",
"resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.4.tgz",
"integrity": "sha512-k3LLVMFrdNA9UCvMDPWMbFrGPNb+GcPyw29ktJTo1RCN7RmxFG5XzPZcPKRlnLuLT/FRm8wp4ohvDwNY7GlROQ=="
},
"@types/graceful-fs": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
@ -802,6 +807,24 @@
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==",
"dev": true
},
"@yarnpkg/fslib": {
"version": "2.5.0-rc.2",
"resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.5.0-rc.2.tgz",
"integrity": "sha512-JrHPktr57m7+Q4bpy/1x+t1h8xAUgOVxo0h3I3Splbl1G9liL6lvBkLhM1hb01qqkyTpHGMDIWQ1lxdYCf8W4Q==",
"requires": {
"@yarnpkg/libzip": "^2.2.1",
"tslib": "^1.13.0"
}
},
"@yarnpkg/libzip": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@yarnpkg/libzip/-/libzip-2.2.1.tgz",
"integrity": "sha512-AYDJXrkzayoDd3ZlVgFJ+LyDX+Zj/cki3vxIpcYxejtgkl3aquVWOxlC0DD9WboBWsJFIP1MjrUbchLyh++/7A==",
"requires": {
"@types/emscripten": "^1.38.0",
"tslib": "^1.13.0"
}
},
"abab": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
@ -4926,6 +4949,11 @@
}
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",

View File

@ -16,6 +16,8 @@
},
"dependencies": {
"@iarna/toml": "2.2.5",
"@yarnpkg/fslib": "2.5.0-rc.2",
"@yarnpkg/libzip": "^2.2.1",
"assert": "^2.0.0",
"chalk": "^4.1.1",
"chokidar": "^3.5.1",

View File

@ -227,13 +227,7 @@ function getPathResultFromInterpreter(
execSplitEntry = execSplitEntry.trim();
if (execSplitEntry) {
const normalizedPath = normalizePath(execSplitEntry);
// Make sure the path exists and is a directory. We don't currently
// support zip files and other formats.
if (fs.existsSync(normalizedPath) && isDirectory(fs, normalizedPath)) {
result.paths.push(normalizedPath);
} else {
importFailureInfo.push(`Skipping '${normalizedPath}' because it is not a valid directory`);
}
result.paths.push(normalizedPath);
}
}

View File

@ -8,21 +8,18 @@
* for testing.
*/
/* eslint-disable no-dupe-class-members */
// * NOTE * except tests, this should be only file that import "fs"
import { FakeFS, PortablePath, PosixFS, ppath, VirtualFS, ZipOpenFS } from '@yarnpkg/fslib';
import { getLibzipSync } from '@yarnpkg/libzip';
import * as chokidar from 'chokidar';
import * as fs from 'fs';
import type * as fs from 'fs';
import * as tmp from 'tmp';
import { promisify } from 'util';
import { ConsoleInterface, NullConsole } from './console';
// Automatically remove files created by tmp at process exit.
tmp.setGracefulCleanup();
const readFile = promisify(fs.readFile);
export type FileWatcherEventType = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir';
export type FileWatcherEventHandler = (eventName: FileWatcherEventType, path: string, stats?: Stats) => void;
@ -55,7 +52,7 @@ export interface TmpfileOptions {
export interface FileSystem {
existsSync(path: string): boolean;
mkdirSync(path: string, options?: MkDirOptions | number): void;
mkdirSync(path: string, options?: MkDirOptions): void;
chdir(path: string): void;
readdirEntriesSync(path: string): fs.Dirent[];
readdirSync(path: string): string[];
@ -110,6 +107,100 @@ export function ignoredWatchEventFunction(paths: string[]) {
const _isMacintosh = process.platform === 'darwin';
const _isLinux = process.platform === 'linux';
const DOT_ZIP = `.zip`;
const DOT_EGG = `.egg`;
// Exactly the same as ZipOpenFS's getArchivePart, but supporting .egg files.
// https://github.com/yarnpkg/berry/blob/64a16b3603ef2ccb741d3c44f109c9cfc14ba8dd/packages/yarnpkg-fslib/sources/ZipOpenFS.ts#L23
function getArchivePart(path: string) {
let idx = path.indexOf(DOT_ZIP);
if (idx <= 0) {
idx = path.indexOf(DOT_EGG);
if (idx <= 0) {
return null;
}
}
// Disallow files named ".zip"
if (path[idx - 1] === ppath.sep) return null;
const nextCharIdx = idx + DOT_ZIP.length; // DOT_ZIP and DOT_EGG are the same length.
// The path either has to end in ".zip" or contain an archive subpath (".zip/...")
if (path.length > nextCharIdx && path[nextCharIdx] !== ppath.sep) return null;
return path.slice(0, nextCharIdx) as PortablePath;
}
// Returns true if the specified path may be inside of a zip or egg file.
// These files don't really exist, and will fail if navigated to in the editor.
export function isInZipOrEgg(path: string) {
return /[^\\/]\.(?:egg|zip)[\\/]/.test(path);
}
// Patch fslib's ZipOpenFS to also consider .egg files to be .zip files.
//
// For now, override findZip (even though it's private), with the intent
// to upstream a change to allow overriding getArchivePart or add some
// other mechanism to support more extensions as zips (or, to remove this
// hack in favor of a full ZipOpenFS fork).
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-expect-error
class EggZipOpenFS extends ZipOpenFS {
// Copied from the ZipOpenFS implementation.
private readonly baseFs!: FakeFS<PortablePath>;
private readonly filter!: RegExp | null;
private isZip!: Set<PortablePath>;
private notZip!: Set<PortablePath>;
// Exactly the same as ZipOpenFS, but uses our getArchivePart.
findZip(p: PortablePath) {
if (this.filter && !this.filter.test(p)) return null;
let filePath = `` as PortablePath;
while (true) {
const archivePart = getArchivePart(p.substr(filePath.length));
if (!archivePart) return null;
filePath = this.pathUtils.join(filePath, archivePart);
if (this.isZip.has(filePath) === false) {
if (this.notZip.has(filePath)) continue;
try {
if (!this.baseFs.lstatSync(filePath).isFile()) {
this.notZip.add(filePath);
continue;
}
} catch {
return null;
}
this.isZip.add(filePath);
}
return {
archivePath: filePath,
subPath: this.pathUtils.join(PortablePath.root, p.substr(filePath.length) as PortablePath),
};
}
}
}
const yarnFS = new PosixFS(
new VirtualFS({
baseFs: new EggZipOpenFS({
// Note: libzip is a WASM module and can take a few milliseconds to load.
// The next version of fslib should allow this to be initialized lazily.
libzip: getLibzipSync(),
useCache: true,
maxOpenFiles: 80,
readOnlyArchives: true,
}),
})
);
class RealFileSystem implements FileSystem {
private _fileWatcherProvider: FileWatcherProvider;
private _tmpdir?: string;
@ -119,11 +210,11 @@ class RealFileSystem implements FileSystem {
}
existsSync(path: string) {
return fs.existsSync(path);
return yarnFS.existsSync(path);
}
mkdirSync(path: string, options?: MkDirOptions | number) {
fs.mkdirSync(path, options);
mkdirSync(path: string, options?: MkDirOptions) {
yarnFS.mkdirSync(path, options);
}
chdir(path: string) {
@ -131,33 +222,67 @@ class RealFileSystem implements FileSystem {
}
readdirSync(path: string): string[] {
return fs.readdirSync(path);
return yarnFS.readdirSync(path);
}
readdirEntriesSync(path: string): fs.Dirent[] {
return fs.readdirSync(path, { withFileTypes: true });
return yarnFS.readdirSync(path, { withFileTypes: true }).map((entry): fs.Dirent => {
// Treat zip/egg files as directories.
// See: https://github.com/yarnpkg/berry/blob/master/packages/vscode-zipfs/sources/ZipFSProvider.ts
if (entry.name.endsWith(DOT_ZIP) || entry.name.endsWith(DOT_EGG)) {
if (entry.isFile()) {
return {
name: entry.name,
isFile: () => false,
isDirectory: () => true,
isBlockDevice: () => false,
isCharacterDevice: () => false,
isSymbolicLink: () => false,
isFIFO: () => false,
isSocket: () => false,
};
}
}
return entry;
});
}
readFileSync(path: string, encoding?: null): Buffer;
readFileSync(path: string, encoding: BufferEncoding): string;
readFileSync(path: string, encoding?: BufferEncoding | null): Buffer | string;
readFileSync(path: string, encoding: BufferEncoding | null = null) {
return fs.readFileSync(path, { encoding });
if (encoding === 'utf8' || encoding === 'utf-8') {
return yarnFS.readFileSync(path, 'utf8');
}
return yarnFS.readFileSync(path);
}
writeFileSync(path: string, data: string | Buffer, encoding: BufferEncoding | null) {
fs.writeFileSync(path, data, { encoding });
yarnFS.writeFileSync(path, data, { encoding: encoding ?? undefined });
}
statSync(path: string) {
return fs.statSync(path);
const stat = yarnFS.statSync(path);
// Treat zip/egg files as directories.
// See: https://github.com/yarnpkg/berry/blob/master/packages/vscode-zipfs/sources/ZipFSProvider.ts
if (path.endsWith(DOT_ZIP) || path.endsWith(DOT_EGG)) {
if (stat.isFile()) {
return {
...stat,
isFile: () => false,
isDirectory: () => true,
};
}
}
return stat;
}
unlinkSync(path: string) {
fs.unlinkSync(path);
yarnFS.unlinkSync(path);
}
realpathSync(path: string) {
return fs.realpathSync(path);
return yarnFS.realpathSync(path);
}
getModulePath(): string {
@ -172,23 +297,27 @@ class RealFileSystem implements FileSystem {
}
createReadStream(path: string): fs.ReadStream {
return fs.createReadStream(path);
return yarnFS.createReadStream(path);
}
createWriteStream(path: string): fs.WriteStream {
return fs.createWriteStream(path);
return yarnFS.createWriteStream(path);
}
copyFileSync(src: string, dst: string): void {
fs.copyFileSync(src, dst);
yarnFS.copyFileSync(src, dst);
}
readFile(path: string): Promise<Buffer> {
return readFile(path);
return yarnFS.readFilePromise(path);
}
readFileText(path: string, encoding: BufferEncoding): Promise<string> {
return readFile(path, { encoding });
async readFileText(path: string, encoding: BufferEncoding): Promise<string> {
if (encoding === 'utf8' || encoding === 'utf-8') {
return yarnFS.readFilePromise(path, 'utf8');
}
const buffer = await yarnFS.readFilePromise(path);
return buffer.toString(encoding);
}
tmpdir() {

View File

@ -65,6 +65,7 @@ import {
} from './common/commandLineOptions';
import { ConfigOptions, getDiagLevelDiagnosticRules } from './common/configOptions';
import { ConsoleInterface, ConsoleWithLogLevel, LogLevel } from './common/console';
import { isDefined } from './common/core';
import { createDeferred, Deferred } from './common/deferred';
import { Diagnostic as AnalyzerDiagnostic, DiagnosticCategory } from './common/diagnostic';
import { DiagnosticRule } from './common/diagnosticRules';
@ -75,6 +76,7 @@ import {
FileWatcher,
FileWatcherEventHandler,
FileWatcherEventType,
isInZipOrEgg,
} from './common/fileSystem';
import { containsPath, convertPathToUri, convertUriToPath } from './common/pathUtils';
import { ProgressReporter, ProgressReportTracker } from './common/progressReporter';
@ -407,21 +409,24 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
});
// For any non-workspace paths, use the node file watcher.
let nodeWatchers: FileWatcher[];
const nodeWatchers = nonWorkspacePaths
.map((path) => {
// Skip paths that don't exist; fs.watch will throw when it tries to watch them,
// and won't give us a watcher that would work if it were created later.
if (!fs.existsSync(path)) {
return undefined;
}
try {
nodeWatchers = nonWorkspacePaths.map((path) => {
return fs.watch(path, { recursive: true }, (event, filename) =>
listener(event as FileWatcherEventType, filename)
);
});
} catch (e) {
// Versions of node >= 14 are reportedly throwing exceptions
// when calling fs.watch with recursive: true. Just swallow
// the exception and proceed.
this.console.error(`Exception received when installing recursive file system watcher`);
nodeWatchers = [];
}
try {
return fs.watch(path, { recursive: true }, (event, filename) =>
listener(event as FileWatcherEventType, filename)
);
} catch (e) {
this.console.error(`Exception received when installing recursive file system watcher: ${e}`);
return undefined;
}
})
.filter(isDefined);
const fileWatcher: InternalFileWatcher = {
close() {
@ -481,7 +486,9 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
if (!locations) {
return undefined;
}
return locations.map((loc) => Location.create(convertPathToUri(this.fs, loc.path), loc.range));
return locations
.filter((loc) => !isInZipOrEgg(loc.path))
.map((loc) => Location.create(convertPathToUri(this.fs, loc.path), loc.range));
};
this._connection.onDefinition((params, token) =>
@ -531,7 +538,9 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
}
const convert = (locs: DocumentRange[]): Location[] => {
return locs.map((loc) => Location.create(convertPathToUri(this.fs, loc.path), loc.range));
return locs
.filter((loc) => !isInZipOrEgg(loc.path))
.map((loc) => Location.create(convertPathToUri(this.fs, loc.path), loc.range));
};
const locations: Location[] = [];
@ -784,6 +793,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
return null;
}
if (isInZipOrEgg(callItem.uri)) {
return null;
}
// Convert the file path in the item to proper URI.
callItem.uri = convertPathToUri(this.fs, callItem.uri);
@ -803,11 +816,13 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
return null;
}
const callItems = workspace.serviceInstance.getIncomingCallsForPosition(filePath, position, token) || null;
let callItems = workspace.serviceInstance.getIncomingCallsForPosition(filePath, position, token) || null;
if (!callItems || callItems.length === 0) {
return null;
}
callItems = callItems.filter((item) => !isInZipOrEgg(item.from.uri));
// Convert the file paths in the items to proper URIs.
callItems.forEach((item) => {
item.from.uri = convertPathToUri(this.fs, item.from.uri);
@ -829,11 +844,13 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
return null;
}
const callItems = workspace.serviceInstance.getOutgoingCallsForPosition(filePath, position, token) || null;
let callItems = workspace.serviceInstance.getOutgoingCallsForPosition(filePath, position, token) || null;
if (!callItems || callItems.length === 0) {
return null;
}
callItems = callItems.filter((item) => !isInZipOrEgg(item.to.uri));
// Convert the file paths in the items to proper URIs.
callItems.forEach((item) => {
item.to.uri = convertPathToUri(this.fs, item.to.uri);
@ -1092,6 +1109,10 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
protected onAnalysisCompletedHandler(results: AnalysisResults): void {
// Send the computed diagnostics to the client.
results.diagnostics.forEach((fileDiag) => {
if (isInZipOrEgg(fileDiag.filePath)) {
return;
}
this._connection.sendDiagnostics({
uri: convertPathToUri(this.fs, fileDiag.filePath),
diagnostics: this._convertDiagnostics(fileDiag.diagnostics),
@ -1278,12 +1299,14 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
const relatedInfo = diag.getRelatedInfo();
if (relatedInfo.length > 0) {
vsDiag.relatedInformation = relatedInfo.map((info) => {
return DiagnosticRelatedInformation.create(
Location.create(convertPathToUri(this.fs, info.filePath), info.range),
info.message
vsDiag.relatedInformation = relatedInfo
.filter((info) => !isInZipOrEgg(info.filePath))
.map((info) =>
DiagnosticRelatedInformation.create(
Location.create(convertPathToUri(this.fs, info.filePath), info.range),
info.message
)
);
});
}
convertedDiags.push(vsDiag);

View File

@ -63,7 +63,7 @@ export class PyrightFileSystem implements FileSystem {
return this._realFS.existsSync(this.getOriginalFilePath(path));
}
mkdirSync(path: string, options?: MkDirOptions | number): void {
mkdirSync(path: string, options?: MkDirOptions): void {
this._realFS.mkdirSync(path, options);
}

View File

@ -0,0 +1,90 @@
/*
* zipfs.test.ts
*
* zip/egg file related FS tests.
*/
import * as assert from 'assert';
import * as path from 'path';
import { createFromRealFileSystem } from '../common/fileSystem';
import { combinePaths } from '../common/pathUtils';
import { compareStringsCaseSensitive } from '../common/stringUtils';
function runTests(p: string): void {
const zipRoot = path.resolve(path.dirname(module.filename), p);
const fs = createFromRealFileSystem();
test('stat root', () => {
const stats = fs.statSync(zipRoot);
assert.strictEqual(stats.isDirectory(), true);
assert.strictEqual(stats.isFile(), false);
});
test('readdirEntriesSync root', () => {
const entries = fs.readdirEntriesSync(zipRoot);
assert.strictEqual(entries.length, 2);
entries.sort((a, b) => compareStringsCaseSensitive(a.name, b.name));
assert.strictEqual(entries[0].name, 'EGG-INFO');
assert.strictEqual(entries[0].isDirectory(), true);
assert.strictEqual(entries[0].isFile(), false);
assert.strictEqual(entries[1].name, 'test');
assert.strictEqual(entries[1].isDirectory(), true);
assert.strictEqual(entries[1].isFile(), false);
});
test('stat EGG-INFO', () => {
const stats = fs.statSync(combinePaths(zipRoot, 'EGG-INFO'));
assert.strictEqual(stats.isDirectory(), true);
assert.strictEqual(stats.isFile(), false);
});
test('readdirEntriesSync root', () => {
const entries = fs.readdirEntriesSync(combinePaths(zipRoot, 'EGG-INFO'));
assert.strictEqual(entries.length, 5);
entries.sort((a, b) => compareStringsCaseSensitive(a.name, b.name));
assert.strictEqual(entries[0].name, 'PKG-INFO');
assert.strictEqual(entries[0].isDirectory(), false);
assert.strictEqual(entries[0].isFile(), true);
assert.strictEqual(entries[1].name, 'SOURCES.txt');
assert.strictEqual(entries[1].isDirectory(), false);
assert.strictEqual(entries[1].isFile(), true);
assert.strictEqual(entries[2].name, 'dependency_links.txt');
assert.strictEqual(entries[2].isDirectory(), false);
assert.strictEqual(entries[2].isFile(), true);
assert.strictEqual(entries[3].name, 'top_level.txt');
assert.strictEqual(entries[3].isDirectory(), false);
assert.strictEqual(entries[3].isFile(), true);
assert.strictEqual(entries[4].name, 'zip-safe');
assert.strictEqual(entries[4].isDirectory(), false);
assert.strictEqual(entries[4].isFile(), true);
});
test('read file', () => {
const contents = fs.readFileSync(combinePaths(zipRoot, 'EGG-INFO', 'top_level.txt'), 'utf-8');
assert.strictEqual(contents.trim(), 'test');
});
test('read file async', async () => {
const contents = await fs.readFileText(combinePaths(zipRoot, 'EGG-INFO', 'top_level.txt'), 'utf-8');
assert.strictEqual(contents.trim(), 'test');
});
test('unlink fails', async () => {
expect(() => {
fs.unlinkSync(combinePaths(zipRoot, 'EGG-INFO', 'top_level.txt'));
}).toThrow(/read-only filesystem/);
});
}
describe('zip', () => runTests('./samples/zipfs/basic.zip'));
describe('egg', () => runTests('./samples/zipfs/basic.egg'));

View File

@ -5,6 +5,12 @@
},
"include": [
"src/**/*",
"src/localization/*.json"
"src/localization/*.json",
"**/*.js"
],
"exclude": [
"node_modules",
"dist",
"out"
]
}
}

View File

@ -1,4 +1,6 @@
#!/usr/bin/env node
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
// Stash the base directory into a global variable.
global.__rootDirectory = __dirname + '/dist/';

View File

@ -1,4 +1,6 @@
#!/usr/bin/env node
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
// Stash the base directory into a global variable.
global.__rootDirectory = __dirname + '/dist/';

View File

@ -36,6 +36,17 @@
"fastq": "^1.6.0"
}
},
"@types/copy-webpack-plugin": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@types/copy-webpack-plugin/-/copy-webpack-plugin-8.0.0.tgz",
"integrity": "sha512-EJ9Nd0a628uwvgCEt7bN4F6f2jA0O+i+ajAyq9F4jRTqJJ0ro13C22GL/bnvnsSoKuN/O93yfXqZWhn2R70b/g==",
"dev": true,
"requires": {
"@types/node": "*",
"tapable": "^2.0.0",
"webpack": "^5.1.0"
}
},
"@types/eslint": {
"version": "7.2.10",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.10.tgz",

View File

@ -23,6 +23,7 @@
"webpack": "webpack --mode development --progress"
},
"devDependencies": {
"@types/copy-webpack-plugin": "^8.0.0",
"@types/node": "^12.20.13",
"copy-webpack-plugin": "^8.1.1",
"shx": "^0.3.3",

View File

@ -9,6 +9,12 @@
}
},
"include": [
"src/**/*"
"src/**/*",
"**/*.js"
],
"exclude": [
"node_modules",
"dist",
"out"
]
}
}

View File

@ -3,13 +3,10 @@
* Copyright: Microsoft 2018
*/
/* eslint-disable @typescript-eslint/no-var-requires */
//@ts-check
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');
const { monorepoResourceNameMapper } = require('../../build/lib/webpack');
const { cacheConfig, monorepoResourceNameMapper } = require('../../build/lib/webpack');
const outPath = path.resolve(__dirname, 'dist');
const typeshedFallback = path.resolve(__dirname, '..', 'pyright-internal', 'typeshed-fallback');
@ -31,6 +28,7 @@ module.exports = (_, { mode }) => {
clean: true,
},
devtool: mode === 'development' ? 'source-map' : 'nosources-source-map',
cache: mode === 'development' ? cacheConfig(__dirname, __filename) : undefined,
stats: {
all: false,
errors: true,

View File

@ -1,11 +1,11 @@
/* eslint-disable @typescript-eslint/no-var-requires */
//@ts-check
const fsExtra = require('fs-extra');
const { promises: fsAsync } = require('fs');
const chalk = require('chalk');
async function main() {
const packageJson = await fsExtra.readFile('package.json', 'utf-8');
const packageJson = await fsAsync.readFile('package.json', 'utf-8');
const obj = JSON.parse(packageJson);
const name = obj.name;

View File

@ -1,15 +1,15 @@
/* eslint-disable @typescript-eslint/no-var-requires */
//@ts-check
const fsExtra = require('fs-extra');
const { promises: fsAsync } = require('fs');
const detectIndent = require('detect-indent');
/**
* @param {string} [filepath]
* @param {(obj: any) => void} [modifier]
* @param {string} filepath
* @param {(obj: any) => void} modifier
*/
async function modifyJsonInPlace(filepath, modifier) {
const input = await fsExtra.readFile(filepath, 'utf-8');
const input = await fsAsync.readFile(filepath, 'utf-8');
const indent = detectIndent(input);
const obj = JSON.parse(input);
@ -25,7 +25,7 @@ async function modifyJsonInPlace(filepath, modifier) {
output = output.replace(/\n/g, '\r\n');
}
await fsExtra.writeFile(filepath, output, 'utf-8');
await fsAsync.writeFile(filepath, output, 'utf-8');
}
async function main() {

View File

@ -41,6 +41,17 @@
"fastq": "^1.6.0"
}
},
"@types/copy-webpack-plugin": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@types/copy-webpack-plugin/-/copy-webpack-plugin-8.0.0.tgz",
"integrity": "sha512-EJ9Nd0a628uwvgCEt7bN4F6f2jA0O+i+ajAyq9F4jRTqJJ0ro13C22GL/bnvnsSoKuN/O93yfXqZWhn2R70b/g==",
"dev": true,
"requires": {
"@types/node": "*",
"tapable": "^2.0.0",
"webpack": "^5.1.0"
}
},
"@types/eslint": {
"version": "7.2.10",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.10.tgz",
@ -318,12 +329,6 @@
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true
},
"at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
"dev": true
},
"azure-devops-node-api": {
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-10.2.2.tgz",
@ -765,18 +770,6 @@
"path-exists": "^4.0.0"
}
},
"fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"requires": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -1021,16 +1014,6 @@
"minimist": "^1.2.0"
}
},
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
}
},
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@ -1755,12 +1738,6 @@
"integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==",
"dev": true
},
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"dev": true
},
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",

View File

@ -768,12 +768,11 @@
"vscode-languageserver-protocol": "3.16.0"
},
"devDependencies": {
"@types/copy-webpack-plugin": "^8.0.0",
"@types/node": "^12.20.13",
"@types/vscode": "~1.54.0",
"chalk": "^4.1.1",
"copy-webpack-plugin": "^8.1.1",
"detect-indent": "^6.0.0",
"fs-extra": "^9.1.0",
"shx": "^0.3.3",
"ts-loader": "^9.1.2",
"tsconfig-paths-webpack-plugin": "^3.5.1",

View File

@ -9,6 +9,12 @@
}
},
"include": [
"src/**/*"
"src/**/*",
"**/*.js"
],
"exclude": [
"node_modules",
"dist",
"out"
]
}
}

View File

@ -3,13 +3,10 @@
* Copyright: Microsoft 2018
*/
/* eslint-disable @typescript-eslint/no-var-requires */
//@ts-check
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');
const { monorepoResourceNameMapper } = require('../../build/lib/webpack');
const { cacheConfig, monorepoResourceNameMapper } = require('../../build/lib/webpack');
const outPath = path.resolve(__dirname, 'dist');
const typeshedFallback = path.resolve(__dirname, '..', 'pyright-internal', 'typeshed-fallback');
@ -32,6 +29,7 @@ module.exports = (_, { mode }) => {
clean: true,
},
devtool: mode === 'development' ? 'source-map' : 'nosources-source-map',
cache: mode === 'development' ? cacheConfig(__dirname, __filename) : undefined,
stats: {
all: false,
errors: true,

View File

@ -15,6 +15,7 @@
"sourceMap": true,
"outDir": "./out",
"noImplicitReturns": true,
"checkJs": true,
},
"exclude": [
"node_modules",