Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: rewrite TS dependency analysis in Rust #5029

Merged
merged 55 commits into from
May 18, 2020

Conversation

bartlomieju
Copy link
Member

@bartlomieju bartlomieju commented May 1, 2020

This PR completely overhauls how module analysis is performed in TS compiler by moving the logic to Rust.

In the current setup module analysis is performed using ts.preProcessFile API in a special TS compiler worker running on a separate thread.

ts.preProcessFile allowed us to build a lot of functionality in CLI including X-TypeScript-Types header support and @deno-types directive support. Unfortunately at the same time complexity of the ops required to perform supporting tasks exploded and caused some hidden permission escapes.

This PR introduces ModuleGraphLoader which can parse source and load recursively all dependent source files; as well as declaration files. All dependencies used in TS compiler and now fetched and collected upfront in Rust before spinning up TS compiler.

To achieve feature parity with existing APIs this PR includes a lot of changes:

  • add ModuleGraphLoader
    • can fetch local and remote sources
    • parses source code using SWC and extracts imports, exports, file references, special headers
    • this struct inherited all of the hidden complexity and cruft from TS version and requires several follow up PRs
  • rewrite cli/tsc.rs to perform module analysis upfront and send all required source code to TS worker in one message
  • remove op_resolve_modules and op_fetch_source_files from cli/ops/compiler.rs
  • run TS worker on the same thread

@bartlomieju
Copy link
Member Author

@kitsonk I won't be able to finish this PR tonight 😞 feel free to pick up

@bartlomieju
Copy link
Member Author

bartlomieju commented May 6, 2020

Note to self:

  • ModuleGraph should be extended to understand X-TypeScript-Types headers as well as type, reference and lib directives (/// <reference lib="dom">)
  • ModuleGraphSourceFile should utilize these directives to download references files
  • ModuleGraphSourceFile should contain following fields:
    • imports - additionally there should be annotation if import has respective type declaration based on X-TypeScript-Types header or // @deno-types="./foo.d.ts" directive above import statement
    • lib_references_directives
    • type_reference_directives
    • referenced_files
  • After ModuleGraph is constructed and all sources loaded it should be possible to JSON-serialize the graph with all of it sources so it can be fed into TS compiler in a single message
  • It would be best if ModuleGraph could collect information about module resolution so TS compiler doesn't need op_resolve_modules - it's only used to build cache of resolved specifier for unresolved specifier/referrer pair (so import could be described something like { specifier: "./foo.ts", resolvedSpecifier: "file://a/b/c/foo.ts, type: "sourceFile" }).

@bartlomieju bartlomieju force-pushed the module_graph branch 7 times, most recently from 9b4ab2f to 691715a Compare May 11, 2020 00:17
@bartlomieju
Copy link
Member Author

I thought that after this change running target/release/deno info https://deno.land/x/oak/mod.ts would not invoke the TS compiler, but I still see Compile https://deno.land/x/oak/mod.ts output.

I did not change deno info in this PR - it's still wired to TS compiler. I'll see to that in follow up PRs this one is big enough.

Copy link
Member

@ry ry left a comment

Choose a reason for hiding this comment

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

LGTM - better to land this - it's clearly an improvement. I'm not super happy with the memory:// stuff, but that can be dealt with later.

@kitsonk
Copy link
Contributor

kitsonk commented May 18, 2020

FYI, I've got time to do a proper review right now. Hopefully a few minutes.

Copy link
Contributor

@kitsonk kitsonk left a comment

Choose a reason for hiding this comment

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

A few comments but LGTM, though hard to fully tell, but certainly something that can be iterated on.

One thing that is/was happening that we need to check after this is that sometimes we would have a miss with dynamic imports that TypeScript would get, but ts.preProcessFile() would miss... like:

const spec = "./foo.ts";
const foo = await import(spec);

I don't know if this catches those or not. If not, we still need an op for TypeScript to pull in something we couldn't statically identify upfront.

cli/js/compiler.ts Outdated Show resolved Hide resolved
cli/js/compiler.ts Show resolved Hide resolved
@@ -1327,7 +1184,6 @@ async function compile(
rootNames,
options,
host,
oldProgram: TS_SNAPSHOT_PROGRAM,
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 as I said above, we should document why we have an unused const above.

cli/js/compiler.ts Outdated Show resolved Hide resolved
cli/js/compiler.ts Outdated Show resolved Hide resolved
spec
} else {
ModuleSpecifier::resolve_url(&format!("memory://{}", specifier))?
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Hrmmmm... yeah, don't like this easier, but yeah... maybe a TODO so we remember to revisit.

pub fn init(i: &mut CoreIsolate, _s: &State) {
let custom_assets = std::collections::HashMap::new();
// TODO(ry) use None.
// TODO(bartlomieju): is this op even required?
Copy link
Contributor

Choose a reason for hiding this comment

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

It could not be required... but it means we need to catch these dependencies when using lib with the runtime compiler APIs.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure I understand, I think if we loaded all available libs during snapshot it could be removed

Copy link
Contributor

Choose a reason for hiding this comment

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

oh, yeah I forgot we did that...

@bartlomieju
Copy link
Member Author

One thing that is/was happening that we need to check after this is that sometimes we would have a miss with dynamic imports that TypeScript would get, but ts.preProcessFile() would miss... like:

const spec = "./foo.ts";
const foo = await import(spec);

I don't know if this catches those or not. If not, we still need an op for TypeScript to pull in something we couldn't statically identify upfront.

So in my PR dynamic imports are only parsed in case of bundle, and only those dynamic imports that look like import("./some/specifier.ts"). What you shown above it probably doable but it won't work after this PR.

@garronej
Copy link
Contributor

This is the commit that breaks EVT.

bartlomieju added a commit that referenced this pull request May 22, 2020
This commit fixes a bug introduced in #5029 that caused bad 
handling of redirects during module analysis. 

Also ensured that duplicate modules are not downloaded.
@ry ry mentioned this pull request May 25, 2020
6 tasks
bartlomieju added a commit that referenced this pull request May 29, 2020
This PR addresses many problems with module graph loading
introduced in #5029, as well as many long standing issues.

"ModuleGraphLoader" has been wired to "ModuleLoader" implemented
on "State" - that means that dependency analysis and fetching is done
before spinning up TS compiler worker.

Basic dependency tracking for TS compilation has been implemented.

Errors caused by import statements are now annotated with import
location.

Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
bartlomieju added a commit to bartlomieju/deno that referenced this pull request Jun 5, 2020
This PR addresses many problems with module graph loading
introduced in denoland#5029, as well as many long standing issues.

"ModuleGraphLoader" has been wired to "ModuleLoader" implemented
on "State" - that means that dependency analysis and fetching is done
before spinning up TS compiler worker.

Basic dependency tracking for TS compilation has been implemented.

Errors caused by import statements are now annotated with import
location.

Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cli related to cli/ dir perf performance related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants