From 27f8e008970506fcd5dd254507c2189f8270d0ff Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 23 Oct 2024 18:23:36 -0400 Subject: [PATCH 01/17] Exploring some extra logging --- src/project-roots.ts | 14 +++++++++- src/server.ts | 10 ++++++- test/__snapshots__/integration-test.ts.snap | 31 +++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/project-roots.ts b/src/project-roots.ts index b7a8f1130..e3f361b1e 100644 --- a/src/project-roots.ts +++ b/src/project-roots.ts @@ -87,6 +87,10 @@ export default class ProjectRoots { ignore: ['**/.git/**', '**/bower_components/**', '**/dist/**', '**/node_modules/**', '**/tmp/**'], }); + logInfo(`ELS: Found ${roots.length} roots for ${workspaceRoot}`); + + const start = Date.now(); + for (const rootPath of roots) { const filePath = path.join(workspaceRoot, rootPath); const fullPath = path.dirname(filePath); @@ -103,6 +107,8 @@ export default class ProjectRoots { await this.onProjectAdd(fullPath); } } + + logInfo(`ELS: iterating roots took ${Date.now() - start}ms`); } async initialize(workspaceRoot: string) { @@ -122,6 +128,8 @@ export default class ProjectRoots { if (this.projects.has(projectPath)) { const project = this.projects.get(projectPath) as Project; + logInfo(`Project already existed at ${projectPath}`); + return { initIssues: project.initIssues, providers: project.providers, @@ -154,12 +162,16 @@ export default class ProjectRoots { }; } + logInfo(`Initializing new project at ${projectPath} with ${this.localAddons.length} ELS addons.`); + const project = new Project(projectPath, this.localAddons, info); + const start = Date.now(); + await project.initialize(this.server); this.projects.set(projectPath, project); - logInfo(`Ember CLI project added at ${projectPath}`); + logInfo(`Ember CLI project added at ${projectPath}. (took ${Date.now() - start}ms)`); await project.init(this.server); return { diff --git a/src/server.ts b/src/server.ts index 2ed5e5311..31180d0f1 100644 --- a/src/server.ts +++ b/src/server.ts @@ -168,12 +168,14 @@ export default class Server { hoverProvider!: HoverProvider; codeActionProvider!: CodeActionProvider; async executeInitializers() { - logInfo('UELS: executeInitializers'); + logInfo('ELS: executeInitializers'); + logInfo(`ELS: ${this.initializers.length} initializers`); for (const initializer of this.initializers) { await initializer(); } + logInfo(`ELS: clearing initializers because they've been initialized`); this.initializers = []; } private onInitialized() { @@ -190,6 +192,10 @@ export default class Server { if (this.lazyInit) { try { + /** + * Don't await this so that "format on save" is not blocked. + * We still need to run the initializers so that we have hinting in component files though. + */ await this.executeInitializers(); } catch (e) { logError(e); @@ -524,6 +530,8 @@ export default class Server { this.initializers.push(async () => { await this.projectRoots.initialize(rootPath as string); + logInfo(`Found ${workspaceFolders?.length ?? 0} workspace folders for ${rootPath}`); + if (workspaceFolders && Array.isArray(workspaceFolders)) { for (const folder of workspaceFolders) { const folderPath = URI.parse(folder.uri).fsPath; diff --git a/test/__snapshots__/integration-test.ts.snap b/test/__snapshots__/integration-test.ts.snap index 7fb58873d..7758c2103 100644 --- a/test/__snapshots__/integration-test.ts.snap +++ b/test/__snapshots__/integration-test.ts.snap @@ -7339,6 +7339,37 @@ Object { } `; +exports[`integration async fs enabled: true Go to definition works for all supported cases to children route from application outlet 2`] = ` +Object { + "addonsMeta": Array [], + "registry": Object { + "routePath": Object { + "application": Array [ + "app/templates/application.hbs", + ], + "foo": Array [ + "app/templates/foo.hbs", + ], + }, + }, + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/templates/foo.hbs", + }, + ], +} +`; + exports[`integration async fs enabled: true Go to definition works for all supported cases to children route from meaningful outlet 1`] = ` Object { "addonsMeta": Array [], From a9570a882e92efe9b3b3fa7881001a8a6390a714 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:32:56 -0400 Subject: [PATCH 02/17] More logging + the fix, I think --- src/utils/addon-api.ts | 12 +++++++++- src/utils/layout-helpers.ts | 44 +++++++++++++++++++++++++++++++++++++ src/utils/logger.ts | 18 +++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/utils/addon-api.ts b/src/utils/addon-api.ts index d3d4007df..7c818b6a1 100644 --- a/src/utils/addon-api.ts +++ b/src/utils/addon-api.ts @@ -11,7 +11,7 @@ import { } from './layout-helpers'; import { TextDocument } from 'vscode-languageserver-textdocument'; import * as path from 'path'; -import { log, logInfo, logError, safeStringify } from './logger'; +import { log, logInfo, logError, safeStringify, instrumentTime } from './logger'; import Server from '../server'; import ASTPath from './../glimmer-utils'; import DAGMap from 'dag-map'; @@ -188,7 +188,13 @@ function requireUncached(module: string) { } export async function collectProjectProviders(root: string, addons: string[]): Promise { + const time = instrumentTime(`collectProjectProviders(${root})`); + + time.log(`Starting`); const [projectAddonsRoots, projectInRepoAddonsRoots] = await Promise.all([getProjectAddonsRoots(root), getProjectInRepoAddonsRoots(root)]); + + time.log(`found roots`); + const roots = addons .concat([root]) .concat(projectAddonsRoots, projectInRepoAddonsRoots) @@ -226,6 +232,8 @@ export async function collectProjectProviders(root: string, addons: string[]): P } } + time.log(`found ELS addons`); + const result: { definitionProviders: DefinitionResolveFunction[]; referencesProviders: ReferenceResolveFunction[]; @@ -332,6 +340,8 @@ export async function collectProjectProviders(root: string, addons: string[]): P } }); + time.log(`finished crawling dagMap`); + return result; } diff --git a/src/utils/layout-helpers.ts b/src/utils/layout-helpers.ts index 6118733e7..29122550d 100644 --- a/src/utils/layout-helpers.ts +++ b/src/utils/layout-helpers.ts @@ -6,9 +6,14 @@ import { clean, coerce, valid } from 'semver'; import { BaseProject } from '../base-project'; import { fsProvider } from '../fs-provider'; import walkAsync from './walk-async'; +import { instrumentTime } from './logger'; // const GLOBAL_REGISTRY = ['primitive-name'][['relatedFiles']]; +// Don't traverse dependencies we've already seen. +// correct package graph can sort of throw us in to cycles if we don't keep track of this. +const SEEN = new Set(); + export const ADDON_CONFIG_KEY = 'ember-language-server'; export async function asyncFilter(arr: T[], predicate: (value: unknown) => Promise): Promise { @@ -196,6 +201,16 @@ export function cached(_proto: unknown, prop: string, desc: PropertyDescriptor) async function getRecursiveInRepoAddonRoots(root: string, roots: string[]) { const packageData = await asyncGetPackageJSON(root); + + // names are required for packages + if (!packageData.name) return []; + + if (SEEN.has(packageData.name)) { + return []; + } + + SEEN.add(packageData.name); + const emberAddonPaths: string[] = (packageData['ember-addon'] && packageData['ember-addon'].paths) || []; if (roots.length) { @@ -213,6 +228,15 @@ async function getRecursiveInRepoAddonRoots(root: string, roots: string[]) { for (const validRoot of validPaths) { const packInfo = await asyncGetPackageJSON(validRoot); + // names are required for packages + if (!packInfo.name) continue; + + if (SEEN.has(packInfo.name)) { + continue; + } + + SEEN.add(packInfo.name); + // we don't need to go deeper if package itself not an ember-addon or els-extension if (!isEmberAddon(packInfo) && !hasEmberLanguageServerExtension(packInfo)) { continue; @@ -234,8 +258,12 @@ async function getRecursiveInRepoAddonRoots(root: string, roots: string[]) { } export async function getProjectInRepoAddonsRoots(root: string): Promise { + const time = instrumentTime(`getProjectInRepoAddonsRoots(${root})`); + const roots: string[] = await getRecursiveInRepoAddonRoots(root, []); + time.log(`finished getRecursiveInRepoAddonRoots`); + return Array.from(new Set(roots)); } @@ -274,8 +302,16 @@ export async function isGlimmerXProject(root: string) { } export async function getProjectAddonsRoots(root: string, resolvedItems: string[] = [], packageFolderName = 'node_modules') { + const time = instrumentTime(`getProjectInRepoAddonsRoots(${root})`); + const pack = await asyncGetPackageJSON(root); + if (!pack.name) return []; + + if (SEEN.has(pack.name)) return []; + + SEEN.add(pack.name); + if (resolvedItems.length) { if (!isEmberAddon(pack)) { return []; @@ -304,6 +340,12 @@ export async function getProjectAddonsRoots(root: string, resolvedItems: string[ for (const rootItem of roots) { const packInfo = packages[roots.indexOf(rootItem)]; + if (!packInfo.name) continue; + + if (SEEN.has(packInfo.name)) continue; + + SEEN.add(packInfo.name); + // we don't need to go deeper if package itself not an ember-addon or els-extension if (!isEmberAddon(packInfo) && !hasEmberLanguageServerExtension(packInfo)) { continue; @@ -321,6 +363,8 @@ export async function getProjectAddonsRoots(root: string, resolvedItems: string[ } } + time.log(`Finished looping over ${roots.length} roots`); + return recursiveRoots; } diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 26d9bd223..5274a7459 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -35,6 +35,24 @@ export function logInfo(str: string) { } } +export function instrumentTime(label: string) { + let last = Date.now(); + + return { + reset: () => { + last = Date.now(); + }, + log: (msg: string) => { + const now = Date.now(); + const diff = now - last; + + last = now; + + logInfo(`[${label}] +${diff}ms :: ${msg}`); + }, + }; +} + export function setConsole(item: RemoteConsole | null) { remoteConsole = item; } From 8981f2b1556b5203e6b2d466e506134f743604f9 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:04:25 -0400 Subject: [PATCH 03/17] Update server.ts --- src/server.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/server.ts b/src/server.ts index 31180d0f1..7d8110af2 100644 --- a/src/server.ts +++ b/src/server.ts @@ -192,10 +192,6 @@ export default class Server { if (this.lazyInit) { try { - /** - * Don't await this so that "format on save" is not blocked. - * We still need to run the initializers so that we have hinting in component files though. - */ await this.executeInitializers(); } catch (e) { logError(e); From 6e8aeadec50c55fd16a457a63c8608074780abb6 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:40:09 -0400 Subject: [PATCH 04/17] What weird style of TS-required import legacy nonsense is tihs --- src/utils/usages-api.ts | 2 +- test/glimmer-utils-test.ts | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/utils/usages-api.ts b/src/utils/usages-api.ts index 83a930ee3..a092ce754 100644 --- a/src/utils/usages-api.ts +++ b/src/utils/usages-api.ts @@ -107,7 +107,7 @@ export function findRelatedFiles(token: string, tokenType: MatchResultType = 'co const tokenQueue: [UsageType, string, string][] = []; -let extractionTimeout: NodeJS.Timeout; +let extractionTimeout: NodeJS.Timeout | number; function scheduleTokensExtraction(kind: UsageType, normalizedName: string, file: string) { tokenQueue.push([kind, normalizedName, file]); diff --git a/test/glimmer-utils-test.ts b/test/glimmer-utils-test.ts index c9a78e873..e3c41743e 100644 --- a/test/glimmer-utils-test.ts +++ b/test/glimmer-utils-test.ts @@ -1,5 +1,6 @@ import { Position } from 'vscode-languageserver'; import { ASTv1, preprocess } from '@glimmer/syntax'; +import * as assert from 'node:assert'; import ASTPath, { getLocalScope, maybeComponentNameForPath, sourceForNode, focusedBlockParamName, maybeBlockParamDefinition } from '../src/glimmer-utils'; import { toPosition } from '../src/estree-utils'; @@ -16,7 +17,7 @@ describe('glimmer-utils', function () { `; const astPath = ASTPath.toPosition(preprocess(input), toPosition(Position.create(3, 5))); - expect(astPath.node).toMatchSnapshot(); + expect(astPath?.node).toMatchSnapshot(); }); }); describe('getLocalScope', function () { @@ -30,6 +31,8 @@ describe('glimmer-utils', function () { `; const astPath = ASTPath.toPosition(preprocess(input), toPosition(Position.create(3, 5))); + assert(astPath, `ASTPath not found in test`); + expect(getLocalScope(astPath).map(({ name, index }) => [name, index])).toEqual([ ['item', 0], ['bar', 1], @@ -48,6 +51,8 @@ describe('glimmer-utils', function () { `; const astPath = ASTPath.toPosition(preprocess(input), toPosition(Position.create(3, 5))); + assert(astPath, `ASTPath not found in test`); + expect(maybeComponentNameForPath(astPath)).toEqual('Component'); }); }); @@ -62,6 +67,8 @@ describe('glimmer-utils', function () { `; const astPath = ASTPath.toPosition(preprocess(input), toPosition(Position.create(2, 2))); + assert(astPath, `ASTPath not found in test`); + expect((astPath.node as ASTv1.ElementNode).tag).toEqual('Component'); expect(sourceForNode(astPath.node, input)).toEqual(input.trim()); }); @@ -69,6 +76,8 @@ describe('glimmer-utils', function () { const input = ['', '{{#let items as |item bar|}}', '{{items}}', '{{/let}}', ''].join('\n'); const astPath = ASTPath.toPosition(preprocess(input), toPosition(Position.create(2, 3))); + assert(astPath, `ASTPath not found in test`); + expect((astPath.node as ASTv1.PathExpression).original).toEqual('items'); expect(sourceForNode(astPath.node, input)).toEqual('items'); }); @@ -79,6 +88,8 @@ describe('glimmer-utils', function () { const pos = toPosition(Position.create(1, 3)); const astPath = ASTPath.toPosition(preprocess(input), pos); + assert(astPath, `ASTPath not found in test`); + expect(maybeBlockParamDefinition(astPath, input, pos)).toEqual(undefined); }); it('able to handle single param', function () { @@ -86,6 +97,8 @@ describe('glimmer-utils', function () { const pos = toPosition(Position.create(0, 16)); const astPath = ASTPath.toPosition(preprocess(input), pos); + assert(astPath, `ASTPath not found in test`); + expect(maybeBlockParamDefinition(astPath, input, pos)).toMatchSnapshot(); }); it('able to handle single fiew params', function () { @@ -93,6 +106,8 @@ describe('glimmer-utils', function () { const pos = toPosition(Position.create(0, 22)); const astPath = ASTPath.toPosition(preprocess(input), pos); + assert(astPath, `ASTPath not found in test`); + expect(maybeBlockParamDefinition(astPath, input, pos)).toMatchSnapshot(); }); }); From f64b1c9099b63208644e3dd81fda44315277e727 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:32:02 -0400 Subject: [PATCH 05/17] Use seen-per-project --- .../core/script-completion-provider.ts | 8 ++-- .../core/script-definition-provider.ts | 16 ++++---- .../core/template-completion-provider.ts | 12 +++--- src/project.ts | 6 ++- src/server.ts | 2 +- src/utils/addon-api.ts | 7 +++- src/utils/definition-helpers.ts | 11 ++++-- src/utils/layout-helpers.ts | 39 +++++++++---------- 8 files changed, 56 insertions(+), 45 deletions(-) diff --git a/src/builtin-addons/core/script-completion-provider.ts b/src/builtin-addons/core/script-completion-provider.ts index 657be868a..441a9ab59 100644 --- a/src/builtin-addons/core/script-completion-provider.ts +++ b/src/builtin-addons/core/script-completion-provider.ts @@ -84,7 +84,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root); + await mGetProjectAddonsInfo(root, this.project.seenDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -107,7 +107,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root); + await mGetProjectAddonsInfo(root, this.project.seenDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -130,7 +130,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root); + await mGetProjectAddonsInfo(root, this.project.seenDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -185,7 +185,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root); + await mGetProjectAddonsInfo(root, this.project.seenDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } diff --git a/src/builtin-addons/core/script-definition-provider.ts b/src/builtin-addons/core/script-definition-provider.ts index 295b901a5..e518dafba 100644 --- a/src/builtin-addons/core/script-definition-provider.ts +++ b/src/builtin-addons/core/script-definition-provider.ts @@ -29,6 +29,7 @@ type ItemType = 'Model' | 'Transform' | 'Service'; // eslint-disable-line type LayoutCollectorFn = (root: string, itemName: string, podModulePrefix?: string) => string[]; type AsyncLayoutCollectorFn = (root: string, itemName: string, podModulePrefix?: string) => Promise; +type ProjectAwareCollectionFn = (project: Project, itemName: string, podModulePrefix?: string) => Promise; function joinPaths(...args: string[]) { return ['.ts', '.js'].map((extName: string) => { @@ -40,7 +41,8 @@ function joinPaths(...args: string[]) { } class PathResolvers { - [key: string]: LayoutCollectorFn | AsyncLayoutCollectorFn; + [key: string]: LayoutCollectorFn | AsyncLayoutCollectorFn | ProjectAwareCollectionFn; + muModelPaths(root: string, modelName: string) { return joinPaths(root, 'src', 'data', 'models', modelName, 'model'); } @@ -68,11 +70,11 @@ class PathResolvers { podServicePaths(root: string, modelName: string, podPrefix: string) { return joinPaths(root, 'app', podPrefix, modelName, 'service'); } - async addonServicePaths(root: string, serviceName: string): Promise { - return await getAddonPathsForType(root, 'services', serviceName); + async addonServicePaths(project: Project, serviceName: string): Promise { + return await getAddonPathsForType(project, 'services', serviceName); } - async addonImportPaths(root: string, pathName: string) { - return await getAddonImport(root, pathName); + async addonImportPaths(project: Project, pathName: string) { + return await getAddonImport(project, pathName); } classicImportPaths(root: string, pathName: string) { const pathParts = pathName.split('/'); @@ -123,7 +125,7 @@ export default class CoreScriptDefinitionProvider { guessedPaths.push(pathLocation); }); - const addonImports = await this.resolvers.addonImportPaths(root, importPath); + const addonImports = await this.resolvers.addonImportPaths(this.project, importPath); addonImports.forEach((pathLocation: string) => { guessedPaths.push(pathLocation); @@ -148,7 +150,7 @@ export default class CoreScriptDefinitionProvider { } if (fnName === 'Service') { - const paths = await this.resolvers.addonServicePaths(root, typeName); + const paths = await this.resolvers.addonServicePaths(this.project, typeName); paths.forEach((item: string) => { guessedPaths.push(item); diff --git a/src/builtin-addons/core/template-completion-provider.ts b/src/builtin-addons/core/template-completion-provider.ts index 9231078a7..e289f63c3 100644 --- a/src/builtin-addons/core/template-completion-provider.ts +++ b/src/builtin-addons/core/template-completion-provider.ts @@ -178,7 +178,7 @@ export default class TemplateCompletionProvider { await mListComponents(project); this.enableRegistryCache('componentsRegistryInitialized'); - await mGetProjectAddonsInfo(project.root); + await mGetProjectAddonsInfo(project.root, project.seenDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); @@ -195,7 +195,7 @@ export default class TemplateCompletionProvider { const items: CompletionItem[] = []; if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root); + await mGetProjectAddonsInfo(root, this.project.seenDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -256,7 +256,7 @@ export default class TemplateCompletionProvider { } async getMustachePathCandidates(root: string) { if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root); + await mGetProjectAddonsInfo(root, this.project.seenDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -305,7 +305,7 @@ export default class TemplateCompletionProvider { } async getBlockPathCandidates(root: string): Promise { if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root); + await mGetProjectAddonsInfo(root, this.project.seenDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -340,7 +340,7 @@ export default class TemplateCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(this.project.root); + await mGetProjectAddonsInfo(this.project.root, this.project.seenDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -447,7 +447,7 @@ export default class TemplateCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root); + await mGetProjectAddonsInfo(root, this.project.seenDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } diff --git a/src/project.ts b/src/project.ts index 04221b713..e8a48aa30 100644 --- a/src/project.ts +++ b/src/project.ts @@ -35,6 +35,10 @@ export interface Executors { } export class Project extends BaseProject { + // Don't traverse dependencies we've already seen. + // correct package graph can sort of throw us in to cycles if we don't keep track of this. + seenDependencies = new Set(); + providers!: ProjectProviders; builtinProviders!: ProjectProviders; addonsMeta: AddonMeta[] = []; @@ -148,7 +152,7 @@ export class Project extends BaseProject { this.providers = emptyProjectProviders(); this.flags.enableEagerRegistryInitialization = false; } else if (server.options.type === 'node') { - this.providers = await collectProjectProviders(this.root, this.addons); + this.providers = await collectProjectProviders(this.root, this.addons, this.seenDependencies); } else { throw new Error(`Unknown server type: "${server.options.type}"`); } diff --git a/src/server.ts b/src/server.ts index 7d8110af2..953ae904c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -218,7 +218,7 @@ export default class Server { }; } - await mGetProjectAddonsInfo(project.root); + await mGetProjectAddonsInfo(project.root, project.seenDependencies); project.invalidateRegistry(); return { diff --git a/src/utils/addon-api.ts b/src/utils/addon-api.ts index 7c818b6a1..917ff4d7c 100644 --- a/src/utils/addon-api.ts +++ b/src/utils/addon-api.ts @@ -187,11 +187,14 @@ function requireUncached(module: string) { return result; } -export async function collectProjectProviders(root: string, addons: string[]): Promise { +export async function collectProjectProviders(root: string, addons: string[], seenDependencies: Set): Promise { const time = instrumentTime(`collectProjectProviders(${root})`); time.log(`Starting`); - const [projectAddonsRoots, projectInRepoAddonsRoots] = await Promise.all([getProjectAddonsRoots(root), getProjectInRepoAddonsRoots(root)]); + const [projectAddonsRoots, projectInRepoAddonsRoots] = await Promise.all([ + getProjectAddonsRoots(root, seenDependencies), + getProjectInRepoAddonsRoots(root, seenDependencies), + ]); time.log(`found roots`); diff --git a/src/utils/definition-helpers.ts b/src/utils/definition-helpers.ts index 22f52ac05..8812b0960 100644 --- a/src/utils/definition-helpers.ts +++ b/src/utils/definition-helpers.ts @@ -6,6 +6,7 @@ import { URI } from 'vscode-uri'; import { podModulePrefixForRoot, hasAddonFolderInPath, getProjectAddonsRoots, getProjectInRepoAddonsRoots, asyncFilter } from './layout-helpers'; import { fsProvider } from '../fs-provider'; +import { Project } from '..'; const mProjectAddonsRoots = memoize(getProjectAddonsRoots, { length: 3, @@ -166,7 +167,8 @@ export function getPathsForComponentTemplates(root: string, maybeComponentName: return paths; } -export async function getAddonImport(root: string, importPath: string) { +export async function getAddonImport(project: Project, importPath: string) { + const { root, seenDependencies } = project; const importParts = importPath.split('/'); let addonName = importParts.shift(); @@ -179,7 +181,7 @@ export async function getAddonImport(root: string, importPath: string) { } const items: string[] = []; - const [addonRoots, inRepoRoots] = await Promise.all([mProjectAddonsRoots(root), mProjectInRepoAddonsRoots(root)]); + const [addonRoots, inRepoRoots] = await Promise.all([mProjectAddonsRoots(root, seenDependencies), mProjectInRepoAddonsRoots(root, seenDependencies)]); const roots = items.concat(addonRoots, inRepoRoots); let existingPaths: string[] = []; @@ -228,9 +230,10 @@ export async function getAddonImport(root: string, importPath: string) { return existingPaths; } -export async function getAddonPathsForType(root: string, collection: 'services' | 'models' | 'modifiers' | 'helpers' | 'routes', name: string) { +export async function getAddonPathsForType(project: Project, collection: 'services' | 'models' | 'modifiers' | 'helpers' | 'routes', name: string) { + const { root, seenDependencies } = project; const items: string[] = []; - const [addonRoots, inRepoRoots] = await Promise.all([mProjectAddonsRoots(root), mProjectInRepoAddonsRoots(root)]); + const [addonRoots, inRepoRoots] = await Promise.all([mProjectAddonsRoots(root, seenDependencies), mProjectInRepoAddonsRoots(root, seenDependencies)]); const roots = items.concat(addonRoots, inRepoRoots); let existingPaths: string[] = []; let hasValidPath = false; diff --git a/src/utils/layout-helpers.ts b/src/utils/layout-helpers.ts index 29122550d..e61aded70 100644 --- a/src/utils/layout-helpers.ts +++ b/src/utils/layout-helpers.ts @@ -10,10 +10,6 @@ import { instrumentTime } from './logger'; // const GLOBAL_REGISTRY = ['primitive-name'][['relatedFiles']]; -// Don't traverse dependencies we've already seen. -// correct package graph can sort of throw us in to cycles if we don't keep track of this. -const SEEN = new Set(); - export const ADDON_CONFIG_KEY = 'ember-language-server'; export async function asyncFilter(arr: T[], predicate: (value: unknown) => Promise): Promise { @@ -199,17 +195,17 @@ export function cached(_proto: unknown, prop: string, desc: PropertyDescriptor) }; } -async function getRecursiveInRepoAddonRoots(root: string, roots: string[]) { +async function getRecursiveInRepoAddonRoots(root: string, seenDependencies: Set, roots: string[]) { const packageData = await asyncGetPackageJSON(root); // names are required for packages if (!packageData.name) return []; - if (SEEN.has(packageData.name)) { + if (seenDependencies.has(packageData.name)) { return []; } - SEEN.add(packageData.name); + seenDependencies.add(packageData.name); const emberAddonPaths: string[] = (packageData['ember-addon'] && packageData['ember-addon'].paths) || []; @@ -231,11 +227,11 @@ async function getRecursiveInRepoAddonRoots(root: string, roots: string[]) { // names are required for packages if (!packInfo.name) continue; - if (SEEN.has(packInfo.name)) { + if (seenDependencies.has(packInfo.name)) { continue; } - SEEN.add(packInfo.name); + seenDependencies.add(packInfo.name); // we don't need to go deeper if package itself not an ember-addon or els-extension if (!isEmberAddon(packInfo) && !hasEmberLanguageServerExtension(packInfo)) { @@ -244,7 +240,7 @@ async function getRecursiveInRepoAddonRoots(root: string, roots: string[]) { if (!recursiveRoots.includes(validRoot)) { recursiveRoots.push(validRoot); - const items = await getRecursiveInRepoAddonRoots(validRoot, recursiveRoots); + const items = await getRecursiveInRepoAddonRoots(validRoot, seenDependencies, recursiveRoots); items.forEach((relatedRoot: string) => { if (!recursiveRoots.includes(relatedRoot)) { @@ -257,10 +253,10 @@ async function getRecursiveInRepoAddonRoots(root: string, roots: string[]) { return recursiveRoots.sort(); } -export async function getProjectInRepoAddonsRoots(root: string): Promise { +export async function getProjectInRepoAddonsRoots(root: string, seenDependencies: Set): Promise { const time = instrumentTime(`getProjectInRepoAddonsRoots(${root})`); - const roots: string[] = await getRecursiveInRepoAddonRoots(root, []); + const roots: string[] = await getRecursiveInRepoAddonRoots(root, seenDependencies, []); time.log(`finished getRecursiveInRepoAddonRoots`); @@ -301,16 +297,16 @@ export async function isGlimmerXProject(root: string) { return hasDep(pack, '@glimmerx/core') || hasDep(pack, 'glimmer-lite-core'); } -export async function getProjectAddonsRoots(root: string, resolvedItems: string[] = [], packageFolderName = 'node_modules') { +export async function getProjectAddonsRoots(root: string, seenDependencies: Set, resolvedItems: string[] = [], packageFolderName = 'node_modules') { const time = instrumentTime(`getProjectInRepoAddonsRoots(${root})`); const pack = await asyncGetPackageJSON(root); if (!pack.name) return []; - if (SEEN.has(pack.name)) return []; + if (seenDependencies.has(pack.name)) return []; - SEEN.add(pack.name); + seenDependencies.add(pack.name); if (resolvedItems.length) { if (!isEmberAddon(pack)) { @@ -342,9 +338,9 @@ export async function getProjectAddonsRoots(root: string, resolvedItems: string[ if (!packInfo.name) continue; - if (SEEN.has(packInfo.name)) continue; + if (seenDependencies.has(packInfo.name)) continue; - SEEN.add(packInfo.name); + seenDependencies.add(packInfo.name); // we don't need to go deeper if package itself not an ember-addon or els-extension if (!isEmberAddon(packInfo) && !hasEmberLanguageServerExtension(packInfo)) { @@ -353,7 +349,7 @@ export async function getProjectAddonsRoots(root: string, resolvedItems: string[ if (!recursiveRoots.includes(rootItem)) { recursiveRoots.push(rootItem); - const addonRoots = await getProjectAddonsRoots(rootItem, recursiveRoots, packageFolderName); + const addonRoots = await getProjectAddonsRoots(rootItem, seenDependencies, recursiveRoots, packageFolderName); addonRoots.forEach((item: string) => { if (!recursiveRoots.includes(item)) { @@ -440,8 +436,11 @@ export function hasAddonFolderInPath(name: string) { return name.includes(path.sep + 'addon' + path.sep) || name.includes(path.sep + 'addon-test-support' + path.sep); } -export async function getProjectAddonsInfo(root: string): Promise { - const [projectAddonsRoots, projectInRepoAddonsRoots] = await Promise.all([getProjectAddonsRoots(root), getProjectInRepoAddonsRoots(root)]); +export async function getProjectAddonsInfo(root: string, seenDependencies: Set): Promise { + const [projectAddonsRoots, projectInRepoAddonsRoots] = await Promise.all([ + getProjectAddonsRoots(root, seenDependencies), + getProjectInRepoAddonsRoots(root, seenDependencies), + ]); const roots = ([] as string[]).concat(projectAddonsRoots, projectInRepoAddonsRoots).filter((pathItem: unknown) => typeof pathItem === 'string'); for (const packagePath of roots) { From 037db3fba2a0b3d6151ee6b3c2329401eef3155a Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 1 Nov 2024 18:19:06 -0400 Subject: [PATCH 06/17] Does CI match my local env? --- ...man-fixture-based-integration-test.ts.snap | 102 +-- .../fixture-based-integration-test.ts.snap | 74 +- .../go-to-definition-integration-test.ts.snap | 96 +-- test/__snapshots__/integration-test.ts.snap | 709 ++---------------- 4 files changed, 78 insertions(+), 903 deletions(-) diff --git a/test/__snapshots__/batman-fixture-based-integration-test.ts.snap b/test/__snapshots__/batman-fixture-based-integration-test.ts.snap index a59965696..612341999 100644 --- a/test/__snapshots__/batman-fixture-based-integration-test.ts.snap +++ b/test/__snapshots__/batman-fixture-based-integration-test.ts.snap @@ -72,107 +72,11 @@ Array [ }, }, }, - Object { - "data": Object { - "files": Array [ - "lib/boo/addon/templates/components/bar.hbs", - "lib/foo/addon/components/bar.js", - "lib/foo/addon/templates/components/bar.hbs", - ], - }, - "detail": "component", - "kind": 7, - "label": "Boo$Bar", - "textEdit": Object { - "newText": "Boo$Bar", - "range": Object { - "end": Object { - "character": 1, - "line": 0, - }, - "start": Object { - "character": 1, - "line": 0, - }, - }, - }, - }, - Object { - "detail": "component", - "kind": 7, - "label": "Foo$Bar", - "textEdit": Object { - "newText": "Foo$Bar", - "range": Object { - "end": Object { - "character": 1, - "line": 0, - }, - "start": Object { - "character": 1, - "line": 0, - }, - }, - }, - }, ] `; -exports[`With \`batman project\` initialized on server Completion request returns all angle-bracket in a element expression for in repo addons with batman syntax 1`] = ` -Array [ - Object { - "detail": "component", - "kind": 7, - "label": "Foo$Bar", - "textEdit": Object { - "newText": "Foo$Bar", - "range": Object { - "end": Object { - "character": 2, - "line": 1, - }, - "start": Object { - "character": 1, - "line": 1, - }, - }, - }, - }, -] -`; +exports[`With \`batman project\` initialized on server Completion request returns all angle-bracket in a element expression for in repo addons with batman syntax 1`] = `Array []`; -exports[`With \`batman project\` initialized on server Definition request return proper component location from batman syntax component name 1`] = ` -Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/lib/boo/addon/templates/components/bar.hbs", - }, -] -`; +exports[`With \`batman project\` initialized on server Definition request return proper component location from batman syntax component name 1`] = `Array []`; -exports[`With \`batman project\` initialized on server return proper import location from in-repo addon 1`] = ` -Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/lib/foo/addon/components/bar.js", - }, -] -`; +exports[`With \`batman project\` initialized on server return proper import location from in-repo addon 1`] = `Array []`; diff --git a/test/__snapshots__/fixture-based-integration-test.ts.snap b/test/__snapshots__/fixture-based-integration-test.ts.snap index 68085d75b..59c0042de 100644 --- a/test/__snapshots__/fixture-based-integration-test.ts.snap +++ b/test/__snapshots__/fixture-based-integration-test.ts.snap @@ -49,59 +49,10 @@ Array [ }, }, }, - Object { - "data": Object { - "files": Array [ - "lib/foo/addon/templates/components/bar.hbs", - ], - }, - "detail": "component", - "kind": 7, - "label": "Bar", - "textEdit": Object { - "newText": "Bar", - "range": Object { - "end": Object { - "character": 2, - "line": 1, - }, - "start": Object { - "character": 1, - "line": 1, - }, - }, - }, - }, ] `; -exports[`With \`full-project\` initialized on server Completion request returns all angle-bracket in a element expression for in repo addons without batman syntax 1`] = ` -Array [ - Object { - "data": Object { - "files": Array [ - "lib/foo/addon/templates/components/bar.hbs", - ], - }, - "detail": "component", - "kind": 7, - "label": "Bar", - "textEdit": Object { - "newText": "Bar", - "range": Object { - "end": Object { - "character": 2, - "line": 1, - }, - "start": Object { - "character": 1, - "line": 1, - }, - }, - }, - }, -] -`; +exports[`With \`full-project\` initialized on server Completion request returns all angle-bracket in a element expression for in repo addons without batman syntax 1`] = `Array []`; exports[`With \`full-project\` initialized on server Completion request returns all components and helpers when requesting completion items in a handlebars expression 1`] = ` Array [ @@ -175,29 +126,6 @@ Array [ }, }, }, - Object { - "data": Object { - "files": Array [ - "lib/foo/addon/templates/components/bar.hbs", - ], - }, - "detail": "component", - "kind": 7, - "label": "bar", - "textEdit": Object { - "newText": "bar", - "range": Object { - "end": Object { - "character": 2, - "line": 1, - }, - "start": Object { - "character": 2, - "line": 1, - }, - }, - }, - }, Object { "data": Object { "files": Array [ diff --git a/test/__snapshots__/go-to-definition-integration-test.ts.snap b/test/__snapshots__/go-to-definition-integration-test.ts.snap index 46034418a..f5aed2af8 100644 --- a/test/__snapshots__/go-to-definition-integration-test.ts.snap +++ b/test/__snapshots__/go-to-definition-integration-test.ts.snap @@ -2,13 +2,7 @@ exports[`script files named import definition resolution addon case default import from addon 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "my-addon", - "root": "node_modules/my-addon", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -16,33 +10,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 14, - "line": 1, - }, - "start": Object { - "character": 7, - "line": 1, - }, - }, - "uri": "/node_modules/my-addon/addon/utils/hello.js", - }, - ], + "response": Array [], } `; exports[`script files named import definition resolution addon case named import from addon 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "my-addon", - "root": "node_modules/my-addon", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -50,33 +24,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 17, - "line": 2, - }, - "start": Object { - "character": 13, - "line": 2, - }, - }, - "uri": "/node_modules/my-addon/addon/utils/hello.js", - }, - ], + "response": Array [], } `; exports[`script files named import definition resolution addon case not existing default import from addon 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "my-addon", - "root": "node_modules/my-addon", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -84,33 +38,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/node_modules/my-addon/addon/utils/hello.js", - }, - ], + "response": Array [], } `; exports[`script files named import definition resolution addon case unknown named import from addon 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "my-addon", - "root": "node_modules/my-addon", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -118,21 +52,7 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/node_modules/my-addon/addon/utils/hello.js", - }, - ], + "response": Array [], } `; diff --git a/test/__snapshots__/integration-test.ts.snap b/test/__snapshots__/integration-test.ts.snap index 7758c2103..f3d204284 100644 --- a/test/__snapshots__/integration-test.ts.snap +++ b/test/__snapshots__/integration-test.ts.snap @@ -2,28 +2,7 @@ exports[`integration async fs enabled: false API:Chain support addon ordering 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "addon1", - "root": "node_modules/addon1", - "version": null, - }, - Object { - "name": "addon2", - "root": "node_modules/addon2", - "version": null, - }, - Object { - "name": "addon3", - "root": "node_modules/addon3", - "version": null, - }, - Object { - "name": "addon4", - "root": "node_modules/addon4", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "dory": Array [ @@ -35,9 +14,9 @@ Object { Object { "detail": "a = undefined", "kind": 10, - "label": "this.a_addon2__addon1__addon4__addon3_", + "label": "this.a", "textEdit": Object { - "newText": "this.a_addon2__addon1__addon4__addon3_", + "newText": "this.a", "range": Object { "end": Object { "character": 8, @@ -87,13 +66,7 @@ Object { exports[`integration async fs enabled: false Able to provide API:Completion support dummy addon completion:script 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -101,23 +74,13 @@ Object { ], }, }, - "response": Array [ - Object { - "label": "name", - }, - ], + "response": Array [], } `; exports[`integration async fs enabled: false Able to provide API:Completion support dummy addon completion:template 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -127,9 +90,11 @@ Object { }, "response": Array [ Object { - "label": "this.name", + "detail": "n = undefined", + "kind": 10, + "label": "this.n", "textEdit": Object { - "newText": "this.name", + "newText": "this.n", "range": Object { "end": Object { "character": 8, @@ -148,13 +113,7 @@ Object { exports[`integration async fs enabled: false Able to provide API:Definition support dummy addon definition:script 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -162,33 +121,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/components/hello/index.js", - }, - ], + "response": Array [], } `; exports[`integration async fs enabled: false Able to provide API:Definition support dummy addon definition:template 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -196,33 +135,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/components/hello/index.hbs", - }, - ], + "response": Array [], } `; exports[`integration async fs enabled: false Able to provide API:Hover support dummy addon hover:template 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -230,31 +149,13 @@ Object { ], }, }, - "response": Object { - "contents": "foo", - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - }, + "response": null, } `; exports[`integration async fs enabled: false Able to provide API:Reference support dummy addon reference:template 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -262,21 +163,7 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/components/hello/index.hbs", - }, - ], + "response": Array [], } `; @@ -463,52 +350,15 @@ This allows you to progressively add CSS classes to your components, and makes t exports[`integration async fs enabled: false Able to provide autocomplete information for local context access support child project addon calling parent project addon 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "biz", - "root": "lib/biz", - "version": 1, - }, - Object { - "name": "foo", - "root": "../lib/foo", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { - "component": Object { - "bar": Array [ - "lib/biz/addon/templates/components/bar.hbs", - "../lib/foo/addon/templates/components/bar.hbs", - "../lib/foo/app/components/bar.js", - ], - }, "routePath": Object { "hello": Array [ "app/templates/hello.hbs", ], }, }, - "response": Array [ - Object { - "detail": "component", - "kind": 7, - "label": "Foo$Bar", - "textEdit": Object { - "newText": "Foo$Bar", - "range": Object { - "end": Object { - "character": 2, - "line": 0, - }, - "start": Object { - "character": 1, - "line": 0, - }, - }, - }, - }, - ], + "response": Array [], } `; @@ -1444,13 +1294,7 @@ Object { exports[`integration async fs enabled: false Able to use classes for API support dummy class-based addon definition:template 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -1458,33 +1302,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/components/hello/index.hbs", - }, - ], + "response": Array [], } `; exports[`integration async fs enabled: false Able to use classes for API support dummy class-based addon definition:template with correctly binded context 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -1492,21 +1316,7 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/components/hello/index.hbs", - }, - ], + "response": Array [], } `; @@ -3086,18 +2896,9 @@ Object { exports[`integration async fs enabled: false Go to definition works for all supported cases go to definition from app to in repo addon 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "biz", - "root": "lib/biz", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { - "bar": Array [ - "lib/biz/addon/components/bar.js", - ], "darling": Array [ "app/components/darling/index.js", ], @@ -3106,33 +2907,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/lib/biz/addon/components/bar.js", - }, - ], + "response": Array [], } `; exports[`integration async fs enabled: false Go to definition works for all supported cases go to definition from app to nested utils location of in repo addon 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "biz", - "root": "lib/biz", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "darling": Array [ @@ -3143,21 +2924,7 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/lib/biz/addon/utils/boo/blah/zoo/bar.js", - }, - ], + "response": Array [], } `; @@ -3517,25 +3284,8 @@ Object { exports[`integration async fs enabled: false Project class resolution, based on fs path and file structure support parent project addon calling child project 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "biz", - "root": "lib/biz", - "version": 1, - }, - Object { - "name": "foo", - "root": "../lib/foo", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { - "component": Object { - "bar": Array [ - "lib/biz/addon/components/bar.js", - "../lib/foo/addon/components/bar.js", - ], - }, "helper": Object { "blah": Array [ "tests/helpers/blah.js", @@ -3547,21 +3297,7 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/tests/helpers/blah.js", - }, - ], + "response": null, } `; @@ -3580,7 +3316,7 @@ Array [ Object { "data": Object { "files": Array [ - "../addon/components/item.hbs", + "../../../../../../../var/folders/wk/w99lck4x7_5930c7gj65r3s40000gp/T/tmp-26763GMELN7q3I1hZ/lib/addon/components/item.hbs", ], }, "detail": "component", @@ -3603,13 +3339,7 @@ Array [ ], }, Object { - "addonsMeta": Array [ - Object { - "name": "my-addon", - "root": "../lib", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "bar": Array [ @@ -3618,16 +3348,13 @@ Array [ "foo": Array [ "app/components/foo.hbs", ], - "item": Array [ - "../lib/addon/components/item.hbs", - ], }, }, "response": Array [ Object { "data": Object { "files": Array [ - "../addon/components/item.hbs", + "../../../../../../../var/folders/wk/w99lck4x7_5930c7gj65r3s40000gp/T/tmp-26763GMELN7q3I1hZ/lib/addon/components/item.hbs", ], }, "detail": "component", @@ -3928,28 +3655,7 @@ Array [ exports[`integration async fs enabled: true API:Chain support addon ordering 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "addon1", - "root": "node_modules/addon1", - "version": null, - }, - Object { - "name": "addon2", - "root": "node_modules/addon2", - "version": null, - }, - Object { - "name": "addon3", - "root": "node_modules/addon3", - "version": null, - }, - Object { - "name": "addon4", - "root": "node_modules/addon4", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "dory": Array [ @@ -3961,9 +3667,9 @@ Object { Object { "detail": "a = undefined", "kind": 10, - "label": "this.a_addon2__addon1__addon4__addon3_", + "label": "this.a", "textEdit": Object { - "newText": "this.a_addon2__addon1__addon4__addon3_", + "newText": "this.a", "range": Object { "end": Object { "character": 8, @@ -4013,13 +3719,7 @@ Object { exports[`integration async fs enabled: true Able to provide API:Completion support dummy addon completion:script 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -4027,23 +3727,13 @@ Object { ], }, }, - "response": Array [ - Object { - "label": "name", - }, - ], + "response": Array [], } `; exports[`integration async fs enabled: true Able to provide API:Completion support dummy addon completion:template 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -4053,9 +3743,11 @@ Object { }, "response": Array [ Object { - "label": "this.name", + "detail": "n = undefined", + "kind": 10, + "label": "this.n", "textEdit": Object { - "newText": "this.name", + "newText": "this.n", "range": Object { "end": Object { "character": 8, @@ -4074,13 +3766,7 @@ Object { exports[`integration async fs enabled: true Able to provide API:Definition support dummy addon definition:script 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -4088,33 +3774,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/components/hello/index.js", - }, - ], + "response": Array [], } `; exports[`integration async fs enabled: true Able to provide API:Definition support dummy addon definition:template 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -4122,33 +3788,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/components/hello/index.hbs", - }, - ], + "response": Array [], } `; exports[`integration async fs enabled: true Able to provide API:Hover support dummy addon hover:template 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -4156,31 +3802,13 @@ Object { ], }, }, - "response": Object { - "contents": "foo", - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - }, + "response": null, } `; exports[`integration async fs enabled: true Able to provide API:Reference support dummy addon reference:template 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -4188,21 +3816,7 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/components/hello/index.hbs", - }, - ], + "response": Array [], } `; @@ -4389,52 +4003,15 @@ This allows you to progressively add CSS classes to your components, and makes t exports[`integration async fs enabled: true Able to provide autocomplete information for local context access support child project addon calling parent project addon 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "biz", - "root": "lib/biz", - "version": 1, - }, - Object { - "name": "foo", - "root": "../lib/foo", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { - "component": Object { - "bar": Array [ - "lib/biz/addon/templates/components/bar.hbs", - "../lib/foo/addon/templates/components/bar.hbs", - "../lib/foo/app/components/bar.js", - ], - }, "routePath": Object { "hello": Array [ "app/templates/hello.hbs", ], }, }, - "response": Array [ - Object { - "detail": "component", - "kind": 7, - "label": "Foo$Bar", - "textEdit": Object { - "newText": "Foo$Bar", - "range": Object { - "end": Object { - "character": 2, - "line": 0, - }, - "start": Object { - "character": 1, - "line": 0, - }, - }, - }, - }, - ], + "response": Array [], } `; @@ -5370,13 +4947,7 @@ Object { exports[`integration async fs enabled: true Able to use classes for API support dummy class-based addon definition:template 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -5384,33 +4955,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/components/hello/index.hbs", - }, - ], + "response": Array [], } `; exports[`integration async fs enabled: true Able to use classes for API support dummy class-based addon definition:template with correctly binded context 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "provider", - "root": "node_modules/provider", - "version": null, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "hello": Array [ @@ -5418,21 +4969,7 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/components/hello/index.hbs", - }, - ], + "response": Array [], } `; @@ -7012,18 +6549,9 @@ Object { exports[`integration async fs enabled: true Go to definition works for all supported cases go to definition from app to in repo addon 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "biz", - "root": "lib/biz", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { - "bar": Array [ - "lib/biz/addon/components/bar.js", - ], "darling": Array [ "app/components/darling/index.js", ], @@ -7032,33 +6560,13 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/lib/biz/addon/components/bar.js", - }, - ], + "response": Array [], } `; exports[`integration async fs enabled: true Go to definition works for all supported cases go to definition from app to nested utils location of in repo addon 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "biz", - "root": "lib/biz", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "darling": Array [ @@ -7069,21 +6577,7 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/lib/biz/addon/utils/boo/blah/zoo/bar.js", - }, - ], + "response": Array [], } `; @@ -7339,37 +6833,6 @@ Object { } `; -exports[`integration async fs enabled: true Go to definition works for all supported cases to children route from application outlet 2`] = ` -Object { - "addonsMeta": Array [], - "registry": Object { - "routePath": Object { - "application": Array [ - "app/templates/application.hbs", - ], - "foo": Array [ - "app/templates/foo.hbs", - ], - }, - }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/templates/foo.hbs", - }, - ], -} -`; - exports[`integration async fs enabled: true Go to definition works for all supported cases to children route from meaningful outlet 1`] = ` Object { "addonsMeta": Array [], @@ -7474,25 +6937,8 @@ Object { exports[`integration async fs enabled: true Project class resolution, based on fs path and file structure support parent project addon calling child project 1`] = ` Object { - "addonsMeta": Array [ - Object { - "name": "biz", - "root": "lib/biz", - "version": 1, - }, - Object { - "name": "foo", - "root": "../lib/foo", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { - "component": Object { - "bar": Array [ - "lib/biz/addon/components/bar.js", - "../lib/foo/addon/components/bar.js", - ], - }, "helper": Object { "blah": Array [ "tests/helpers/blah.js", @@ -7504,21 +6950,7 @@ Object { ], }, }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/tests/helpers/blah.js", - }, - ], + "response": null, } `; @@ -7537,7 +6969,7 @@ Array [ Object { "data": Object { "files": Array [ - "../addon/components/item.hbs", + "../../../../../../../var/folders/wk/w99lck4x7_5930c7gj65r3s40000gp/T/tmp-26763vvD36al6vbcp/lib/addon/components/item.hbs", ], }, "detail": "component", @@ -7560,13 +6992,7 @@ Array [ ], }, Object { - "addonsMeta": Array [ - Object { - "name": "my-addon", - "root": "../lib", - "version": 1, - }, - ], + "addonsMeta": Array [], "registry": Object { "component": Object { "bar": Array [ @@ -7575,16 +7001,13 @@ Array [ "foo": Array [ "app/components/foo.hbs", ], - "item": Array [ - "../lib/addon/components/item.hbs", - ], }, }, "response": Array [ Object { "data": Object { "files": Array [ - "../addon/components/item.hbs", + "../../../../../../../var/folders/wk/w99lck4x7_5930c7gj65r3s40000gp/T/tmp-26763vvD36al6vbcp/lib/addon/components/item.hbs", ], }, "detail": "component", From f6dd52e9b1bf17cbb7893dfe23c8b9e367d7710e Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 1 Nov 2024 18:23:31 -0400 Subject: [PATCH 07/17] Add code-workspace file --- ember-language-server.code-workspace | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 ember-language-server.code-workspace diff --git a/ember-language-server.code-workspace b/ember-language-server.code-workspace new file mode 100644 index 000000000..1e5e53c65 --- /dev/null +++ b/ember-language-server.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "../vscode-ember" + } + ], + "settings": {} +} \ No newline at end of file From 10685e09bf3999e4990a7b79a9ee18d821cb40a0 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 1 Nov 2024 18:23:41 -0400 Subject: [PATCH 08/17] Revert "Does CI match my local env?" (it does) This reverts commit 037db3fba2a0b3d6151ee6b3c2329401eef3155a. --- ...man-fixture-based-integration-test.ts.snap | 102 ++- .../fixture-based-integration-test.ts.snap | 74 +- .../go-to-definition-integration-test.ts.snap | 96 ++- test/__snapshots__/integration-test.ts.snap | 709 ++++++++++++++++-- 4 files changed, 903 insertions(+), 78 deletions(-) diff --git a/test/__snapshots__/batman-fixture-based-integration-test.ts.snap b/test/__snapshots__/batman-fixture-based-integration-test.ts.snap index 612341999..a59965696 100644 --- a/test/__snapshots__/batman-fixture-based-integration-test.ts.snap +++ b/test/__snapshots__/batman-fixture-based-integration-test.ts.snap @@ -72,11 +72,107 @@ Array [ }, }, }, + Object { + "data": Object { + "files": Array [ + "lib/boo/addon/templates/components/bar.hbs", + "lib/foo/addon/components/bar.js", + "lib/foo/addon/templates/components/bar.hbs", + ], + }, + "detail": "component", + "kind": 7, + "label": "Boo$Bar", + "textEdit": Object { + "newText": "Boo$Bar", + "range": Object { + "end": Object { + "character": 1, + "line": 0, + }, + "start": Object { + "character": 1, + "line": 0, + }, + }, + }, + }, + Object { + "detail": "component", + "kind": 7, + "label": "Foo$Bar", + "textEdit": Object { + "newText": "Foo$Bar", + "range": Object { + "end": Object { + "character": 1, + "line": 0, + }, + "start": Object { + "character": 1, + "line": 0, + }, + }, + }, + }, ] `; -exports[`With \`batman project\` initialized on server Completion request returns all angle-bracket in a element expression for in repo addons with batman syntax 1`] = `Array []`; +exports[`With \`batman project\` initialized on server Completion request returns all angle-bracket in a element expression for in repo addons with batman syntax 1`] = ` +Array [ + Object { + "detail": "component", + "kind": 7, + "label": "Foo$Bar", + "textEdit": Object { + "newText": "Foo$Bar", + "range": Object { + "end": Object { + "character": 2, + "line": 1, + }, + "start": Object { + "character": 1, + "line": 1, + }, + }, + }, + }, +] +`; -exports[`With \`batman project\` initialized on server Definition request return proper component location from batman syntax component name 1`] = `Array []`; +exports[`With \`batman project\` initialized on server Definition request return proper component location from batman syntax component name 1`] = ` +Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/lib/boo/addon/templates/components/bar.hbs", + }, +] +`; -exports[`With \`batman project\` initialized on server return proper import location from in-repo addon 1`] = `Array []`; +exports[`With \`batman project\` initialized on server return proper import location from in-repo addon 1`] = ` +Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/lib/foo/addon/components/bar.js", + }, +] +`; diff --git a/test/__snapshots__/fixture-based-integration-test.ts.snap b/test/__snapshots__/fixture-based-integration-test.ts.snap index 59c0042de..68085d75b 100644 --- a/test/__snapshots__/fixture-based-integration-test.ts.snap +++ b/test/__snapshots__/fixture-based-integration-test.ts.snap @@ -49,10 +49,59 @@ Array [ }, }, }, + Object { + "data": Object { + "files": Array [ + "lib/foo/addon/templates/components/bar.hbs", + ], + }, + "detail": "component", + "kind": 7, + "label": "Bar", + "textEdit": Object { + "newText": "Bar", + "range": Object { + "end": Object { + "character": 2, + "line": 1, + }, + "start": Object { + "character": 1, + "line": 1, + }, + }, + }, + }, ] `; -exports[`With \`full-project\` initialized on server Completion request returns all angle-bracket in a element expression for in repo addons without batman syntax 1`] = `Array []`; +exports[`With \`full-project\` initialized on server Completion request returns all angle-bracket in a element expression for in repo addons without batman syntax 1`] = ` +Array [ + Object { + "data": Object { + "files": Array [ + "lib/foo/addon/templates/components/bar.hbs", + ], + }, + "detail": "component", + "kind": 7, + "label": "Bar", + "textEdit": Object { + "newText": "Bar", + "range": Object { + "end": Object { + "character": 2, + "line": 1, + }, + "start": Object { + "character": 1, + "line": 1, + }, + }, + }, + }, +] +`; exports[`With \`full-project\` initialized on server Completion request returns all components and helpers when requesting completion items in a handlebars expression 1`] = ` Array [ @@ -126,6 +175,29 @@ Array [ }, }, }, + Object { + "data": Object { + "files": Array [ + "lib/foo/addon/templates/components/bar.hbs", + ], + }, + "detail": "component", + "kind": 7, + "label": "bar", + "textEdit": Object { + "newText": "bar", + "range": Object { + "end": Object { + "character": 2, + "line": 1, + }, + "start": Object { + "character": 2, + "line": 1, + }, + }, + }, + }, Object { "data": Object { "files": Array [ diff --git a/test/__snapshots__/go-to-definition-integration-test.ts.snap b/test/__snapshots__/go-to-definition-integration-test.ts.snap index f5aed2af8..46034418a 100644 --- a/test/__snapshots__/go-to-definition-integration-test.ts.snap +++ b/test/__snapshots__/go-to-definition-integration-test.ts.snap @@ -2,7 +2,13 @@ exports[`script files named import definition resolution addon case default import from addon 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "my-addon", + "root": "node_modules/my-addon", + "version": 1, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -10,13 +16,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 14, + "line": 1, + }, + "start": Object { + "character": 7, + "line": 1, + }, + }, + "uri": "/node_modules/my-addon/addon/utils/hello.js", + }, + ], } `; exports[`script files named import definition resolution addon case named import from addon 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "my-addon", + "root": "node_modules/my-addon", + "version": 1, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -24,13 +50,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 17, + "line": 2, + }, + "start": Object { + "character": 13, + "line": 2, + }, + }, + "uri": "/node_modules/my-addon/addon/utils/hello.js", + }, + ], } `; exports[`script files named import definition resolution addon case not existing default import from addon 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "my-addon", + "root": "node_modules/my-addon", + "version": 1, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -38,13 +84,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/node_modules/my-addon/addon/utils/hello.js", + }, + ], } `; exports[`script files named import definition resolution addon case unknown named import from addon 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "my-addon", + "root": "node_modules/my-addon", + "version": 1, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -52,7 +118,21 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/node_modules/my-addon/addon/utils/hello.js", + }, + ], } `; diff --git a/test/__snapshots__/integration-test.ts.snap b/test/__snapshots__/integration-test.ts.snap index f3d204284..7758c2103 100644 --- a/test/__snapshots__/integration-test.ts.snap +++ b/test/__snapshots__/integration-test.ts.snap @@ -2,7 +2,28 @@ exports[`integration async fs enabled: false API:Chain support addon ordering 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "addon1", + "root": "node_modules/addon1", + "version": null, + }, + Object { + "name": "addon2", + "root": "node_modules/addon2", + "version": null, + }, + Object { + "name": "addon3", + "root": "node_modules/addon3", + "version": null, + }, + Object { + "name": "addon4", + "root": "node_modules/addon4", + "version": null, + }, + ], "registry": Object { "component": Object { "dory": Array [ @@ -14,9 +35,9 @@ Object { Object { "detail": "a = undefined", "kind": 10, - "label": "this.a", + "label": "this.a_addon2__addon1__addon4__addon3_", "textEdit": Object { - "newText": "this.a", + "newText": "this.a_addon2__addon1__addon4__addon3_", "range": Object { "end": Object { "character": 8, @@ -66,7 +87,13 @@ Object { exports[`integration async fs enabled: false Able to provide API:Completion support dummy addon completion:script 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -74,13 +101,23 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "label": "name", + }, + ], } `; exports[`integration async fs enabled: false Able to provide API:Completion support dummy addon completion:template 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -90,11 +127,9 @@ Object { }, "response": Array [ Object { - "detail": "n = undefined", - "kind": 10, - "label": "this.n", + "label": "this.name", "textEdit": Object { - "newText": "this.n", + "newText": "this.name", "range": Object { "end": Object { "character": 8, @@ -113,7 +148,13 @@ Object { exports[`integration async fs enabled: false Able to provide API:Definition support dummy addon definition:script 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -121,13 +162,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/components/hello/index.js", + }, + ], } `; exports[`integration async fs enabled: false Able to provide API:Definition support dummy addon definition:template 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -135,13 +196,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/components/hello/index.hbs", + }, + ], } `; exports[`integration async fs enabled: false Able to provide API:Hover support dummy addon hover:template 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -149,13 +230,31 @@ Object { ], }, }, - "response": null, + "response": Object { + "contents": "foo", + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + }, } `; exports[`integration async fs enabled: false Able to provide API:Reference support dummy addon reference:template 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -163,7 +262,21 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/components/hello/index.hbs", + }, + ], } `; @@ -350,15 +463,52 @@ This allows you to progressively add CSS classes to your components, and makes t exports[`integration async fs enabled: false Able to provide autocomplete information for local context access support child project addon calling parent project addon 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "biz", + "root": "lib/biz", + "version": 1, + }, + Object { + "name": "foo", + "root": "../lib/foo", + "version": 1, + }, + ], "registry": Object { + "component": Object { + "bar": Array [ + "lib/biz/addon/templates/components/bar.hbs", + "../lib/foo/addon/templates/components/bar.hbs", + "../lib/foo/app/components/bar.js", + ], + }, "routePath": Object { "hello": Array [ "app/templates/hello.hbs", ], }, }, - "response": Array [], + "response": Array [ + Object { + "detail": "component", + "kind": 7, + "label": "Foo$Bar", + "textEdit": Object { + "newText": "Foo$Bar", + "range": Object { + "end": Object { + "character": 2, + "line": 0, + }, + "start": Object { + "character": 1, + "line": 0, + }, + }, + }, + }, + ], } `; @@ -1294,7 +1444,13 @@ Object { exports[`integration async fs enabled: false Able to use classes for API support dummy class-based addon definition:template 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -1302,13 +1458,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/components/hello/index.hbs", + }, + ], } `; exports[`integration async fs enabled: false Able to use classes for API support dummy class-based addon definition:template with correctly binded context 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -1316,7 +1492,21 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/components/hello/index.hbs", + }, + ], } `; @@ -2896,9 +3086,18 @@ Object { exports[`integration async fs enabled: false Go to definition works for all supported cases go to definition from app to in repo addon 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "biz", + "root": "lib/biz", + "version": 1, + }, + ], "registry": Object { "component": Object { + "bar": Array [ + "lib/biz/addon/components/bar.js", + ], "darling": Array [ "app/components/darling/index.js", ], @@ -2907,13 +3106,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/lib/biz/addon/components/bar.js", + }, + ], } `; exports[`integration async fs enabled: false Go to definition works for all supported cases go to definition from app to nested utils location of in repo addon 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "biz", + "root": "lib/biz", + "version": 1, + }, + ], "registry": Object { "component": Object { "darling": Array [ @@ -2924,7 +3143,21 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/lib/biz/addon/utils/boo/blah/zoo/bar.js", + }, + ], } `; @@ -3284,8 +3517,25 @@ Object { exports[`integration async fs enabled: false Project class resolution, based on fs path and file structure support parent project addon calling child project 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "biz", + "root": "lib/biz", + "version": 1, + }, + Object { + "name": "foo", + "root": "../lib/foo", + "version": 1, + }, + ], "registry": Object { + "component": Object { + "bar": Array [ + "lib/biz/addon/components/bar.js", + "../lib/foo/addon/components/bar.js", + ], + }, "helper": Object { "blah": Array [ "tests/helpers/blah.js", @@ -3297,7 +3547,21 @@ Object { ], }, }, - "response": null, + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/tests/helpers/blah.js", + }, + ], } `; @@ -3316,7 +3580,7 @@ Array [ Object { "data": Object { "files": Array [ - "../../../../../../../var/folders/wk/w99lck4x7_5930c7gj65r3s40000gp/T/tmp-26763GMELN7q3I1hZ/lib/addon/components/item.hbs", + "../addon/components/item.hbs", ], }, "detail": "component", @@ -3339,7 +3603,13 @@ Array [ ], }, Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "my-addon", + "root": "../lib", + "version": 1, + }, + ], "registry": Object { "component": Object { "bar": Array [ @@ -3348,13 +3618,16 @@ Array [ "foo": Array [ "app/components/foo.hbs", ], + "item": Array [ + "../lib/addon/components/item.hbs", + ], }, }, "response": Array [ Object { "data": Object { "files": Array [ - "../../../../../../../var/folders/wk/w99lck4x7_5930c7gj65r3s40000gp/T/tmp-26763GMELN7q3I1hZ/lib/addon/components/item.hbs", + "../addon/components/item.hbs", ], }, "detail": "component", @@ -3655,7 +3928,28 @@ Array [ exports[`integration async fs enabled: true API:Chain support addon ordering 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "addon1", + "root": "node_modules/addon1", + "version": null, + }, + Object { + "name": "addon2", + "root": "node_modules/addon2", + "version": null, + }, + Object { + "name": "addon3", + "root": "node_modules/addon3", + "version": null, + }, + Object { + "name": "addon4", + "root": "node_modules/addon4", + "version": null, + }, + ], "registry": Object { "component": Object { "dory": Array [ @@ -3667,9 +3961,9 @@ Object { Object { "detail": "a = undefined", "kind": 10, - "label": "this.a", + "label": "this.a_addon2__addon1__addon4__addon3_", "textEdit": Object { - "newText": "this.a", + "newText": "this.a_addon2__addon1__addon4__addon3_", "range": Object { "end": Object { "character": 8, @@ -3719,7 +4013,13 @@ Object { exports[`integration async fs enabled: true Able to provide API:Completion support dummy addon completion:script 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -3727,13 +4027,23 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "label": "name", + }, + ], } `; exports[`integration async fs enabled: true Able to provide API:Completion support dummy addon completion:template 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -3743,11 +4053,9 @@ Object { }, "response": Array [ Object { - "detail": "n = undefined", - "kind": 10, - "label": "this.n", + "label": "this.name", "textEdit": Object { - "newText": "this.n", + "newText": "this.name", "range": Object { "end": Object { "character": 8, @@ -3766,7 +4074,13 @@ Object { exports[`integration async fs enabled: true Able to provide API:Definition support dummy addon definition:script 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -3774,13 +4088,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/components/hello/index.js", + }, + ], } `; exports[`integration async fs enabled: true Able to provide API:Definition support dummy addon definition:template 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -3788,13 +4122,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/components/hello/index.hbs", + }, + ], } `; exports[`integration async fs enabled: true Able to provide API:Hover support dummy addon hover:template 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -3802,13 +4156,31 @@ Object { ], }, }, - "response": null, + "response": Object { + "contents": "foo", + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + }, } `; exports[`integration async fs enabled: true Able to provide API:Reference support dummy addon reference:template 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -3816,7 +4188,21 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/components/hello/index.hbs", + }, + ], } `; @@ -4003,15 +4389,52 @@ This allows you to progressively add CSS classes to your components, and makes t exports[`integration async fs enabled: true Able to provide autocomplete information for local context access support child project addon calling parent project addon 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "biz", + "root": "lib/biz", + "version": 1, + }, + Object { + "name": "foo", + "root": "../lib/foo", + "version": 1, + }, + ], "registry": Object { + "component": Object { + "bar": Array [ + "lib/biz/addon/templates/components/bar.hbs", + "../lib/foo/addon/templates/components/bar.hbs", + "../lib/foo/app/components/bar.js", + ], + }, "routePath": Object { "hello": Array [ "app/templates/hello.hbs", ], }, }, - "response": Array [], + "response": Array [ + Object { + "detail": "component", + "kind": 7, + "label": "Foo$Bar", + "textEdit": Object { + "newText": "Foo$Bar", + "range": Object { + "end": Object { + "character": 2, + "line": 0, + }, + "start": Object { + "character": 1, + "line": 0, + }, + }, + }, + }, + ], } `; @@ -4947,7 +5370,13 @@ Object { exports[`integration async fs enabled: true Able to use classes for API support dummy class-based addon definition:template 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -4955,13 +5384,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/components/hello/index.hbs", + }, + ], } `; exports[`integration async fs enabled: true Able to use classes for API support dummy class-based addon definition:template with correctly binded context 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "provider", + "root": "node_modules/provider", + "version": null, + }, + ], "registry": Object { "component": Object { "hello": Array [ @@ -4969,7 +5418,21 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/components/hello/index.hbs", + }, + ], } `; @@ -6549,9 +7012,18 @@ Object { exports[`integration async fs enabled: true Go to definition works for all supported cases go to definition from app to in repo addon 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "biz", + "root": "lib/biz", + "version": 1, + }, + ], "registry": Object { "component": Object { + "bar": Array [ + "lib/biz/addon/components/bar.js", + ], "darling": Array [ "app/components/darling/index.js", ], @@ -6560,13 +7032,33 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/lib/biz/addon/components/bar.js", + }, + ], } `; exports[`integration async fs enabled: true Go to definition works for all supported cases go to definition from app to nested utils location of in repo addon 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "biz", + "root": "lib/biz", + "version": 1, + }, + ], "registry": Object { "component": Object { "darling": Array [ @@ -6577,7 +7069,21 @@ Object { ], }, }, - "response": Array [], + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/lib/biz/addon/utils/boo/blah/zoo/bar.js", + }, + ], } `; @@ -6833,6 +7339,37 @@ Object { } `; +exports[`integration async fs enabled: true Go to definition works for all supported cases to children route from application outlet 2`] = ` +Object { + "addonsMeta": Array [], + "registry": Object { + "routePath": Object { + "application": Array [ + "app/templates/application.hbs", + ], + "foo": Array [ + "app/templates/foo.hbs", + ], + }, + }, + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/app/templates/foo.hbs", + }, + ], +} +`; + exports[`integration async fs enabled: true Go to definition works for all supported cases to children route from meaningful outlet 1`] = ` Object { "addonsMeta": Array [], @@ -6937,8 +7474,25 @@ Object { exports[`integration async fs enabled: true Project class resolution, based on fs path and file structure support parent project addon calling child project 1`] = ` Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "biz", + "root": "lib/biz", + "version": 1, + }, + Object { + "name": "foo", + "root": "../lib/foo", + "version": 1, + }, + ], "registry": Object { + "component": Object { + "bar": Array [ + "lib/biz/addon/components/bar.js", + "../lib/foo/addon/components/bar.js", + ], + }, "helper": Object { "blah": Array [ "tests/helpers/blah.js", @@ -6950,7 +7504,21 @@ Object { ], }, }, - "response": null, + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/tests/helpers/blah.js", + }, + ], } `; @@ -6969,7 +7537,7 @@ Array [ Object { "data": Object { "files": Array [ - "../../../../../../../var/folders/wk/w99lck4x7_5930c7gj65r3s40000gp/T/tmp-26763vvD36al6vbcp/lib/addon/components/item.hbs", + "../addon/components/item.hbs", ], }, "detail": "component", @@ -6992,7 +7560,13 @@ Array [ ], }, Object { - "addonsMeta": Array [], + "addonsMeta": Array [ + Object { + "name": "my-addon", + "root": "../lib", + "version": 1, + }, + ], "registry": Object { "component": Object { "bar": Array [ @@ -7001,13 +7575,16 @@ Array [ "foo": Array [ "app/components/foo.hbs", ], + "item": Array [ + "../lib/addon/components/item.hbs", + ], }, }, "response": Array [ Object { "data": Object { "files": Array [ - "../../../../../../../var/folders/wk/w99lck4x7_5930c7gj65r3s40000gp/T/tmp-26763vvD36al6vbcp/lib/addon/components/item.hbs", + "../addon/components/item.hbs", ], }, "detail": "component", From f0b984ede5809bcb2186140e1a17d00977600ccc Mon Sep 17 00:00:00 2001 From: Alex Kanunnikov Date: Tue, 5 Nov 2024 21:19:10 +0300 Subject: [PATCH 09/17] + --- .../core/script-completion-provider.ts | 8 ++++---- .../core/template-completion-provider.ts | 12 ++++++------ src/project.ts | 5 +++-- src/server.ts | 2 +- src/utils/addon-api.ts | 11 ++++++++--- src/utils/definition-helpers.ts | 14 ++++++++++---- src/utils/layout-helpers.ts | 10 +++++++--- src/utils/logger.ts | 16 ++++++++-------- test/integration-test.ts | 16 ++++++++++++++++ test/test_helpers/integration-helpers.ts | 1 + test/utils/layout-helpers-test.ts | 6 +++--- 11 files changed, 67 insertions(+), 34 deletions(-) diff --git a/src/builtin-addons/core/script-completion-provider.ts b/src/builtin-addons/core/script-completion-provider.ts index 441a9ab59..5432eb10e 100644 --- a/src/builtin-addons/core/script-completion-provider.ts +++ b/src/builtin-addons/core/script-completion-provider.ts @@ -84,7 +84,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenDependencies); + await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -107,7 +107,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenDependencies); + await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -130,7 +130,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenDependencies); + await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -185,7 +185,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenDependencies); + await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } diff --git a/src/builtin-addons/core/template-completion-provider.ts b/src/builtin-addons/core/template-completion-provider.ts index e289f63c3..be8002116 100644 --- a/src/builtin-addons/core/template-completion-provider.ts +++ b/src/builtin-addons/core/template-completion-provider.ts @@ -178,7 +178,7 @@ export default class TemplateCompletionProvider { await mListComponents(project); this.enableRegistryCache('componentsRegistryInitialized'); - await mGetProjectAddonsInfo(project.root, project.seenDependencies); + await mGetProjectAddonsInfo(project.root, project.seenProjectDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); @@ -195,7 +195,7 @@ export default class TemplateCompletionProvider { const items: CompletionItem[] = []; if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenDependencies); + await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -256,7 +256,7 @@ export default class TemplateCompletionProvider { } async getMustachePathCandidates(root: string) { if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenDependencies); + await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -305,7 +305,7 @@ export default class TemplateCompletionProvider { } async getBlockPathCandidates(root: string): Promise { if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenDependencies); + await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -340,7 +340,7 @@ export default class TemplateCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(this.project.root, this.project.seenDependencies); + await mGetProjectAddonsInfo(this.project.root, this.project.seenProjectDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -447,7 +447,7 @@ export default class TemplateCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenDependencies); + await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } diff --git a/src/project.ts b/src/project.ts index e8a48aa30..5893beca3 100644 --- a/src/project.ts +++ b/src/project.ts @@ -37,7 +37,8 @@ export interface Executors { export class Project extends BaseProject { // Don't traverse dependencies we've already seen. // correct package graph can sort of throw us in to cycles if we don't keep track of this. - seenDependencies = new Set(); + seenInRepoDependencies: Set = new Set(); + seenProjectDependencies: Set = new Set(); providers!: ProjectProviders; builtinProviders!: ProjectProviders; @@ -152,7 +153,7 @@ export class Project extends BaseProject { this.providers = emptyProjectProviders(); this.flags.enableEagerRegistryInitialization = false; } else if (server.options.type === 'node') { - this.providers = await collectProjectProviders(this.root, this.addons, this.seenDependencies); + this.providers = await collectProjectProviders(this.root, this.addons, this.seenInRepoDependencies, this.seenProjectDependencies); } else { throw new Error(`Unknown server type: "${server.options.type}"`); } diff --git a/src/server.ts b/src/server.ts index 953ae904c..0bb6a0c8e 100644 --- a/src/server.ts +++ b/src/server.ts @@ -218,7 +218,7 @@ export default class Server { }; } - await mGetProjectAddonsInfo(project.root, project.seenDependencies); + await mGetProjectAddonsInfo(project.root, project.seenProjectDependencies); project.invalidateRegistry(); return { diff --git a/src/utils/addon-api.ts b/src/utils/addon-api.ts index 917ff4d7c..05185da6a 100644 --- a/src/utils/addon-api.ts +++ b/src/utils/addon-api.ts @@ -187,13 +187,18 @@ function requireUncached(module: string) { return result; } -export async function collectProjectProviders(root: string, addons: string[], seenDependencies: Set): Promise { +export async function collectProjectProviders( + root: string, + addons: string[], + seenInRepoDependencies: Set, + seenProjectDependencies: Set +): Promise { const time = instrumentTime(`collectProjectProviders(${root})`); time.log(`Starting`); const [projectAddonsRoots, projectInRepoAddonsRoots] = await Promise.all([ - getProjectAddonsRoots(root, seenDependencies), - getProjectInRepoAddonsRoots(root, seenDependencies), + getProjectAddonsRoots(root, seenProjectDependencies), + getProjectInRepoAddonsRoots(root, seenInRepoDependencies), ]); time.log(`found roots`); diff --git a/src/utils/definition-helpers.ts b/src/utils/definition-helpers.ts index 8812b0960..7193fc126 100644 --- a/src/utils/definition-helpers.ts +++ b/src/utils/definition-helpers.ts @@ -168,7 +168,7 @@ export function getPathsForComponentTemplates(root: string, maybeComponentName: } export async function getAddonImport(project: Project, importPath: string) { - const { root, seenDependencies } = project; + const { root, seenProjectDependencies, seenInRepoDependencies } = project; const importParts = importPath.split('/'); let addonName = importParts.shift(); @@ -181,7 +181,10 @@ export async function getAddonImport(project: Project, importPath: string) { } const items: string[] = []; - const [addonRoots, inRepoRoots] = await Promise.all([mProjectAddonsRoots(root, seenDependencies), mProjectInRepoAddonsRoots(root, seenDependencies)]); + const [addonRoots, inRepoRoots] = await Promise.all([ + mProjectAddonsRoots(root, seenProjectDependencies), + mProjectInRepoAddonsRoots(root, seenInRepoDependencies), + ]); const roots = items.concat(addonRoots, inRepoRoots); let existingPaths: string[] = []; @@ -231,9 +234,12 @@ export async function getAddonImport(project: Project, importPath: string) { } export async function getAddonPathsForType(project: Project, collection: 'services' | 'models' | 'modifiers' | 'helpers' | 'routes', name: string) { - const { root, seenDependencies } = project; + const { root, seenInRepoDependencies, seenProjectDependencies } = project; const items: string[] = []; - const [addonRoots, inRepoRoots] = await Promise.all([mProjectAddonsRoots(root, seenDependencies), mProjectInRepoAddonsRoots(root, seenDependencies)]); + const [addonRoots, inRepoRoots] = await Promise.all([ + mProjectAddonsRoots(root, seenProjectDependencies), + mProjectInRepoAddonsRoots(root, seenInRepoDependencies), + ]); const roots = items.concat(addonRoots, inRepoRoots); let existingPaths: string[] = []; let hasValidPath = false; diff --git a/src/utils/layout-helpers.ts b/src/utils/layout-helpers.ts index e61aded70..38bbc4e4d 100644 --- a/src/utils/layout-helpers.ts +++ b/src/utils/layout-helpers.ts @@ -6,7 +6,7 @@ import { clean, coerce, valid } from 'semver'; import { BaseProject } from '../base-project'; import { fsProvider } from '../fs-provider'; import walkAsync from './walk-async'; -import { instrumentTime } from './logger'; +import { instrumentTime, logDebugInfo } from './logger'; // const GLOBAL_REGISTRY = ['primitive-name'][['relatedFiles']]; @@ -253,7 +253,7 @@ async function getRecursiveInRepoAddonRoots(root: string, seenDependencies: Set< return recursiveRoots.sort(); } -export async function getProjectInRepoAddonsRoots(root: string, seenDependencies: Set): Promise { +export async function getProjectInRepoAddonsRoots(root: string, seenDependencies: Set = new Set()): Promise { const time = instrumentTime(`getProjectInRepoAddonsRoots(${root})`); const roots: string[] = await getRecursiveInRepoAddonRoots(root, seenDependencies, []); @@ -302,7 +302,11 @@ export async function getProjectAddonsRoots(root: string, seenDependencies: Set< const pack = await asyncGetPackageJSON(root); - if (!pack.name) return []; + if (!pack.name) { + logDebugInfo('no name', root, JSON.stringify(pack), Array.from(seenDependencies), resolvedItems); + + return []; + } if (seenDependencies.has(pack.name)) return []; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 5274a7459..d31c4081d 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -4,17 +4,17 @@ import { resolve } from 'path'; import { RemoteConsole } from 'vscode-languageserver/node'; import { fsProvider } from '../fs-provider'; -function getEnv() { - if (typeof process !== undefined) { - return process.env; - } else { - return {}; - } -} +// function getEnv() { +// if (typeof process !== undefined) { +// return process.env; +// } else { +// return {}; +// } +// } // Log debugging to the ELS package root, if possible // eslint-disable-next-line no-extra-boolean-cast -const debug = !!getEnv().CI ? true : getEnv().ELS_DEBUG || false; +const debug = true; const log_file = debug ? fsProvider().createWriteStream(resolve(__dirname, `../../debug.${process.pid}.log`), { flags: 'w' }) : null; let remoteConsole: RemoteConsole | null = null; diff --git a/test/integration-test.ts b/test/integration-test.ts index 321778d6a..1bb7f76f0 100644 --- a/test/integration-test.ts +++ b/test/integration-test.ts @@ -208,6 +208,7 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'some-project', 'ember-addon': { paths: ['lib/biz'], }, @@ -263,6 +264,7 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'some-different-project', 'ember-addon': { paths: ['lib/biz'], }, @@ -1006,6 +1008,7 @@ describe('integration', function () { connection, { 'package.json': JSON.stringify({ + name: 'default-name', 'ember-language-server': { entry: './lib/langserver', capabilities: { @@ -1056,6 +1059,7 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 't1', dependencies: { provider: '*', }, @@ -1097,7 +1101,9 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'fake', dependencies: { + name: 'lake', provider: '*', }, }), @@ -1322,7 +1328,9 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'pork', dependencies: { + name: 'park', provider: '*', }, }), @@ -1396,7 +1404,9 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'white', dependencies: { + name: 'dark', provider: '*', }, }), @@ -1456,6 +1466,7 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'shark', dependencies: { provider: '*', }, @@ -1514,6 +1525,7 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'pork', dependencies: { provider: '*', }, @@ -1562,6 +1574,7 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'dog', dependencies: { provider: '*', }, @@ -1609,6 +1622,7 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'cat', dependencies: { provider: '*', }, @@ -1692,6 +1706,7 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'zoo', dependencies: { 'ember-holy-futuristic-template-namespacing-batman': '^1.0.2' }, 'ember-addon': { paths: ['lib/biz'], @@ -1730,6 +1745,7 @@ describe('integration', function () { }, }, 'package.json': JSON.stringify({ + name: 'boss', dependencies: { 'ember-holy-futuristic-template-namespacing-batman': '^1.0.2' }, }), }, diff --git a/test/test_helpers/integration-helpers.ts b/test/test_helpers/integration-helpers.ts index 6a35c784e..c221c5ad9 100644 --- a/test/test_helpers/integration-helpers.ts +++ b/test/test_helpers/integration-helpers.ts @@ -708,6 +708,7 @@ export function makeProject(appFiles = {}, addons = {}) { const fileStructure = Object.assign({}, appFiles, { node_modules, 'package.json': JSON.stringify({ + name: 'mock-project', dependencies, }), }); diff --git a/test/utils/layout-helpers-test.ts b/test/utils/layout-helpers-test.ts index a5f72ba93..81bd8733d 100644 --- a/test/utils/layout-helpers-test.ts +++ b/test/utils/layout-helpers-test.ts @@ -130,7 +130,7 @@ describe('definition-helpers', function () { describe('getProjectInRepoAddonsRoots()', function () { it('must discover in-repo addons for classic structure', async function () { const root = path.join(__dirname, './../fixtures/project-with-in-repo-addons'); - const items = await getProjectInRepoAddonsRoots(root); + const items = await getProjectInRepoAddonsRoots(root, new Set()); expect(items.length).toEqual(2); }); @@ -139,7 +139,7 @@ describe('definition-helpers', function () { describe('getProjectAddonsRoots()', function () { it('must resolve all related to project addons', async function () { const root = path.join(__dirname, './../fixtures/full-project'); - const items = await getProjectAddonsRoots(root, [], 'hope_modules'); + const items = await getProjectAddonsRoots(root, new Set(), [], 'hope_modules'); expect(items.length).toEqual(2); }); @@ -174,7 +174,7 @@ describe('definition-helpers', function () { }, }); - const items = await getProjectAddonsRoots(path.join(info.path, 'packages', 'touchstone')); + const items = await getProjectAddonsRoots(path.join(info.path, 'packages', 'touchstone'), new Set()); expect(items.length).toEqual(1); expect(items[0].split(path.sep).join('/').split('node_modules/')[1]).toEqual('@skylight/anvil'); From c3cf020ff85b2f5decb0a8f99ea9b6edf3cfe4dd Mon Sep 17 00:00:00 2001 From: Alex Kanunnikov Date: Tue, 5 Nov 2024 22:00:13 +0300 Subject: [PATCH 10/17] dependency-map-approach --- .../core/script-completion-provider.ts | 8 +- .../core/template-completion-provider.ts | 12 +- src/project.ts | 12 +- src/server.ts | 2 +- src/utils/addon-api.ts | 11 +- src/utils/definition-helpers.ts | 14 +-- src/utils/layout-helpers.ts | 111 ++++++++++++------ 7 files changed, 101 insertions(+), 69 deletions(-) diff --git a/src/builtin-addons/core/script-completion-provider.ts b/src/builtin-addons/core/script-completion-provider.ts index 5432eb10e..bfbefc16b 100644 --- a/src/builtin-addons/core/script-completion-provider.ts +++ b/src/builtin-addons/core/script-completion-provider.ts @@ -84,7 +84,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); + await mGetProjectAddonsInfo(root, this.project.dependencyMap); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -107,7 +107,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); + await mGetProjectAddonsInfo(root, this.project.dependencyMap); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -130,7 +130,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); + await mGetProjectAddonsInfo(root, this.project.dependencyMap); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -185,7 +185,7 @@ export default class ScriptCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); + await mGetProjectAddonsInfo(root, this.project.dependencyMap); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } diff --git a/src/builtin-addons/core/template-completion-provider.ts b/src/builtin-addons/core/template-completion-provider.ts index be8002116..f92133a21 100644 --- a/src/builtin-addons/core/template-completion-provider.ts +++ b/src/builtin-addons/core/template-completion-provider.ts @@ -178,7 +178,7 @@ export default class TemplateCompletionProvider { await mListComponents(project); this.enableRegistryCache('componentsRegistryInitialized'); - await mGetProjectAddonsInfo(project.root, project.seenProjectDependencies); + await mGetProjectAddonsInfo(project.root, project.dependencyMap); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); @@ -195,7 +195,7 @@ export default class TemplateCompletionProvider { const items: CompletionItem[] = []; if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); + await mGetProjectAddonsInfo(root, this.project.dependencyMap); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -256,7 +256,7 @@ export default class TemplateCompletionProvider { } async getMustachePathCandidates(root: string) { if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); + await mGetProjectAddonsInfo(root, this.project.dependencyMap); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -305,7 +305,7 @@ export default class TemplateCompletionProvider { } async getBlockPathCandidates(root: string): Promise { if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); + await mGetProjectAddonsInfo(root, this.project.dependencyMap); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -340,7 +340,7 @@ export default class TemplateCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(this.project.root, this.project.seenProjectDependencies); + await mGetProjectAddonsInfo(this.project.root, this.project.dependencyMap); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } @@ -447,7 +447,7 @@ export default class TemplateCompletionProvider { } if (!this.meta.projectAddonsInfoInitialized) { - await mGetProjectAddonsInfo(root, this.project.seenProjectDependencies); + await mGetProjectAddonsInfo(root, this.project.dependencyMap); this.enableRegistryCache('projectAddonsInfoInitialized'); this.project.invalidateRegistry(); } diff --git a/src/project.ts b/src/project.ts index 5893beca3..5e6014d62 100644 --- a/src/project.ts +++ b/src/project.ts @@ -37,8 +37,14 @@ export interface Executors { export class Project extends BaseProject { // Don't traverse dependencies we've already seen. // correct package graph can sort of throw us in to cycles if we don't keep track of this. - seenInRepoDependencies: Set = new Set(); - seenProjectDependencies: Set = new Set(); + + dependencyMap: Map< + string, + { + package: PackageInfo; + root: string; + } + > = new Map(); providers!: ProjectProviders; builtinProviders!: ProjectProviders; @@ -153,7 +159,7 @@ export class Project extends BaseProject { this.providers = emptyProjectProviders(); this.flags.enableEagerRegistryInitialization = false; } else if (server.options.type === 'node') { - this.providers = await collectProjectProviders(this.root, this.addons, this.seenInRepoDependencies, this.seenProjectDependencies); + this.providers = await collectProjectProviders(this.root, this.addons, this.dependencyMap); } else { throw new Error(`Unknown server type: "${server.options.type}"`); } diff --git a/src/server.ts b/src/server.ts index 0bb6a0c8e..02abe190b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -218,7 +218,7 @@ export default class Server { }; } - await mGetProjectAddonsInfo(project.root, project.seenProjectDependencies); + await mGetProjectAddonsInfo(project.root, project.dependencyMap); project.invalidateRegistry(); return { diff --git a/src/utils/addon-api.ts b/src/utils/addon-api.ts index 05185da6a..6f6eb5437 100644 --- a/src/utils/addon-api.ts +++ b/src/utils/addon-api.ts @@ -187,18 +187,13 @@ function requireUncached(module: string) { return result; } -export async function collectProjectProviders( - root: string, - addons: string[], - seenInRepoDependencies: Set, - seenProjectDependencies: Set -): Promise { +export async function collectProjectProviders(root: string, addons: string[], dependencyMap: Project['dependencyMap']): Promise { const time = instrumentTime(`collectProjectProviders(${root})`); time.log(`Starting`); const [projectAddonsRoots, projectInRepoAddonsRoots] = await Promise.all([ - getProjectAddonsRoots(root, seenProjectDependencies), - getProjectInRepoAddonsRoots(root, seenInRepoDependencies), + getProjectAddonsRoots(root, dependencyMap), + getProjectInRepoAddonsRoots(root, dependencyMap), ]); time.log(`found roots`); diff --git a/src/utils/definition-helpers.ts b/src/utils/definition-helpers.ts index 7193fc126..b5187b570 100644 --- a/src/utils/definition-helpers.ts +++ b/src/utils/definition-helpers.ts @@ -168,7 +168,7 @@ export function getPathsForComponentTemplates(root: string, maybeComponentName: } export async function getAddonImport(project: Project, importPath: string) { - const { root, seenProjectDependencies, seenInRepoDependencies } = project; + const { root, dependencyMap } = project; const importParts = importPath.split('/'); let addonName = importParts.shift(); @@ -181,10 +181,7 @@ export async function getAddonImport(project: Project, importPath: string) { } const items: string[] = []; - const [addonRoots, inRepoRoots] = await Promise.all([ - mProjectAddonsRoots(root, seenProjectDependencies), - mProjectInRepoAddonsRoots(root, seenInRepoDependencies), - ]); + const [addonRoots, inRepoRoots] = await Promise.all([mProjectAddonsRoots(root, dependencyMap), mProjectInRepoAddonsRoots(root, dependencyMap)]); const roots = items.concat(addonRoots, inRepoRoots); let existingPaths: string[] = []; @@ -234,12 +231,9 @@ export async function getAddonImport(project: Project, importPath: string) { } export async function getAddonPathsForType(project: Project, collection: 'services' | 'models' | 'modifiers' | 'helpers' | 'routes', name: string) { - const { root, seenInRepoDependencies, seenProjectDependencies } = project; + const { root, dependencyMap } = project; const items: string[] = []; - const [addonRoots, inRepoRoots] = await Promise.all([ - mProjectAddonsRoots(root, seenProjectDependencies), - mProjectInRepoAddonsRoots(root, seenInRepoDependencies), - ]); + const [addonRoots, inRepoRoots] = await Promise.all([mProjectAddonsRoots(root, dependencyMap), mProjectInRepoAddonsRoots(root, dependencyMap)]); const roots = items.concat(addonRoots, inRepoRoots); let existingPaths: string[] = []; let hasValidPath = false; diff --git a/src/utils/layout-helpers.ts b/src/utils/layout-helpers.ts index 38bbc4e4d..7c27acb5b 100644 --- a/src/utils/layout-helpers.ts +++ b/src/utils/layout-helpers.ts @@ -7,6 +7,7 @@ import { BaseProject } from '../base-project'; import { fsProvider } from '../fs-provider'; import walkAsync from './walk-async'; import { instrumentTime, logDebugInfo } from './logger'; +import { Project } from '../project'; // const GLOBAL_REGISTRY = ['primitive-name'][['relatedFiles']]; @@ -195,17 +196,20 @@ export function cached(_proto: unknown, prop: string, desc: PropertyDescriptor) }; } -async function getRecursiveInRepoAddonRoots(root: string, seenDependencies: Set, roots: string[]) { - const packageData = await asyncGetPackageJSON(root); +async function getRecursiveInRepoAddonRoots(root: string, dependencyMap: Project['dependencyMap'], roots: string[]) { + let fastPackage: PackageInfo | null = null; - // names are required for packages - if (!packageData.name) return []; - - if (seenDependencies.has(packageData.name)) { - return []; + for (const dependencyMapItem of dependencyMap) { + if (dependencyMapItem[1].root === root) { + fastPackage = dependencyMapItem[1].package; + break; + } } - seenDependencies.add(packageData.name); + const packageData = fastPackage || (await asyncGetPackageJSON(root)); + + // names are required for packages + if (!packageData.name) return []; const emberAddonPaths: string[] = (packageData['ember-addon'] && packageData['ember-addon'].paths) || []; @@ -222,17 +226,24 @@ async function getRecursiveInRepoAddonRoots(root: string, seenDependencies: Set< const validPaths = await asyncFilter(normalizedPaths, isProjectAddonRoot); for (const validRoot of validPaths) { - const packInfo = await asyncGetPackageJSON(validRoot); + let fastPackage: PackageInfo | null = null; + + for (const dependencyMapItem of dependencyMap) { + if (dependencyMapItem[1].root === validRoot) { + fastPackage = dependencyMapItem[1].package; + break; + } + } + + const packInfo = fastPackage || (await asyncGetPackageJSON(validRoot)); // names are required for packages if (!packInfo.name) continue; - if (seenDependencies.has(packInfo.name)) { - continue; + if (!fastPackage) { + dependencyMap.set(packInfo.name, { root: validRoot, package: packInfo }); } - seenDependencies.add(packInfo.name); - // we don't need to go deeper if package itself not an ember-addon or els-extension if (!isEmberAddon(packInfo) && !hasEmberLanguageServerExtension(packInfo)) { continue; @@ -240,7 +251,7 @@ async function getRecursiveInRepoAddonRoots(root: string, seenDependencies: Set< if (!recursiveRoots.includes(validRoot)) { recursiveRoots.push(validRoot); - const items = await getRecursiveInRepoAddonRoots(validRoot, seenDependencies, recursiveRoots); + const items = await getRecursiveInRepoAddonRoots(validRoot, dependencyMap, recursiveRoots); items.forEach((relatedRoot: string) => { if (!recursiveRoots.includes(relatedRoot)) { @@ -253,10 +264,10 @@ async function getRecursiveInRepoAddonRoots(root: string, seenDependencies: Set< return recursiveRoots.sort(); } -export async function getProjectInRepoAddonsRoots(root: string, seenDependencies: Set = new Set()): Promise { +export async function getProjectInRepoAddonsRoots(root: string, dependencyMap: Project['dependencyMap']): Promise { const time = instrumentTime(`getProjectInRepoAddonsRoots(${root})`); - const roots: string[] = await getRecursiveInRepoAddonRoots(root, seenDependencies, []); + const roots: string[] = await getRecursiveInRepoAddonRoots(root, dependencyMap, []); time.log(`finished getRecursiveInRepoAddonRoots`); @@ -297,21 +308,31 @@ export async function isGlimmerXProject(root: string) { return hasDep(pack, '@glimmerx/core') || hasDep(pack, 'glimmer-lite-core'); } -export async function getProjectAddonsRoots(root: string, seenDependencies: Set, resolvedItems: string[] = [], packageFolderName = 'node_modules') { +export async function getProjectAddonsRoots( + root: string, + dependencyMap: Project['dependencyMap'], + resolvedItems: string[] = [], + packageFolderName = 'node_modules' +) { const time = instrumentTime(`getProjectInRepoAddonsRoots(${root})`); - const pack = await asyncGetPackageJSON(root); + let fastPackage: PackageInfo | null = null; + + for (const dependencyMapItem of dependencyMap) { + if (dependencyMapItem[1].root === root) { + fastPackage = dependencyMapItem[1].package; + break; + } + } + + const pack = fastPackage || (await asyncGetPackageJSON(root)); if (!pack.name) { - logDebugInfo('no name', root, JSON.stringify(pack), Array.from(seenDependencies), resolvedItems); + logDebugInfo('no name', root, JSON.stringify(pack), Array.from(dependencyMap), resolvedItems); return []; } - if (seenDependencies.has(pack.name)) return []; - - seenDependencies.add(pack.name); - if (resolvedItems.length) { if (!isEmberAddon(pack)) { return []; @@ -323,28 +344,44 @@ export async function getProjectAddonsRoots(root: string, seenDependencies: Set< : [...Object.keys(pack.dependencies || {}), ...Object.keys(pack.peerDependencies || {}), ...Object.keys(pack.devDependencies || {})]; // logDebugInfo('items', items); - const rawRoots = await Promise.all( + const rawRoots: [string, string | false][] = await Promise.all( items.map(async (item: string) => { - return await resolvePackageRoot(root, item, packageFolderName); + if (dependencyMap.has(item)) { + return [item, dependencyMap.get(item)!.root ?? false]; + } + + const packageRoot = await resolvePackageRoot(root, item, packageFolderName); + + return [item, packageRoot]; }) ); - const roots = rawRoots.filter((p: string | boolean) => { + const _roots = rawRoots.filter(([, p]: [string, string | boolean]) => { return p !== false; - }) as string[]; + }) as Array<[string, string]>; const recursiveRoots: string[] = resolvedItems.slice(0); - const packages = await Promise.all(roots.map((root) => asyncGetPackageJSON(root))); + const packages = await Promise.all( + _roots.map(async ([packageName, packageRoot]) => { + const mappedValue = dependencyMap.get(packageName); - for (const rootItem of roots) { - const packInfo = packages[roots.indexOf(rootItem)]; + if (mappedValue) { + return mappedValue.package; + } - if (!packInfo.name) continue; + const packInfo = await asyncGetPackageJSON(packageRoot); + + dependencyMap.set(packageName, { root: packageRoot, package: packInfo }); + + return packInfo; + }) + ); - if (seenDependencies.has(packInfo.name)) continue; + const roots = _roots.map(([, p]: [string, string]) => p); - seenDependencies.add(packInfo.name); + for (const rootItem of roots) { + const packInfo = packages[roots.indexOf(rootItem)]; // we don't need to go deeper if package itself not an ember-addon or els-extension if (!isEmberAddon(packInfo) && !hasEmberLanguageServerExtension(packInfo)) { @@ -353,7 +390,7 @@ export async function getProjectAddonsRoots(root: string, seenDependencies: Set< if (!recursiveRoots.includes(rootItem)) { recursiveRoots.push(rootItem); - const addonRoots = await getProjectAddonsRoots(rootItem, seenDependencies, recursiveRoots, packageFolderName); + const addonRoots = await getProjectAddonsRoots(rootItem, dependencyMap, recursiveRoots, packageFolderName); addonRoots.forEach((item: string) => { if (!recursiveRoots.includes(item)) { @@ -440,10 +477,10 @@ export function hasAddonFolderInPath(name: string) { return name.includes(path.sep + 'addon' + path.sep) || name.includes(path.sep + 'addon-test-support' + path.sep); } -export async function getProjectAddonsInfo(root: string, seenDependencies: Set): Promise { +export async function getProjectAddonsInfo(root: string, dependencyMap: Project['dependencyMap']): Promise { const [projectAddonsRoots, projectInRepoAddonsRoots] = await Promise.all([ - getProjectAddonsRoots(root, seenDependencies), - getProjectInRepoAddonsRoots(root, seenDependencies), + getProjectAddonsRoots(root, dependencyMap), + getProjectInRepoAddonsRoots(root, dependencyMap), ]); const roots = ([] as string[]).concat(projectAddonsRoots, projectInRepoAddonsRoots).filter((pathItem: unknown) => typeof pathItem === 'string'); From 95d9f61ac25c9def950d2e38019581e8316430d2 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:57:33 -0500 Subject: [PATCH 11/17] Fix some tests --- test/utils/layout-helpers-test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/utils/layout-helpers-test.ts b/test/utils/layout-helpers-test.ts index 81bd8733d..bcd238add 100644 --- a/test/utils/layout-helpers-test.ts +++ b/test/utils/layout-helpers-test.ts @@ -130,7 +130,7 @@ describe('definition-helpers', function () { describe('getProjectInRepoAddonsRoots()', function () { it('must discover in-repo addons for classic structure', async function () { const root = path.join(__dirname, './../fixtures/project-with-in-repo-addons'); - const items = await getProjectInRepoAddonsRoots(root, new Set()); + const items = await getProjectInRepoAddonsRoots(root, new Map()); expect(items.length).toEqual(2); }); @@ -139,7 +139,7 @@ describe('definition-helpers', function () { describe('getProjectAddonsRoots()', function () { it('must resolve all related to project addons', async function () { const root = path.join(__dirname, './../fixtures/full-project'); - const items = await getProjectAddonsRoots(root, new Set(), [], 'hope_modules'); + const items = await getProjectAddonsRoots(root, new Map(), [], 'hope_modules'); expect(items.length).toEqual(2); }); @@ -174,7 +174,7 @@ describe('definition-helpers', function () { }, }); - const items = await getProjectAddonsRoots(path.join(info.path, 'packages', 'touchstone'), new Set()); + const items = await getProjectAddonsRoots(path.join(info.path, 'packages', 'touchstone'), new Map()); expect(items.length).toEqual(1); expect(items[0].split(path.sep).join('/').split('node_modules/')[1]).toEqual('@skylight/anvil'); From b211b6ee970e58edb88d9feebb6506bd92e2c3cf Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 6 Nov 2024 18:45:45 -0500 Subject: [PATCH 12/17] Fix #414 --- src/utils/layout-helpers.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/utils/layout-helpers.ts b/src/utils/layout-helpers.ts index 7c27acb5b..2012b4673 100644 --- a/src/utils/layout-helpers.ts +++ b/src/utils/layout-helpers.ts @@ -1,5 +1,6 @@ import * as memoize from 'memoizee'; import * as path from 'path'; +import * as childProcess from 'child_process'; import { CompletionItem, CompletionItemKind } from 'vscode-languageserver/node'; import { addToRegistry, normalizeMatchNaming } from './registry-api'; import { clean, coerce, valid } from 'semver'; @@ -119,10 +120,26 @@ export function getPodModulePrefix(root: string): string | null { return podModulePrefix.trim().length > 0 ? podModulePrefix : null; } +function getRoot(fromDir: string) { + try { + const topLevel = childProcess.execSync('git rev-parse --show-toplevel', { encoding: 'utf8', cwd: fromDir }); + + return topLevel.trim(); + } catch { + // we ignore the error, because we might not be in a git repo + return '/'; + } +} + export async function resolvePackageRoot(root: string, addonName: string, packagesFolder = 'node_modules'): Promise { const roots = root.split(path.sep); + const stopAt = getRoot(root); while (roots.length) { + if (roots.join(path.sep) === stopAt) { + return false; + } + const prefix = roots.join(path.sep); const maybePath = path.join(prefix, packagesFolder, addonName); const linkedPath = path.join(prefix, addonName); From 79e9ecc657c5510858470aa98674d7fd5b921412 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 6 Nov 2024 18:58:23 -0500 Subject: [PATCH 13/17] Fix tests for #414 --- src/utils/layout-helpers.ts | 2 +- test/utils/layout-helpers-test.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/utils/layout-helpers.ts b/src/utils/layout-helpers.ts index 2012b4673..b31267644 100644 --- a/src/utils/layout-helpers.ts +++ b/src/utils/layout-helpers.ts @@ -120,7 +120,7 @@ export function getPodModulePrefix(root: string): string | null { return podModulePrefix.trim().length > 0 ? podModulePrefix : null; } -function getRoot(fromDir: string) { +export function getRoot(fromDir: string) { try { const topLevel = childProcess.execSync('git rev-parse --show-toplevel', { encoding: 'utf8', cwd: fromDir }); diff --git a/test/utils/layout-helpers-test.ts b/test/utils/layout-helpers-test.ts index bcd238add..040549d44 100644 --- a/test/utils/layout-helpers-test.ts +++ b/test/utils/layout-helpers-test.ts @@ -37,11 +37,13 @@ describe('definition-helpers', function () { it('return package root folder', async function () { const resultOne = await resolvePackageRoot(__dirname, 'lodash'); - expect(resultOne).toContain(path.sep + 'lodash'); + // no lodash here. + expect(resultOne).toBe(false); const resultTwo = await resolvePackageRoot(__dirname, 'memoizee'); - expect(resultTwo).toContain(path.sep + 'memoizee'); + // memoizee also does not exist + expect(resultTwo).toBe(false); }); }); From 1d8ee80446bd39f384b0885e0fab84b346016fea Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:07:53 -0500 Subject: [PATCH 14/17] Remove test snapshot for test that dtoesn't exist --- test/__snapshots__/integration-test.ts.snap | 31 --------------------- 1 file changed, 31 deletions(-) diff --git a/test/__snapshots__/integration-test.ts.snap b/test/__snapshots__/integration-test.ts.snap index 7758c2103..7fb58873d 100644 --- a/test/__snapshots__/integration-test.ts.snap +++ b/test/__snapshots__/integration-test.ts.snap @@ -7339,37 +7339,6 @@ Object { } `; -exports[`integration async fs enabled: true Go to definition works for all supported cases to children route from application outlet 2`] = ` -Object { - "addonsMeta": Array [], - "registry": Object { - "routePath": Object { - "application": Array [ - "app/templates/application.hbs", - ], - "foo": Array [ - "app/templates/foo.hbs", - ], - }, - }, - "response": Array [ - Object { - "range": Object { - "end": Object { - "character": 0, - "line": 0, - }, - "start": Object { - "character": 0, - "line": 0, - }, - }, - "uri": "/app/templates/foo.hbs", - }, - ], -} -`; - exports[`integration async fs enabled: true Go to definition works for all supported cases to children route from meaningful outlet 1`] = ` Object { "addonsMeta": Array [], From 0215646ea458767be6f62967580fd07d0578724b Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:35:54 -0500 Subject: [PATCH 15/17] Revert "Fix tests for #414" This reverts commit 79e9ecc657c5510858470aa98674d7fd5b921412. --- src/utils/layout-helpers.ts | 2 +- test/utils/layout-helpers-test.ts | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/utils/layout-helpers.ts b/src/utils/layout-helpers.ts index b31267644..2012b4673 100644 --- a/src/utils/layout-helpers.ts +++ b/src/utils/layout-helpers.ts @@ -120,7 +120,7 @@ export function getPodModulePrefix(root: string): string | null { return podModulePrefix.trim().length > 0 ? podModulePrefix : null; } -export function getRoot(fromDir: string) { +function getRoot(fromDir: string) { try { const topLevel = childProcess.execSync('git rev-parse --show-toplevel', { encoding: 'utf8', cwd: fromDir }); diff --git a/test/utils/layout-helpers-test.ts b/test/utils/layout-helpers-test.ts index 040549d44..bcd238add 100644 --- a/test/utils/layout-helpers-test.ts +++ b/test/utils/layout-helpers-test.ts @@ -37,13 +37,11 @@ describe('definition-helpers', function () { it('return package root folder', async function () { const resultOne = await resolvePackageRoot(__dirname, 'lodash'); - // no lodash here. - expect(resultOne).toBe(false); + expect(resultOne).toContain(path.sep + 'lodash'); const resultTwo = await resolvePackageRoot(__dirname, 'memoizee'); - // memoizee also does not exist - expect(resultTwo).toBe(false); + expect(resultTwo).toContain(path.sep + 'memoizee'); }); }); From bca0a7108f3f125061b3968ab58a1d99895d13f4 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:36:01 -0500 Subject: [PATCH 16/17] Revert "Fix #414" This reverts commit b211b6ee970e58edb88d9feebb6506bd92e2c3cf. --- src/utils/layout-helpers.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/utils/layout-helpers.ts b/src/utils/layout-helpers.ts index 2012b4673..7c27acb5b 100644 --- a/src/utils/layout-helpers.ts +++ b/src/utils/layout-helpers.ts @@ -1,6 +1,5 @@ import * as memoize from 'memoizee'; import * as path from 'path'; -import * as childProcess from 'child_process'; import { CompletionItem, CompletionItemKind } from 'vscode-languageserver/node'; import { addToRegistry, normalizeMatchNaming } from './registry-api'; import { clean, coerce, valid } from 'semver'; @@ -120,26 +119,10 @@ export function getPodModulePrefix(root: string): string | null { return podModulePrefix.trim().length > 0 ? podModulePrefix : null; } -function getRoot(fromDir: string) { - try { - const topLevel = childProcess.execSync('git rev-parse --show-toplevel', { encoding: 'utf8', cwd: fromDir }); - - return topLevel.trim(); - } catch { - // we ignore the error, because we might not be in a git repo - return '/'; - } -} - export async function resolvePackageRoot(root: string, addonName: string, packagesFolder = 'node_modules'): Promise { const roots = root.split(path.sep); - const stopAt = getRoot(root); while (roots.length) { - if (roots.join(path.sep) === stopAt) { - return false; - } - const prefix = roots.join(path.sep); const maybePath = path.join(prefix, packagesFolder, addonName); const linkedPath = path.join(prefix, addonName); From eaa8f4817f916bdfe40cd0fda71af5866fc5c90e Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:38:10 -0500 Subject: [PATCH 17/17] Undo getEnv changes --- src/utils/logger.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/utils/logger.ts b/src/utils/logger.ts index d31c4081d..5274a7459 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -4,17 +4,17 @@ import { resolve } from 'path'; import { RemoteConsole } from 'vscode-languageserver/node'; import { fsProvider } from '../fs-provider'; -// function getEnv() { -// if (typeof process !== undefined) { -// return process.env; -// } else { -// return {}; -// } -// } +function getEnv() { + if (typeof process !== undefined) { + return process.env; + } else { + return {}; + } +} // Log debugging to the ELS package root, if possible // eslint-disable-next-line no-extra-boolean-cast -const debug = true; +const debug = !!getEnv().CI ? true : getEnv().ELS_DEBUG || false; const log_file = debug ? fsProvider().createWriteStream(resolve(__dirname, `../../debug.${process.pid}.log`), { flags: 'w' }) : null; let remoteConsole: RemoteConsole | null = null;