From 24f33e5b7bb2fa1b0c918af3e1ce0b2ca683f12f Mon Sep 17 00:00:00 2001 From: Suchita Doshi Date: Mon, 12 Apr 2021 13:40:29 -0700 Subject: [PATCH] feat: Support jump to definition from parent to child app and tests (#234) * failing parent defintion case * failing parent to child defintion case * update snapshot * add support for test definitions and parent to child imports * address comments * address comments --- .../core/script-definition-provider.ts | 31 +++++++++-- test/__snapshots__/integration-test.ts.snap | 48 +++++++++++++++++ test/integration-test.ts | 52 +++++++++++++++++++ 3 files changed, 127 insertions(+), 4 deletions(-) diff --git a/src/builtin-addons/core/script-definition-provider.ts b/src/builtin-addons/core/script-definition-provider.ts index 6e65ca35..8e2ac0e0 100644 --- a/src/builtin-addons/core/script-definition-provider.ts +++ b/src/builtin-addons/core/script-definition-provider.ts @@ -76,6 +76,11 @@ class PathResolvers { return joinPaths(...appParams); } + + resolveTestScopeImport(root: string, pathName: string) { + return joinPaths(path.join(root, pathName)); + } + muImportPaths(root: string, pathName: string) { const pathParts = pathName.split('/'); @@ -185,12 +190,30 @@ export default class CoreScriptDefinitionProvider { } else if (isImportSpecifier(astPath)) { logInfo(`Handle script import for Project "${project.name}"`); const pathName: string = ((astPath.parentFromLevel(2) as unknown) as t.ImportDeclaration).source.value; + const pathParts = pathName.split('/'); + let maybeAppName = pathParts.shift(); - project.roots.forEach((projectRoot) => { - const potentialPaths = this.guessPathForImport(projectRoot, uri, pathName) || []; + if (maybeAppName && maybeAppName.startsWith('@')) { + maybeAppName = maybeAppName + '/' + pathParts.shift(); + } - definitions = definitions.concat(potentialPaths); - }); + let potentialPaths: Location[]; + const addonInfo = project.addonsMeta.find(({ name }) => pathName.startsWith(name + '/tests')); + + // If the start of the pathname is same as the project name, then use that as the root. + if (project.name === maybeAppName && pathName.startsWith(project.name + '/tests')) { + const importPaths = this.resolvers.resolveTestScopeImport(project.root, pathParts.join(path.sep)); + + potentialPaths = pathsToLocations(...importPaths); + } else if (addonInfo) { + const importPaths = this.resolvers.resolveTestScopeImport(addonInfo.root, pathName); + + potentialPaths = pathsToLocations(...importPaths); + } else { + potentialPaths = this.guessPathForImport(project.root, uri, pathName) || []; + } + + definitions = definitions.concat(potentialPaths); } else if (isServiceInjection(astPath)) { let serviceName = ((astPath.node as unknown) as t.Identifier).name; const args = astPath.parent.value.arguments; diff --git a/test/__snapshots__/integration-test.ts.snap b/test/__snapshots__/integration-test.ts.snap index 12d7d1a2..7a9a0cb6 100644 --- a/test/__snapshots__/integration-test.ts.snap +++ b/test/__snapshots__/integration-test.ts.snap @@ -2798,6 +2798,54 @@ Object { } `; +exports[`integration 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", + }, + Object { + "name": "foo", + "root": "../lib/foo", + }, + ], + "registry": Object { + "component": Object { + "bar": Array [ + "../lib/foo/addon/components/bar.js", + "lib/biz/addon/components/bar.js", + ], + }, + "helper": Object { + "blah": Array [ + "tests/helpers/blah.js", + ], + }, + "routePath": Object { + "hello": Array [ + "app/templates/hello.hbs", + ], + }, + }, + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/tests/helpers/blah.js", + }, + ], +} +`; + exports[`integration autocomplete works for angle component slots 1`] = ` Array [ Object { diff --git a/test/integration-test.ts b/test/integration-test.ts index 4d2d8045..9ea10a31 100644 --- a/test/integration-test.ts +++ b/test/integration-test.ts @@ -1591,6 +1591,58 @@ describe('integration', function () { expect(result.length).toBe(2); expect(result[0].response.length).toBe(3); }); + + it('support parent project addon calling child project', async () => { + const result = await getResult( + DefinitionRequest.method, + connection, + { + 'full-project': { + 'app/templates/hello.hbs': '', + 'tests/helpers/blah.js': '', + lib: { + biz: { + 'addon/components/bar.js': '', + 'package.json': JSON.stringify({ + name: 'biz', + keywords: ['ember-addon'], + dependencies: {}, + 'ember-addon': { + paths: ['../../../lib/foo'], + }, + }), + 'index.js': '', + }, + }, + 'package.json': JSON.stringify({ + name: 'full-project', + dependencies: { 'ember-holy-futuristic-template-namespacing-batman': '^1.0.2' }, + 'ember-addon': { + paths: ['lib/biz'], + }, + }), + }, + lib: { + foo: { + addon: { + 'components/bar.js': 'import Blah from "full-project/tests/helpers/blah"', + }, + 'package.json': JSON.stringify({ + name: 'foo', + keywords: ['ember-addon'], + dependencies: {}, + }), + 'index.js': '', + }, + }, + }, + 'lib/foo/addon/components/bar.js', + { line: 0, character: 8 }, + 'full-project' + ); + + expect(result).toMatchSnapshot(); + }); }); describe('Autocomplete works for broken templates', () => {