Skip to content

Add option to fully resolve paths, making compiled code ECMAScript Module-ready - #31

Merged
justkey007 merged 2 commits into
justkey007:masterfrom
bscotch:feature/module-resolution
Jun 6, 2021
Merged

justkey007 merged 2 commits into
justkey007:masterfrom
bscotch:feature/module-resolution

Conversation

@adam-coster

@adam-coster adam-coster commented Jun 5, 2021 •

Copy link
Copy Markdown
Contributor

Typescript configs allow for both path aliases and node-style module resolution, while not fully supporting either on compile due to not modifying import paths.

tsc-alias handles the path aliases part of this problem, but not the overlapping module-resolution problem.

When using ECMAScript modules (ESM) in Node, being able to write in Typescript without full resolution is nice but the compiled JavaScript cannot run because ESM requires full paths.

The new feature attempts to find all import paths and then attempts to resolve them to an existing JavaScript file. If found, it replaces the original with the fully-resolved path.

While this could be done as a standalone tool, being able to use a single tool to resolve path issues in tsc-compiled code would be useful.

Additional changes:

  • Refactor of the test suite to make room for alternative test commands
  • Addition of tests for import-path-matching regexes
  • Addition of a class and new import-path-matching regex allowing for a single regex to match all styles of import
  • Cloned 3 of the test projects and changed their tsc-alias call to include the new --resolve-full-paths flag, so that there are test cases for this new feature.

The 2nd commit removes the two-regex matching used by the alias resolver and replaces it with the single-regex pattern. The new pattern does not exactly match the use of the original two patterns, but the new pattern successfully matches all test cases so it should not create surprises.

Comment thread src/index.ts
import { Output } from './utils';
import { Output, resolveFullImportPaths } from './utils';

export interface ReplaceTscAliasPathsOptions {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the options into an exported interface to make it accessible to API consumers.

Comment thread src/index.ts
};

const replaceAlias = (file: string): boolean => {
const replaceAlias = (file: string, resolveFullPath?: boolean): boolean => {

@adam-coster adam-coster Jun 5, 2021 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since fullpath resolution is an overlapping problem with alias resolution, I felt like it was reasonable to add it here. It feels a bit like a side effect, but a rename of the function (e.g. to resolveImporPaths) would make it less weird.

I considered doing it separately from this function, but that would require an additional save to disk. Probably wouldn't really matter.

Also, I added the parameter to make this function more portable in case it gets moved into the outer scope at some point. If it's intended to be kept in the inner scope, the parameter could be removed and the reference changed to a direct reference to options?.resolveFullPath.

Comment thread src/index.ts

// Fully resolve all import paths (not just aliased ones)
// *after* the aliases are resolved
if (resolveFullPath) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where the new resolver gets called, if the option is supplied.

// Separate patterns for each style of import statement,
// wrapped in non-capturing groups,
// so that they can be strung together in one big pattern.
const funcStyle = `(?:\\b(?:import|require)\\s*\\(\\s*${importString}\\s*\\))`;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I merged the original two-pattern import-statement regexes into one, and created a class to make repeated use of that new pattern easier.

The new pattern closely matches the original two in behavior, but adds some additional constraints.

* Replace all source import paths, using a replacer
* function (a la `String.prototype.replace(globalRegex,replacer)`)
*/
replaceSourceImportPaths(replacer: StringReplacer) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is essentially what the code was originally doing to resolve aliases, as a method that allows the dev to not have to also provide the regex.

Comment thread tests/test.spec.ts
* valid statements are incrementing numbers, starting from 0,
* so that it is easy to verify validity of results.
*/
const sampleImportStatements = `

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New test cases can easily be added here. Currently it's assumed that the valid ones will use numeric characters for the import paths, so that the valid "paths" can be automatically pulled out and tested against.

Comment thread tests/test.spec.ts
`;

function runTestProject(projectNumber: number) {
const projectDir = join(projectsRoot, `project${projectNumber}`);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally pulled this out into its own function because I misunderstood how the test projects worked, and wanted to be able to run this in different contexts.

Comment thread tests/test.spec.ts
}

it(`Import regex matches import statements`, () => {
const expectedImportPaths = sampleImportStatements.match(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where I use the assumption that all the valid import statements in the sample have only digits in their import strings.

Comment thread tests/test.spec.ts
});
});

// Run tests on projects. 9-11 are for testing fullpath file resolution

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manual review of the resulting files for the new 9-11 projects (which use the new flag) gave the expected results. I'm not sure what the coverage of import statement cases is with these tests, but between the direct testing of the regex used and these tests to ensure that paths can be fully resolved, it should be sufficient coverage.

@justkey007
justkey007 merged commit 90f8d9b into justkey007:master Jun 6, 2021
@justkey007

Copy link
Copy Markdown
Owner

You did a great job, thanks for the detailed explanations

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants