From d14c81f45d5b4415279c285fbb0b4a36c83426bf Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Mon, 20 Mar 2023 17:46:45 +0000 Subject: [PATCH] Replace `detective-typescript` (#739) --- package-lock.json | 274 ------------------ .../tailwindcss-language-server/package.json | 1 - .../src/util/getModuleDependencies.ts | 98 +++++-- 3 files changed, 72 insertions(+), 301 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4d68db..e1867a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,6 @@ "debounce": "1.2.0", "deepmerge": "4.2.2", "detect-indent": "6.0.0", - "detective-typescript": "9.0.0", "dlv": "1.1.3", "dset": "3.1.2", "enhanced-resolve-301": "0.0.1", @@ -4964,18 +4963,6 @@ } } }, - "node_modules/@typescript-eslint/types": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz", - "integrity": "sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/typescript-estree": { "version": "2.34.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", @@ -5002,30 +4989,6 @@ } } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz", - "integrity": "sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==", - "dependencies": { - "@typescript-eslint/types": "5.55.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/@zkochan/cmd-shim": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz", @@ -5460,14 +5423,6 @@ "node": ">=0.10.0" } }, - "node_modules/ast-module-types": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-3.0.0.tgz", - "integrity": "sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ==", - "engines": { - "node": ">=6.0" - } - }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -7760,115 +7715,6 @@ "node": ">=8" } }, - "node_modules/detective-typescript": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-9.0.0.tgz", - "integrity": "sha512-lR78AugfUSBojwlSRZBeEqQ1l8LI7rbxOl1qTUnGLcjZQDjZmrZCb7R46rK8U8B5WzFvJrxa7fEBA8FoD/n5fA==", - "dependencies": { - "@typescript-eslint/typescript-estree": "^5.13.0", - "ast-module-types": "^3.0.0", - "node-source-walk": "^5.0.0", - "typescript": "^4.5.5" - }, - "engines": { - "node": "^12.20.0 || ^14.14.0 || >=16.0.0" - } - }, - "node_modules/detective-typescript/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz", - "integrity": "sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==", - "dependencies": { - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/visitor-keys": "5.55.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/detective-typescript/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/detective-typescript/node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/detective-typescript/node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/detective-typescript/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detective-typescript/node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/detective-typescript/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, "node_modules/dezalgo": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", @@ -15553,17 +15399,6 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" }, - "node_modules/node-source-walk": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-5.0.0.tgz", - "integrity": "sha512-58APXoMXpmmU+oVBJFajhTCoD8d/OGtngnVAWzIo2A8yn0IXwBzvIVIsTzoie/SrA37u+1hnpNz2HMWx/VIqlw==", - "dependencies": { - "@babel/parser": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/nopt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", @@ -25728,11 +25563,6 @@ "eslint-visitor-keys": "^1.1.0" } }, - "@typescript-eslint/types": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz", - "integrity": "sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==" - }, "@typescript-eslint/typescript-estree": { "version": "2.34.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", @@ -25747,22 +25577,6 @@ "tsutils": "^3.17.1" } }, - "@typescript-eslint/visitor-keys": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz", - "integrity": "sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==", - "requires": { - "@typescript-eslint/types": "5.55.0", - "eslint-visitor-keys": "^3.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" - } - } - }, "@zkochan/cmd-shim": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz", @@ -26096,11 +25910,6 @@ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, - "ast-module-types": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-3.0.0.tgz", - "integrity": "sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ==" - }, "ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -27870,81 +27679,6 @@ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==" }, - "detective-typescript": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-9.0.0.tgz", - "integrity": "sha512-lR78AugfUSBojwlSRZBeEqQ1l8LI7rbxOl1qTUnGLcjZQDjZmrZCb7R46rK8U8B5WzFvJrxa7fEBA8FoD/n5fA==", - "requires": { - "@typescript-eslint/typescript-estree": "^5.13.0", - "ast-module-types": "^3.0.0", - "node-source-walk": "^5.0.0", - "typescript": "^4.5.5" - }, - "dependencies": { - "@typescript-eslint/typescript-estree": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz", - "integrity": "sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==", - "requires": { - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/visitor-keys": "5.55.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "requires": { - "path-type": "^4.0.0" - } - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - } - } - }, "dezalgo": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", @@ -33929,14 +33663,6 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" }, - "node-source-walk": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-5.0.0.tgz", - "integrity": "sha512-58APXoMXpmmU+oVBJFajhTCoD8d/OGtngnVAWzIo2A8yn0IXwBzvIVIsTzoie/SrA37u+1hnpNz2HMWx/VIqlw==", - "requires": { - "@babel/parser": "^7.0.0" - } - }, "nopt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json index f32d1b2..f4200b6 100644 --- a/packages/tailwindcss-language-server/package.json +++ b/packages/tailwindcss-language-server/package.json @@ -46,7 +46,6 @@ "culori": "0.20.1", "debounce": "1.2.0", "deepmerge": "4.2.2", - "detective-typescript": "9.0.0", "dlv": "1.1.3", "dset": "3.1.2", "enhanced-resolve-301": "0.0.1", diff --git a/packages/tailwindcss-language-server/src/util/getModuleDependencies.ts b/packages/tailwindcss-language-server/src/util/getModuleDependencies.ts index 15f45d0..fc0ae8c 100644 --- a/packages/tailwindcss-language-server/src/util/getModuleDependencies.ts +++ b/packages/tailwindcss-language-server/src/util/getModuleDependencies.ts @@ -1,41 +1,87 @@ +// https://github.com/tailwindlabs/tailwindcss/blob/e046a37dbc17f163b066cd34a559e7c8a276bd8b/src/lib/getModuleDependencies.js import fs from 'fs' import path from 'path' -import resolve from 'resolve' -import detective from 'detective-typescript' import normalizePath from 'normalize-path' -function createModule(file: string): { file: string; requires: string[] } { - let source = fs.readFileSync(file, 'utf-8') - return { file, requires: detective(source, { mixedImports: true }) } +let jsExtensions = ['.js', '.cjs', '.mjs'] + +// Given the current file `a.ts`, we want to make sure that when importing `b` that we resolve +// `b.ts` before `b.js` +// +// E.g.: +// +// a.ts +// b // .ts +// c // .ts +// a.js +// b // .js or .ts + +let jsResolutionOrder = ['', '.js', '.cjs', '.mjs', '.ts', '.cts', '.mts', '.jsx', '.tsx'] +let tsResolutionOrder = ['', '.ts', '.cts', '.mts', '.tsx', '.js', '.cjs', '.mjs', '.jsx'] + +function resolveWithExtension(file: string, extensions: string[]): string | null { + // Try to find `./a.ts`, `./a.ts`, ... from `./a` + for (let ext of extensions) { + let full = `${file}${ext}` + if (fs.existsSync(full) && fs.statSync(full).isFile()) { + return full + } + } + + // Try to find `./a/index.js` from `./a` + for (let ext of extensions) { + let full = `${file}/index${ext}` + if (fs.existsSync(full)) { + return full + } + } + + return null } -function* _getModuleDependencies(entryFile: string): Generator { - yield entryFile +function* _getModuleDependencies( + filename: string, + base: string, + seen: Set +): Generator { + let ext = path.extname(filename) - let mod = createModule(entryFile) + // Try to find the file + let absoluteFile = resolveWithExtension( + path.resolve(base, filename), + jsExtensions.includes(ext) ? jsResolutionOrder : tsResolutionOrder + ) + if (absoluteFile === null) return // File doesn't exist - let ext = path.extname(entryFile) - let isTypeScript = ext === '.ts' || ext === '.cts' || ext === '.mts' - let extensions = [...(isTypeScript ? ['.ts', '.cts', '.mts'] : []), '.js', '.cjs', '.mjs'] + // Prevent infinite loops when there are circular dependencies + if (seen.has(absoluteFile)) return // Already seen + seen.add(absoluteFile) - // Iterate over the modules, even when new - // ones are being added - for (let dep of mod.requires) { - // Only track local modules, not node_modules - if (!dep.startsWith('./') && !dep.startsWith('../')) { - continue - } + // Mark the file as a dependency + yield absoluteFile - try { - let basedir = path.dirname(mod.file) - let depPath = resolve.sync(dep, { basedir, extensions }) - yield* _getModuleDependencies(depPath) - } catch {} + // Resolve new base for new imports/requires + base = path.dirname(absoluteFile) + + let contents = fs.readFileSync(absoluteFile, 'utf-8') + + // Find imports/requires + for (let match of [ + ...contents.matchAll(/import[\s\S]*?['"](.{3,}?)['"]/gi), + ...contents.matchAll(/import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi), + ...contents.matchAll(/require\(['"`](.{3,})['"`]\)/gi), + ]) { + // Bail out if it's not a relative file + if (!match[1].startsWith('.')) continue + + yield* _getModuleDependencies(match[1], base, seen) } } -export function getModuleDependencies(entryFile: string): string[] { - return Array.from(_getModuleDependencies(entryFile)) - .filter((file) => file !== entryFile) +export function getModuleDependencies(absoluteFilePath: string): string[] { + return Array.from( + _getModuleDependencies(absoluteFilePath, path.dirname(absoluteFilePath), new Set()) + ) + .filter((file) => file !== absoluteFilePath) .map((file) => normalizePath(file)) }