Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": { "checkJs": true }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function get(path) {
return path;
}

export function post(path, body) {
return [path, body];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SvelteKit's $lib, mapped by the generated .svelte-kit/tsconfig.json that a checkout lacks
import * as api from '$lib/api.js';
import { post } from '$lib/api';

export function load({ params }) {
return api.get(params.slug);
}

export function save(body) {
return post('/save', body);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Nuxt's ~ and @, mapped by the generated .nuxt/tsconfig.json that a checkout lacks
import { formatPrice } from '~/utils/price';
import { formatPrice as viaAt } from '@/utils/price';

export default function handler() {
return formatPrice(1) + viaAt(2);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "extends": "./.nuxt/tsconfig.json" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function formatPrice(n) {
return n;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Nuxt 4: the source dir is app/, and the root config only references the generated ones
import { formatDate } from '~/utils/date';

export function page(d) {
return formatDate(d);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function formatDate(d) {
return d;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"files": [],
"references": [{ "path": "./.nuxt/tsconfig.app.json" }, { "path": "./.nuxt/tsconfig.server.json" }]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "extends": "./.config/tsconfig.json" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// CONTROL: a missing extended config that no framework generates maps nothing
import { helper } from '$lib/helper';

export function probe() {
return helper();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function helper() {
return 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": { "baseUrl": ".", "paths": { "$lib/*": ["./shared/*"] } }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// CONTROL: the project maps $lib itself, and its own paths win over any framework default
import { pick } from '$lib/util';

export function chosen() {
return pick();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function pick() {
return 2;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function pick() {
return 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "name": "framework-alias-fixture", "type": "module" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import_cause other/probe.js:2:10 $lib/helper not_staged
package_entry framework-alias-fixture . [] DEFAULT_INDEX index.js MISSING_FILE -> -
unresolved other/probe.js:5:10 FUNCTION_CALL helper callee_untyped
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kit/src/routes/page.js:10:10 FUNCTION_CALL post -> known_edge kit/src/lib/api.js:5:1 post
kit/src/routes/page.js:6:10 METHOD_CALL api.get -> known_edge kit/src/lib/api.js:1:1 get
nuxt/server/api/price.js:6:10 FUNCTION_CALL formatPrice -> known_edge nuxt/utils/price.js:1:1 formatPrice
nuxt/server/api/price.js:6:27 FUNCTION_CALL viaAt -> known_edge nuxt/utils/price.js:1:1 formatPrice
nuxt4/app/pages/index.js:5:10 FUNCTION_CALL formatDate -> known_edge nuxt4/app/utils/date.js:1:1 formatDate
other/probe.js:5:10 FUNCTION_CALL helper -> ambiguous_unknown -
own/probe.js:5:10 FUNCTION_CALL pick -> known_edge own/shared/util.js:1:1 pick
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# defects: 0
9 changes: 9 additions & 0 deletions parser/src/test/javascript-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3210,6 +3210,13 @@ async function pathAliasesResolveThroughTheNearestConfig(): Promise<number> {
write('remix/app/route.js', "import { getNote } from '~/models/note.server';\n");
write('plain/jsconfig.json', '{ "compilerOptions": { "checkJs": true } }');
write('plain/probe.js', "import { checkA } from '@/lib/check';\n");
// a config that only extends the framework's generated one, absent from a checkout (#1756)
write('kit/jsconfig.json', '{ "extends": "./.svelte-kit/tsconfig.json" }');
write('kit/src/lib/api.js', 'export function get() { return 1; }\n');
write('kit/src/routes/page.js', "import * as api from '$lib/api.js';\n");
write('other/jsconfig.json', '{ "extends": "./.config/tsconfig.json" }');
write('other/src/lib/api.js', 'export function get() { return 1; }\n');
write('other/probe.js', "import * as api from '$lib/api.js';\n");
await new JavaScriptProjectAnalyzer().analyzeAll([root], { outputDir: out, baseMservPath: root, serviceVersionLink: 'gate-v1' });
const relations = readRelations(out);
const imports = relations.find((r) => r.name === 'js_import');
Expand All @@ -3225,6 +3232,8 @@ async function pathAliasesResolveThroughTheNearestConfig(): Promise<number> {
['src/app.js', './lib/check.js', 'RESOLVED_PROJECT', 'src/lib/check'],
['remix/app/route.js', '~/models/note.server', 'RESOLVED_PROJECT', 'remix/app/models/note.server'],
['plain/probe.js', '@/lib/check', 'UNRESOLVED_MISSING', ''],
['kit/src/routes/page.js', '$lib/api.js', 'RESOLVED_PROJECT', 'kit/src/lib/api'],
['other/probe.js', '$lib/api.js', 'UNRESOLVED_MISSING', ''],
];
for (const [file, specifier, wantOutcome, target] of want) {
const rows = imports.rows.filter((row) => fileOf.get(row[owner] ?? '') === file && row[spec] === specifier);
Expand Down
51 changes: 51 additions & 0 deletions parser/src/workflows/javascript/javascript-project-analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,10 +683,61 @@ function readPathAliases(configPath: string): PathAliases {
if (options.paths !== undefined) {
aliases.paths = options.paths;
aliases.pathsBasePath = options.pathsBasePath;
} else {
const generated = frameworkDefaultAliases(read.config, path.dirname(configPath));
if (generated !== undefined) {
aliases.paths = generated;
aliases.pathsBasePath = path.dirname(configPath);
}
}
return aliases;
}

/**
* The aliases a framework's GENERATED config would have supplied, when the project's
* config `extends` (or `references`) one that a checkout does not contain.
*
* SvelteKit writes `.svelte-kit/tsconfig.json` and Nuxt `.nuxt/tsconfig*.json` at
* dev/build time; the project's own config only points at them, so without this every
* `$lib/api` or `~/utils/price` import was UNRESOLVED_MISSING (#1756). The mapping is the
* framework's default one: SvelteKit `$lib` → `src/lib`; Nuxt `~`, `@` → the source dir
* (`app/` first, as Nuxt 4 lays it out, then the root) and `~~`, `@@` → the root. A
* generated config that IS present is read as written, and a project `paths` wins.
*/
function frameworkDefaultAliases(config: unknown, configDirectory: string): ts.MapLike<string[]> | undefined {
const { extends: extended, references } = (config ?? {}) as { extends?: unknown; references?: unknown };
const named = [
...(Array.isArray(extended) ? extended : [extended]),
...(Array.isArray(references) ? references.map((r) => (r as { path?: unknown } | null)?.path) : []),
].filter((p): p is string => typeof p === 'string');
for (const pointed of named) {
const target = path.resolve(configDirectory, pointed);
// `./.nuxt/tsconfig.app.json`, `./.nuxt/tsconfig` (extension implied), or a
// `references` directory, which means its tsconfig.json
const namesFile = path.basename(target).startsWith('tsconfig');
const configFile = !namesFile ? path.join(target, 'tsconfig.json')
: target.endsWith('.json') ? target : `${target}.json`;
const generatedDirectory = path.dirname(configFile);
if (fs.existsSync(configFile) || path.dirname(generatedDirectory) !== configDirectory) {
continue;
}
const framework = path.basename(generatedDirectory);
if (framework === '.svelte-kit') {
return { '$lib': ['./src/lib'], '$lib/*': ['./src/lib/*'] };
}
if (framework === '.nuxt') {
const source = ['./app', '.'];
const root = ['.'];
const under = (dirs: string[]) => dirs.map((d) => `${d}/*`);
return {
'~': source, '~/*': under(source), '@': source, '@/*': under(source),
'~~': root, '~~/*': under(root), '@@': root, '@@/*': under(root),
};
}
}
return undefined;
}

/**
* Every emitted path hangs off this, not off `rootDir`.
*
Expand Down
Loading