Skip to content

Commit 0c31350

Browse files
committed
simonhaenisch#15 fixed formatting and added shx for cross platform rm command and improve ts config read in
1 parent 108a0ef commit 0c31350

File tree

3 files changed

+349
-185
lines changed

3 files changed

+349
-185
lines changed

index.ts

+142-184
Original file line numberDiff line numberDiff line change
@@ -1,200 +1,158 @@
11
import path from 'path';
22
import { Plugin } from 'rollup';
3-
import {
4-
CompilerOptions,
5-
findConfigFile,
6-
nodeModuleNameResolver,
7-
parseConfigFileTextToJson,
8-
sys,
9-
} from 'typescript';
3+
import * as ts from 'typescript';
104

115
export const typescriptPaths = ({
12-
absolute = true,
13-
nonRelative = false,
14-
preserveExtensions = false,
15-
tsConfigPath = findConfigFile('./', sys.fileExists),
16-
transform,
6+
absolute = true,
7+
nonRelative = false,
8+
preserveExtensions = false,
9+
tsConfigPath = ts.findConfigFile('./', ts.sys.fileExists),
10+
transform,
1711
}: Options = {}): Plugin => {
18-
const { compilerOptions } = getTsConfig(tsConfigPath);
19-
const outDir = compilerOptions.outDir ?? '.';
20-
21-
return {
22-
name: 'resolve-typescript-paths',
23-
resolveId: (importee: string, importer?: string) => {
24-
const enabled = Boolean(
25-
compilerOptions.paths || (compilerOptions.baseUrl && nonRelative)
26-
);
27-
28-
if (
29-
typeof importer === 'undefined' ||
30-
importee.startsWith('\0') ||
31-
!enabled
32-
) {
33-
return null;
34-
}
35-
36-
const hasMatchingPath =
37-
!!compilerOptions.paths &&
38-
Object.keys(compilerOptions.paths).some((path) =>
39-
new RegExp('^' + path.replace('*', '.+') + '$').test(importee)
40-
);
41-
42-
if (!hasMatchingPath && !nonRelative) {
43-
return null;
44-
}
45-
46-
if (importee.startsWith('.')) {
47-
return null; // never resolve relative modules, only non-relative
48-
}
49-
50-
const { resolvedModule } = nodeModuleNameResolver(
51-
importee,
52-
importer,
53-
compilerOptions,
54-
sys
55-
);
56-
57-
if (!resolvedModule) {
58-
return null;
59-
}
60-
61-
const { resolvedFileName } = resolvedModule;
62-
63-
if (!resolvedFileName || resolvedFileName.endsWith('.d.ts')) {
64-
return null;
65-
}
66-
67-
const targetFileName = path.join(
68-
outDir,
69-
preserveExtensions
70-
? resolvedFileName
71-
: resolvedFileName.replace(/\.tsx?$/i, '.js')
72-
);
73-
74-
const resolved = absolute
75-
? sys.resolvePath(targetFileName)
76-
: targetFileName;
77-
78-
return transform ? transform(resolved) : resolved;
79-
},
80-
};
12+
const compilerOptions = getTsConfig(tsConfigPath);
13+
14+
return {
15+
name: 'resolve-typescript-paths',
16+
resolveId: (importee: string, importer?: string) => {
17+
const enabled = Boolean(
18+
compilerOptions.paths || (compilerOptions.baseUrl && nonRelative),
19+
);
20+
21+
if (
22+
typeof importer === 'undefined' ||
23+
importee.startsWith('\0') ||
24+
!enabled
25+
) {
26+
return null;
27+
}
28+
29+
const hasMatchingPath =
30+
!!compilerOptions.paths &&
31+
Object.keys(compilerOptions.paths).some((path) =>
32+
new RegExp('^' + path.replace('*', '.+') + '$').test(importee),
33+
);
34+
35+
if (!hasMatchingPath && !nonRelative) {
36+
return null;
37+
}
38+
39+
if (importee.startsWith('.')) {
40+
return null; // never resolve relative modules, only non-relative
41+
}
42+
43+
const { resolvedModule } = ts.nodeModuleNameResolver(
44+
importee,
45+
importer,
46+
compilerOptions,
47+
ts.sys,
48+
);
49+
50+
if (!resolvedModule) {
51+
return null;
52+
}
53+
54+
const { resolvedFileName } = resolvedModule;
55+
56+
if (!resolvedFileName || resolvedFileName.endsWith('.d.ts')) {
57+
return null;
58+
}
59+
60+
// TODO: Do we need outDir as "resolvedFileName" is already correct absolute path
61+
const targetFileName = path.join(
62+
compilerOptions.outDir,
63+
preserveExtensions
64+
? resolvedFileName
65+
: resolvedFileName.replace(/\.tsx?$/i, '.js'),
66+
);
67+
68+
const resolved = absolute
69+
? ts.sys.resolvePath(targetFileName)
70+
: targetFileName;
71+
72+
return transform ? transform(resolved) : resolved;
73+
},
74+
};
8175
};
8276

8377
const getTsConfig = (configPath?: string): TsConfig => {
84-
const defaults: TsConfig = { compilerOptions: { outDir: '.' } };
85-
if (typeof configPath !== 'string') {
86-
return defaults;
87-
}
88-
89-
// Read in tsconfig.json
90-
const configJson = sys.readFile(configPath);
91-
if (configJson == null) {
92-
return defaults;
93-
}
94-
95-
const { config: rootConfig } = parseConfigFileTextToJson(
96-
configPath,
97-
configJson
98-
);
99-
const rootConfigWithDefaults = {
100-
...rootConfig,
101-
...defaults,
102-
compilerOptions: {
103-
...defaults.compilerOptions,
104-
...(rootConfig.compilerOptions ?? {}),
105-
},
106-
};
107-
const resolvedConfig = handleTsConfigExtends(
108-
rootConfigWithDefaults,
109-
configPath
110-
);
111-
112-
return resolvedConfig;
113-
};
114-
115-
const handleTsConfigExtends = (
116-
config: TsConfig,
117-
rootConfigPath: string
118-
): TsConfig => {
119-
if (!('extends' in config) || typeof config.extends !== 'string') {
120-
return config;
121-
}
122-
123-
let extendedConfigPath;
124-
try {
125-
// Try to resolve as a module (npm)
126-
extendedConfigPath = require.resolve(config.extends);
127-
} catch (e) {
128-
// Try to resolve as a file relative to the current config
129-
extendedConfigPath = path.join(
130-
path.dirname(rootConfigPath),
131-
config.extends
132-
);
133-
}
134-
135-
// Read in extended tsconfig.json
136-
const extendedConfig = getTsConfig(extendedConfigPath);
137-
138-
// Merge base config and current config.
139-
// This does not handle array concatenation or nested objects,
140-
// besides 'compilerOptions' paths as the other options are not relevant
141-
config = {
142-
...extendedConfig,
143-
...config,
144-
compilerOptions: {
145-
...extendedConfig.compilerOptions,
146-
...config.compilerOptions,
147-
paths: {
148-
...(extendedConfig.compilerOptions.paths ?? {}),
149-
...(config.compilerOptions.paths ?? {}),
150-
},
151-
},
152-
};
153-
154-
// Remove the "extends" field
155-
delete config.extends;
156-
157-
return config;
78+
const defaults: TsConfig = { outDir: '.' };
79+
if (typeof configPath !== 'string') {
80+
return defaults;
81+
}
82+
83+
// Define a host object that implements ParseConfigFileHost.
84+
// The host provides file system operations and error handling for parsing the configuration file.
85+
const host: ts.ParseConfigFileHost = {
86+
fileExists: ts.sys.fileExists,
87+
readFile: ts.sys.readFile,
88+
readDirectory: ts.sys.readDirectory,
89+
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
90+
getCurrentDirectory: ts.sys.getCurrentDirectory,
91+
onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
92+
console.error(
93+
'Unrecoverable error in config file:',
94+
diagnostic.messageText,
95+
);
96+
process.exit(1);
97+
},
98+
};
99+
100+
// Read in tsconfig.json
101+
const parsedCommandLine = ts.getParsedCommandLineOfConfigFile(
102+
configPath,
103+
{},
104+
host,
105+
);
106+
107+
// Access the parsed tsconfig.json file options
108+
let resolvedConfig = {};
109+
if (parsedCommandLine != null) {
110+
resolvedConfig = parsedCommandLine.options;
111+
} else {
112+
console.error('Failed to parse TypeScript configuration file:', configPath);
113+
process.exit(1);
114+
}
115+
116+
return { ...defaults, ...resolvedConfig };
158117
};
159118

160119
export interface Options {
161-
/**
162-
* Whether to resolve to absolute paths; defaults to `true`.
163-
*/
164-
absolute?: boolean;
165-
166-
/**
167-
* Whether to resolve non-relative paths based on tsconfig's `baseUrl`, even
168-
* if none of the `paths` are matched; defaults to `false`.
169-
*
170-
* @see https://www.typescriptlang.org/docs/handbook/module-resolution.html#relative-vs-non-relative-module-imports
171-
* @see https://www.typescriptlang.org/docs/handbook/module-resolution.html#base-url
172-
*/
173-
nonRelative?: boolean;
174-
175-
/**
176-
* Whether to preserve `.ts` and `.tsx` file extensions instead of having them
177-
* changed to `.js`; defaults to `false`.
178-
*/
179-
preserveExtensions?: boolean;
180-
181-
/**
182-
* Custom path to your `tsconfig.json`. Use this if the plugin can't seem to
183-
* find the correct one by itself.
184-
*/
185-
tsConfigPath?: string;
186-
187-
/**
188-
* If the plugin successfully resolves a path, this function allows you to
189-
* hook into the process and transform that path before it is returned.
190-
*/
191-
transform?(path: string): string;
120+
/**
121+
* Whether to resolve to absolute paths; defaults to `true`.
122+
*/
123+
absolute?: boolean;
124+
125+
/**
126+
* Whether to resolve non-relative paths based on tsconfig's `baseUrl`, even
127+
* if none of the `paths` are matched; defaults to `false`.
128+
*
129+
* @see https://www.typescriptlang.org/docs/handbook/module-resolution.html#relative-vs-non-relative-module-imports
130+
* @see https://www.typescriptlang.org/docs/handbook/module-resolution.html#base-url
131+
*/
132+
nonRelative?: boolean;
133+
134+
/**
135+
* Whether to preserve `.ts` and `.tsx` file extensions instead of having them
136+
* changed to `.js`; defaults to `false`.
137+
*/
138+
preserveExtensions?: boolean;
139+
140+
/**
141+
* Custom path to your `tsconfig.json`. Use this if the plugin can't seem to
142+
* find the correct one by itself.
143+
*/
144+
tsConfigPath?: string;
145+
146+
/**
147+
* If the plugin successfully resolves a path, this function allows you to
148+
* hook into the process and transform that path before it is returned.
149+
*/
150+
transform?(path: string): string;
192151
}
193152

194-
interface TsConfig {
195-
compilerOptions: CompilerOptions;
196-
extends?: string;
197-
}
153+
type TsConfig = {
154+
outDir: string;
155+
} & Omit<ts.CompilerOptions, 'outDir'>;
198156

199157
/**
200158
* For backwards compatibility.

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"scripts": {
1111
"test": "npm run prepare && node test",
1212
"preversion": "npm test",
13-
"prepare": "rm -rf dist && tsc"
13+
"prepare": "shx rm -rf dist && tsc",
14+
"format": "prettier --write \"**/*.{js,ts,tsx,md}\""
1415
},
1516
"author": "Simon Haenisch (https://github.com/simonhaenisch)",
1617
"repository": "simonhaenisch/rollup-plugin-typescript-paths",
@@ -28,6 +29,7 @@
2829
"prettier": "2.8.8",
2930
"prettier-plugin-organize-imports": "3.2.2",
3031
"rollup": "2.79.1",
32+
"shx": "^0.3.4",
3133
"typescript": "5.1.3"
3234
},
3335
"peerDependencies": {

0 commit comments

Comments
 (0)