From 704b67af87842cf59988d846083430041764f426 Mon Sep 17 00:00:00 2001 From: Jasper De Moor Date: Thu, 18 Nov 2021 17:14:01 +0100 Subject: [PATCH 1/4] tsconfig fixtures --- packages/sandpack-core/src/resolver/fixture/jsconfig.json | 7 +++++++ .../sandpack-core/src/resolver/fixture/src/app/index.js | 0 .../src/resolver/fixture/src/app/something.js | 0 .../src/resolver/fixture/src/app_config/test.js | 0 .../src/resolver/fixture/src/components/Button/index.tsx | 0 .../src/resolver/fixture/src/components/Card/index.jsx | 0 packages/sandpack-core/src/resolver/fixture/tsconfig.json | 6 ++++++ 7 files changed, 13 insertions(+) create mode 100644 packages/sandpack-core/src/resolver/fixture/jsconfig.json create mode 100644 packages/sandpack-core/src/resolver/fixture/src/app/index.js create mode 100644 packages/sandpack-core/src/resolver/fixture/src/app/something.js create mode 100644 packages/sandpack-core/src/resolver/fixture/src/app_config/test.js create mode 100644 packages/sandpack-core/src/resolver/fixture/src/components/Button/index.tsx create mode 100644 packages/sandpack-core/src/resolver/fixture/src/components/Card/index.jsx create mode 100644 packages/sandpack-core/src/resolver/fixture/tsconfig.json diff --git a/packages/sandpack-core/src/resolver/fixture/jsconfig.json b/packages/sandpack-core/src/resolver/fixture/jsconfig.json new file mode 100644 index 00000000000..c23d580f5bb --- /dev/null +++ b/packages/sandpack-core/src/resolver/fixture/jsconfig.json @@ -0,0 +1,7 @@ +{ + "baseUrl": "src", + "paths": { + "@app/*": ["app/*"], + "@config/*": ["app_config/*"] + } +} diff --git a/packages/sandpack-core/src/resolver/fixture/src/app/index.js b/packages/sandpack-core/src/resolver/fixture/src/app/index.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/sandpack-core/src/resolver/fixture/src/app/something.js b/packages/sandpack-core/src/resolver/fixture/src/app/something.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/sandpack-core/src/resolver/fixture/src/app_config/test.js b/packages/sandpack-core/src/resolver/fixture/src/app_config/test.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/sandpack-core/src/resolver/fixture/src/components/Button/index.tsx b/packages/sandpack-core/src/resolver/fixture/src/components/Button/index.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/sandpack-core/src/resolver/fixture/src/components/Card/index.jsx b/packages/sandpack-core/src/resolver/fixture/src/components/Card/index.jsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/sandpack-core/src/resolver/fixture/tsconfig.json b/packages/sandpack-core/src/resolver/fixture/tsconfig.json new file mode 100644 index 00000000000..ac48590e283 --- /dev/null +++ b/packages/sandpack-core/src/resolver/fixture/tsconfig.json @@ -0,0 +1,6 @@ +{ + "baseUrl": "src", + "paths": { + "~/*": ["./*"] + } +} From 39bf9179ad866eb9760208c63737ace7041e7a4a Mon Sep 17 00:00:00 2001 From: Jasper De Moor Date: Thu, 18 Nov 2021 20:26:33 +0100 Subject: [PATCH 2/4] support tsconfig paths and baseUrl --- packages/sandpack-core/package.json | 3 +- .../src/resolver/fixture/jsconfig.json | 7 --- .../fixture/src/not_app/something-else.ts | 0 .../src/resolver/fixture/tsconfig.json | 5 +- .../src/resolver/resolver.test.ts | 42 +++++++++++++ .../sandpack-core/src/resolver/resolver.ts | 60 ++++++++++++++++++- .../src/resolver/utils/tsconfig.ts | 49 +++++++++++++++ yarn.lock | 4 +- 8 files changed, 156 insertions(+), 14 deletions(-) delete mode 100644 packages/sandpack-core/src/resolver/fixture/jsconfig.json create mode 100644 packages/sandpack-core/src/resolver/fixture/src/not_app/something-else.ts create mode 100644 packages/sandpack-core/src/resolver/utils/tsconfig.ts diff --git a/packages/sandpack-core/package.json b/packages/sandpack-core/package.json index ba67e3687b7..3edf24a590f 100644 --- a/packages/sandpack-core/package.json +++ b/packages/sandpack-core/package.json @@ -30,6 +30,7 @@ "micromatch": "^4.0.4", "node-fetch": "^2.6.1", "sandbox-hooks": "0.1.0", - "source-map": "^0.7.3" + "source-map": "^0.7.3", + "strip-json-comments": "3" } } diff --git a/packages/sandpack-core/src/resolver/fixture/jsconfig.json b/packages/sandpack-core/src/resolver/fixture/jsconfig.json deleted file mode 100644 index c23d580f5bb..00000000000 --- a/packages/sandpack-core/src/resolver/fixture/jsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "baseUrl": "src", - "paths": { - "@app/*": ["app/*"], - "@config/*": ["app_config/*"] - } -} diff --git a/packages/sandpack-core/src/resolver/fixture/src/not_app/something-else.ts b/packages/sandpack-core/src/resolver/fixture/src/not_app/something-else.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/sandpack-core/src/resolver/fixture/tsconfig.json b/packages/sandpack-core/src/resolver/fixture/tsconfig.json index ac48590e283..90205e4b016 100644 --- a/packages/sandpack-core/src/resolver/fixture/tsconfig.json +++ b/packages/sandpack-core/src/resolver/fixture/tsconfig.json @@ -1,6 +1,9 @@ { "baseUrl": "src", "paths": { - "~/*": ["./*"] + "~/*": ["./*"], + "@app/*": ["app/*", "not_app/*"], + "@config/*": ["app_config/*"], + "something-special": ["app/something"] } } diff --git a/packages/sandpack-core/src/resolver/resolver.test.ts b/packages/sandpack-core/src/resolver/resolver.test.ts index b79057f6d44..0cfd0da8272 100644 --- a/packages/sandpack-core/src/resolver/resolver.test.ts +++ b/packages/sandpack-core/src/resolver/resolver.test.ts @@ -480,4 +480,46 @@ describe('resolve', () => { expect(normalizeModuleSpecifier('react//test')).toBe('react/test'); }); }); + + describe('tsconfig', () => { + it('should be able to resolve relative to basePath of tsconfig.json', () => { + const resolved = resolveSync('app', { + filename: '/foo.js', + extensions: ['.ts', '.tsx', '.js', '.jsx'], + isFile, + readFile, + }); + expect(resolved).toBe('/src/app/index.js'); + }); + + it('should be able to resolve paths that are simple aliases', () => { + const resolved = resolveSync('something-special', { + filename: '/foo.js', + extensions: ['.ts', '.tsx', '.js', '.jsx'], + isFile, + readFile, + }); + expect(resolved).toBe('/src/app/something.js'); + }); + + it('should be able to resolve wildcard paths with single char', () => { + const resolved = resolveSync('~/app_config/test', { + filename: '/foo.js', + extensions: ['.ts', '.tsx', '.js', '.jsx'], + isFile, + readFile, + }); + expect(resolved).toBe('/src/app_config/test.js'); + }); + + it('should be able to resolve wildcard paths with name', () => { + const resolved = resolveSync('@app/something', { + filename: '/foo.js', + extensions: ['.ts', '.tsx', '.js', '.jsx'], + isFile, + readFile, + }); + expect(resolved).toBe('/src/app/something.js'); + }); + }); }); diff --git a/packages/sandpack-core/src/resolver/resolver.ts b/packages/sandpack-core/src/resolver/resolver.ts index e7c01d7cf80..74a05a46314 100644 --- a/packages/sandpack-core/src/resolver/resolver.ts +++ b/packages/sandpack-core/src/resolver/resolver.ts @@ -7,8 +7,13 @@ import * as pathUtils from '@codesandbox/common/lib/utils/path'; import { ModuleNotFoundError } from './errors/ModuleNotFound'; import { ProcessedPackageJSON, processPackageJSON } from './utils/pkg-json'; import { isFile, FnIsFile, FnReadFile, getParentDirectories } from './utils/fs'; +import { + ProcessedTSConfig, + processTSConfig, + getPotentialPathsFromTSConfig, +} from './utils/tsconfig'; -export type PackageCache = Map; +export type ResolverCache = Map; export interface IResolveOptionsInput { filename: string; @@ -16,12 +21,12 @@ export interface IResolveOptionsInput { isFile: FnIsFile; readFile: FnReadFile; moduleDirectories?: string[]; - packageCache?: PackageCache; + packageCache?: ResolverCache; } interface IResolveOptions extends IResolveOptionsInput { moduleDirectories: string[]; - packageCache: PackageCache; + packageCache: ResolverCache; } function normalizeResolverOptions(opts: IResolveOptionsInput): IResolveOptions { @@ -259,6 +264,37 @@ export function normalizeModuleSpecifier(specifier: string): string { return normalized; } +const TS_CONFIG_CACHE_KEY = '__root_tsconfig'; +function* getTSConfig( + opts: IResolveOptions +): Generator { + const cachedConfig = opts.packageCache.get(TS_CONFIG_CACHE_KEY); + if (cachedConfig != null) { + return cachedConfig; + } + + let config: ProcessedTSConfig | false = false; + try { + const contents = yield* opts.readFile('/tsconfig.json'); + const processed = processTSConfig(contents); + if (processed) { + config = processed; + } + } catch (err) { + try { + const contents = yield* opts.readFile('/jsconfig.json'); + const processed = processTSConfig(contents); + if (processed) { + config = processed; + } + } catch { + // do nothing + } + } + opts.packageCache.set(TS_CONFIG_CACHE_KEY, config); + return config; +} + export const resolver = gensync< (moduleSpecifier: string, inputOpts: IResolveOptionsInput) => string >(function* resolve(moduleSpecifier, inputOpts): Generator { @@ -267,6 +303,24 @@ export const resolver = gensync< const modulePath = yield* resolveModule(normalizedSpecifier, opts); if (modulePath[0] !== '/') { + // This isn't a node module, we can attempt to resolve using a tsconfig/jsconfig + if (!opts.filename.includes('/node_modules')) { + const parsedTSConfig = yield* getTSConfig(opts); + if (parsedTSConfig) { + const potentialPaths = getPotentialPathsFromTSConfig( + modulePath, + parsedTSConfig + ); + for (const potentialPath of potentialPaths) { + try { + return yield* resolve(potentialPath, opts); + } catch { + // do nothing, it's probably a node_module in this case + } + } + } + } + try { return yield* resolveNodeModule(modulePath, opts); } catch (e) { diff --git a/packages/sandpack-core/src/resolver/utils/tsconfig.ts b/packages/sandpack-core/src/resolver/utils/tsconfig.ts new file mode 100644 index 00000000000..b35c60a76ef --- /dev/null +++ b/packages/sandpack-core/src/resolver/utils/tsconfig.ts @@ -0,0 +1,49 @@ +import stripJsonComments from 'strip-json-comments'; +import * as pathUtils from '@codesandbox/common/lib/utils/path'; + +export interface ProcessedTSConfig { + baseUrl: string; + paths: Record; +} + +export function processTSConfig(content: string): ProcessedTSConfig | null { + const parsed = JSON.parse(stripJsonComments(content)); + if (parsed.baseUrl) { + const paths: ProcessedTSConfig['paths'] = {}; + if (parsed.paths) { + for (const p of Object.keys(parsed.paths)) { + paths[p] = parsed.paths[p].map((val: string) => { + return pathUtils.join('/', parsed.baseUrl, val).replace(/\*/g, ''); + }); + } + } + + return { + baseUrl: pathUtils.join('/', parsed.baseUrl), + paths, + }; + } + return null; +} + +export function getPotentialPathsFromTSConfig( + moduleSpecifier: string, + config: ProcessedTSConfig +): string[] { + const res = []; + for (const p of Object.keys(config.paths)) { + if (p.endsWith('*')) { + const prefix = p.substring(0, p.length - 1); + if (moduleSpecifier.startsWith(prefix)) { + const suffix = moduleSpecifier.substr(prefix.length); + for (const alias of config.paths[p]) { + res.push(alias + suffix); + } + } + } else if (moduleSpecifier === p) { + res.push(...config.paths[p]); + } + } + res.push(pathUtils.join(config.baseUrl, moduleSpecifier)); + return res; +} diff --git a/yarn.lock b/yarn.lock index 3c0804fee98..95bdf1da18a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13286,7 +13286,7 @@ esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@1.0.1, esquery@^1.0.0, esquery@^1.0.1: +esquery@^1.0.0, esquery@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== @@ -30673,7 +30673,7 @@ strip-json-comments@2.0.1, strip-json-comments@^2.0.0, strip-json-comments@^2.0. version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== From ba04fd5b48f526c619bda6a1d68807d7b333da63 Mon Sep 17 00:00:00 2001 From: Jasper De Moor Date: Thu, 18 Nov 2021 20:38:05 +0100 Subject: [PATCH 3/4] rename packageCache to resolverCache --- packages/sandpack-core/src/manager.ts | 12 ++++++------ packages/sandpack-core/src/resolver/resolver.ts | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/sandpack-core/src/manager.ts b/packages/sandpack-core/src/manager.ts index bfb9f908eb8..efa6d6ab561 100644 --- a/packages/sandpack-core/src/manager.ts +++ b/packages/sandpack-core/src/manager.ts @@ -12,7 +12,7 @@ import { endMeasure, now } from '@codesandbox/common/lib/utils/metrics'; import DependencyNotFoundError from 'sandbox-hooks/errors/dependency-not-found-error'; import ModuleNotFoundError from 'sandbox-hooks/errors/module-not-found-error'; -import { PackageCache, resolveAsync, resolveSync } from './resolver/resolver'; +import { ResolverCache, resolveAsync, resolveSync } from './resolver/resolver'; import { generateBenchmarkInterface } from './utils/benchmark'; import { Module } from './types/module'; import { @@ -193,7 +193,7 @@ export default class Manager implements IEvaluator { // All paths are resolved at least twice: during transpilation and evaluation. // We can improve performance by almost 2x in this scenario if we cache the lookups cachedPaths: { [path: string]: { [path: string]: string } }; - resolverPackageCache: PackageCache; + resolverCache: ResolverCache; configurations: ParsedConfigurationFiles; @@ -225,7 +225,7 @@ export default class Manager implements IEvaluator { this.stage = 'transpilation'; this.version = options.versionIdentifier; this.esmodules = new Map(); - this.resolverPackageCache = new Map(); + this.resolverCache = new Map(); /** * Contribute the file fetcher, which needs the manager to resolve the files @@ -283,7 +283,7 @@ export default class Manager implements IEvaluator { // Call this whenever the file structure or modules change, so before each compilation... resetResolverCache() { this.cachedPaths = {}; - this.resolverPackageCache = new Map(); + this.resolverCache = new Map(); } async evaluate(path: string, baseTModule?: TranspiledModule): Promise { @@ -830,7 +830,7 @@ export default class Manager implements IEvaluator { isFile: this.isFile, readFile: this.readFile, moduleDirectories: this.getModuleDirectories(), - packageCache: this.resolverPackageCache, + resolverCache: this.resolverCache, }); endMeasure(measureKey, { silent: true, lastTime: measureStartTime }); @@ -975,7 +975,7 @@ export default class Manager implements IEvaluator { isFile: this.isFile, readFile: this.readFile, moduleDirectories: this.getModuleDirectories(), - packageCache: this.resolverPackageCache, + resolverCache: this.resolverCache, }); endMeasure(measureKey, { silent: true, lastTime: measureStartTime }); diff --git a/packages/sandpack-core/src/resolver/resolver.ts b/packages/sandpack-core/src/resolver/resolver.ts index 74a05a46314..f1f50314d03 100644 --- a/packages/sandpack-core/src/resolver/resolver.ts +++ b/packages/sandpack-core/src/resolver/resolver.ts @@ -21,12 +21,12 @@ export interface IResolveOptionsInput { isFile: FnIsFile; readFile: FnReadFile; moduleDirectories?: string[]; - packageCache?: ResolverCache; + resolverCache?: ResolverCache; } interface IResolveOptions extends IResolveOptionsInput { moduleDirectories: string[]; - packageCache: ResolverCache; + resolverCache: ResolverCache; } function normalizeResolverOptions(opts: IResolveOptionsInput): IResolveOptions { @@ -43,7 +43,7 @@ function normalizeResolverOptions(opts: IResolveOptionsInput): IResolveOptions { isFile: opts.isFile, readFile: opts.readFile, moduleDirectories: [...normalizedModuleDirectories], - packageCache: opts.packageCache || new Map(), + resolverCache: opts.resolverCache || new Map(), }; } @@ -60,16 +60,16 @@ function* loadPackageJSON( const directories = getParentDirectories(filepath, rootDir); for (const directory of directories) { const packageFilePath = pathUtils.join(directory, 'package.json'); - let packageContent = opts.packageCache.get(packageFilePath); + let packageContent = opts.resolverCache.get(packageFilePath); if (packageContent === undefined) { try { packageContent = processPackageJSON( JSON.parse(yield* opts.readFile(packageFilePath)), pathUtils.dirname(packageFilePath) ); - opts.packageCache.set(packageFilePath, packageContent); + opts.resolverCache.set(packageFilePath, packageContent); } catch (err) { - opts.packageCache.set(packageFilePath, false); + opts.resolverCache.set(packageFilePath, false); } } if (packageContent) { @@ -268,7 +268,7 @@ const TS_CONFIG_CACHE_KEY = '__root_tsconfig'; function* getTSConfig( opts: IResolveOptions ): Generator { - const cachedConfig = opts.packageCache.get(TS_CONFIG_CACHE_KEY); + const cachedConfig = opts.resolverCache.get(TS_CONFIG_CACHE_KEY); if (cachedConfig != null) { return cachedConfig; } @@ -291,7 +291,7 @@ function* getTSConfig( // do nothing } } - opts.packageCache.set(TS_CONFIG_CACHE_KEY, config); + opts.resolverCache.set(TS_CONFIG_CACHE_KEY, config); return config; } From bf9c7c62cfac918bf6f84388af9acdb4a6184ec0 Mon Sep 17 00:00:00 2001 From: Jasper De Moor Date: Thu, 18 Nov 2021 20:44:33 +0100 Subject: [PATCH 4/4] add missing dep --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 95bdf1da18a..c074d582666 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13286,7 +13286,7 @@ esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.0, esquery@^1.0.1: +esquery@1.0.1, esquery@^1.0.0, esquery@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==