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

Project roadmap and ways others can contribute #161

Open
20 of 50 tasks
alangpierce opened this issue Jan 23, 2018 · 9 comments
Open
20 of 50 tasks

Project roadmap and ways others can contribute #161

alangpierce opened this issue Jan 23, 2018 · 9 comments

Comments

@alangpierce
Copy link
Owner

alangpierce commented Jan 23, 2018

I've had a few requests from others about how they can help out. I've been keeping a checklist of possible future tasks, and figured it would be good to make that public to make it easier for others to contribute.

Feel free to stop by and chat in the gitter room, which I just created. Generally my thought is that this can be a big checklist of possible things to do, and as work starts on them, they can be "promoted" to real issues with individual discussions/assignees/etc. I'm sure there's plenty of context to be shared about most of these issues, so feel free to chat if you want to take one of them on.

Currently, I'm focused on performance, and want to see how high I can get the N in "Sucrase is N times faster than Babel". But there's certainly lots of other stuff to do as well.

Stability

  • Add additional example projects that can be automatically tested. Ideally they would be large codebases in TypeScript/Flow/JSX and have fast test suites so they can be run as part of PR builds.
  • Find a way to integrate the Babylon test suite (and maybe other test suites like the TypeScript test suite and test262). I worry that there are some subtle edge cases around arrow function parsing that are missed right now.
  • Set up istanbul and make sure the tests have good code coverage.

Bugs

  • Complex assignments to mutable exports aren't handled correctly, e.g. export let x; [x] = arr;. Babel and TypeScript are both incomplete here, and ideally we just mimic one or the other.
  • TypeScript import pruning does not yet handle shadowed imported names.
  • import statements should be hoisted to the top of the file, but they aren't right now.
  • The line numbers almost always match the source line numbers, but that doesn't happen correctly for multiline class field initializers.
  • There are subtle differences between Babel and TypeScript around the transformed result of import and export. Ideally we exactly copy either Babel or TypeScript, based on the config.
  • Fix the disabled tests, which both crash the parser for TypeScript.
  • Class field computed names with side-effects should be evaluated only once instead of on each constructor invocation.
  • react-display-name has a case where it uses the filename to determine the display name for an anonymous export default React class. We could add a filename arg to the Sucrase API to support this, although it's maybe obscure enough that the API change isn't worth it.

Tooling

  • Figure out automated releases. semantic-release might work, but it's less clear since it's a monorepo (each integration is its own package).
  • Auto-publish the website on every release.
  • Set up Greenkeeper or similar.
  • See if Lerna would be useful. At the moment, the only packages other than the main sucrase package are small integrations, so Lerna may make less sense than for a larger project like Babel.
  • Enable noImplicitThis in the TypeScript config. Implicit any for this has bit me a few times, but there are enough pre-existing errors that it's hard to enable.
  • Figure out a good way for Sucrase to compile itself for production builds. At the moment, there's a self-build task, but we still use TypeScript for the real build.

Website (sucrase.io)

  • It would be sweet to have a "file a bug" button that you can click to automatically submit a bug report with the current code. Even better if it can notice that Sucrase crashed and Babel succeeded, or Sucrase produced invalid JS, and explicitly prompt you to file a bug.
  • Port the website to TypeScript or Flow and use Sucrase to build it.
  • I'm sure there are plenty of visual polish improvements that could be made.
  • Better handle Sucrase getting into an infinite loop, e.g. by adding a generous timeout (maybe 10 seconds) and restarting the web worker and giving a friendly error message.
  • Improve the initial load experience. The Monaco editor is a fairly large download, and the web worker is also huge (1.1MB compressed), possibly due to babel-standalone being big. One approach is to optimize the bundle size, and another approach is to degrade better, e.g. showing a dumb editor if Monaco isn't loaded and only running Sucrase if Babel isn't loaded.
  • Make the website more mobile-friendly.
  • I recently disabled service workers because they were causing cache issues, but it would be nice to re-enable them.

Profiling/benchmarking

  • Find a way to make performance numbers more stable, e.g. a dedicated server with nothing else running.
  • Come up with a more realistic benchmark to test on. The current one is a bit boring/repetitive and may not be representative of real code.
  • Explore other profiling techniques, like figuring out a way to measure the cost of function call overhead or other overhead, or looking at any JIT diagnostics provided by V8.
  • It would be awesome to have a part of the website showing historical performance data, similar to https://arewefastyet.com/ . For example, it could run the Babel and Sucrase build on every example project every day and show how they compare.

Features

  • Figure out if we need source maps, and implement them if so. The output line numbers are the same as the input line numbers, so theoretically they shouldn't be necessary at least for things like stack traces. (Spun off as Generate source maps #223.)
  • Support the TypeScript and Flow transforms with the imports transform disabled. (In other words, targeting ES modules).
  • Add a transform for object rest/spread. I was hoping to avoid this, but it looks like Webpack uses a parser that doesn't support it, so it may be necessary to implement for practical reasons. Also, MDN says it's not supported at all in Edge or Safari. (Note that webpack-object-rest-spread-plugin makes this work in Webpack, and object will probably be very hard to implement in nested cases, so probably this transform should be called out of scope.) I'm calling this out of scope.

Configuration

  • Allow Sucrase to read a tsconfig.json for its config (including which directories to compile and which to exclude, etc).
  • Allow Sucrase to read a .babelrc for its config.
  • Think more about how to simplify the configuration. One alternate approach is to specify the language being compiled (TS, TS+JSX, Flow, Flow+JSX, JSX) and the target (ESM, CJS) rather than a list of transforms. Much of this can be inferred from context, though, so ideally in most situations there can be zero configuration.
  • Another idea I've had is "Sucrase Surgeon", which goes into your node_modules directory and patches Babel/TypeScript to run Sucrase instead. It would be fragile, but it would let you try out Sucrase without needing to make any changes to your build config. It wouldn't make sense for real use, but it could lower the barrier to entry.

Integrations

  • Improve the current integrations (gulp, jest, require hook), e.g. providing better ways to specify options.
  • Add a Webpack loader.
  • Add a Rollup plugin.
  • Add a node ES module loader hook so it's possible to transform modules on-the-fly using native ES module support in node.
  • Any other integrations that seem useful.
  • See if there's a nicer way to share the build system across projects and manage publishing and versions.

Performance

  • Prototype a part of the project (probably the lexer) in AssemblyScript.
  • Prototype a part of the project in Rust, both compiled to WebAssembly and run natively.
  • See how practical it would be to write a custom WebAssembly code generator.
  • Further simplify the parser by removing code that we know isn't needed.
  • Dumb down all of the code so that it's more reasonable to run in WebAssembly (no classes or inheritance, no dynamic objects, etc.).
  • Find ways to skip unnecessary parsing details, e.g. skipping all tokens in curly braces when parsing a type.
  • Avoid backtracking in the parser, or find a way to make it more performant.
  • Consider using lower-level features, e.g. ints instead of strings to identify tokens, and mutating an array rather than building a string using +.
  • Consider simplifying the token format, e.g. making it so each type is just one full token.
@zerkalica
Copy link

  • d.ts and flow declarations generation
  • resolve import aliases. ts: baseUrl, paths from tsconfig.json support, flow: module.name_mapper from .flowconfig

@alangpierce
Copy link
Owner Author

@zerkalica

d.ts and flow declarations generation

Unfortunately I think this is too hard for Sucrase to do. I'm not 100% sure about flow declarations, but .d.ts generation is definitely out of scope.

As one example, d.ts generation relies on TypeScript's type inference algorithm, so this code:

export function foo() {
  return 3;
}

gets this type definition:

export declare function foo(): number;

Determining that 3 has type number maybe isn't conceptually so bad, but there are much more complicated cases (e.g. a variable that has a refined type due to previous control flow operations), and doing this right means that Sucrase would need to reimplement the whole TypeScript type system. TypeScript also infers types across files, and one design goal of Sucrase is that it can compile each file individually.

Sucrase is simple and fast because it just removes types and doesn't need to know the whole type system, so any features requiring type system knowledge or cross-file resolution are out of scope. The TypeScript support in Babel has the same limitations. Also, the main intended use case for Sucrase is development builds where iteration speed is important, and my understanding is the main use case for .d.ts files is production builds when shipping a library.

TypeScript recently added a new --emitDeclarationsOnly flag if you want to have Sucrase compile the JS and TypeScript compile the .d.ts files, so that's one way to get the build to work:
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#new---emitdeclarationsonly

resolve import aliases. ts: baseUrl, paths from tsconfig.json support, flow: module.name_mapper from .flowconfig

What's the use case for path resolution? Sucrase just takes import statements in and produces either the same import or an equivalent require. All path resolution should happen later, when running the code emitted by Sucrase. Again, it's just like Babel in that sense. To be clear, Sucrase just transforms code and doesn't do any typechecking; you need to run typechecking separately.

@zerkalica
Copy link

Path aliasing needed to avoid relative paths ../../ hell in libraries and applications above 1K SLOC.
Relative path is a big problem in js imports. There are many workarounds to fix it: tspath, path transform in awesome-typescript-loader, path transform in parcel-plugin-typescript. In babel environment people use babel-plugin-module-resolver.

@alangpierce
Copy link
Owner Author

@zerkalica I see, thanks for explaining. At my work, we have webpack and node set up to accept absolute paths, so it's not needed at transpile time. In webpack, it's the resolve.modules setting, and in node, it's NODE_PATH. But I guess NODE_PATH won't be supported in ES modules (still experimental in node 10, but maybe node 12 will have them), as I understand things, so I can see why you might prefer to resolve paths at transpile time.

I want to keep the project focused and avoid feature creep, so I'd rather hold off on the feature for now, but if there's more demand for it, I could see it being added in the future. Some more thoughts/concerns:

  • The feature would add a dependency on the filesystem. Currently Sucrase is just a string to string transform, which means it naturally runs in the browser and at least theoretically can run in WebAssembly. Adding synchronous file I/O for import resolution would add complexity and possibly hurt performance.
  • There's already code dealing with imports, so this feature would need to safely interact with it in all configurations. It wouldn't really be an isolated plugin, so it would add complexity to the project as a whole. Part of the reason Sucrase is possible is that it's tackling a relatively small problem (removing types, transforming JSX), so the complexity is manageable.
  • I could see a feature like this leading to a million configuration options, and I'd rather keep the project as low-configuration as possible.
  • It arguably goes against the project vision of handling common language extensions and letting them run in node or the browser. Unlike Babel, Sucrase isn't really meant to be a platform for doing arbitrary code transformations.

Possibly a way to get this working at transpile time would be a two-phase step where you run Sucrase and then a separate import-rewriting step. I've thought about pulling the Sucrase parser into its own package so that anyone could make a separate transform with a fast parser. It would be a little less efficient than ideal, though, since you'd need to re-parse after the first transform is done. In this case, though, I bet you could get away with more superficial parsing than Sucrase needs, like just using regexes or something.

@gleba
Copy link

gleba commented Jun 21, 2018

Yes, @alangpierce , these are the features I consider important for TypeScrip support.

I do not yet know if this is possible with sucrase.
But I will watch the topics in anticipation of examples of solving the problem.

@MaxGraey
Copy link

MaxGraey commented Jun 21, 2018

Also you should try prepack #269

@dsseng
Copy link

dsseng commented May 27, 2019

I'll try to implement stuff around reports

@dsseng
Copy link

dsseng commented May 27, 2019

Mobile support: #451

@Raynos
Copy link
Contributor

Raynos commented Feb 16, 2020

Allow Sucrase to read a tsconfig.json for its config (including which directories to compile and which to exclude, etc).

Implemented in #509

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

No branches or pull requests

6 participants