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

What do we call this command-line option? #2715

Closed
RyanCavanaugh opened this issue Apr 10, 2015 · 46 comments
Closed

What do we call this command-line option? #2715

RyanCavanaugh opened this issue Apr 10, 2015 · 46 comments
Labels
Discussion Issues which may not have code impact

Comments

@RyanCavanaugh
Copy link
Member

Consider two TypeScript files:
_a.ts_

module M {
  export var t = 5;
}

_b.ts_

var t = 'foo';
module M {
  var s = t; // <-- what does this mean?
}

If we compile b.ts on its own, the t in var s = t refers to the outer var t, a string. The emit for this line is simply var s = t;.

If we compile b.ts along with a.ts, the t in var s = t refers to M.t, the exported variable of type number. The emit for this line is var s = M.t.

This behavior, and a few other language features (e.g. const enum), prevent TypeScript from being able to correctly operate on a single file without understanding the entire universe of files involved in the program. This has performance implications for features like compile-on-save, especially in larger projects, and blocks scenarios like on-the-fly compilation in module loaders.

We'd really prefer to be able to emit a file without first understanding the entire program it's a part of. The language as-is prevents this. However, by slightly subsetting the language to disallow things that require whole-program binding/typechecking, we can re-enable those scenarios. People can "opt in" their project to this language subset as a trade-off.

Tentatively, we're calling the prongs of this fork "whole program mode" and "single file mode". Naming TBD.

A key part of this is that you need to be able to compile e.g. a.ts and b.ts above in a mode that says "This code will not work in single-file-mode", e.g.:

 > tsc --warnMeIfThisIsNotSafeInSingleFileMode a.ts b.ts
TS1234 (b.ts line 2): Modules may not be re-opened in single-file mode

We need some help naming these things, especially the commandline flag. The current list of ideas on the whiteboard, of which nobody likes anything, consists of the following 😦

  • separateCompile
  • singleFileEmit
  • disableGlobalOptimizations
  • safeForTranspile
  • safeForSyntacticTranspilation
  • separate
  • isolated
  • safeTransform
  • singleFileScope
  • crossFileSafe
  • crossFileRestrictions

Please help 🚲 🏠!

Ref #2499

@RyanCavanaugh RyanCavanaugh added the Discussion Issues which may not have code impact label Apr 10, 2015
@JamesKovacs
Copy link

If you're in single-file mode, how do you even know that the module, M, in b.ts is being re-opened?

@RyanCavanaugh
Copy link
Member Author

That's the tricky thing -- this isn't a flag that turns on single-file mode. It's a flag that says "Compile this normally, but error if you do anything that doesn't work in single-file mode"

@yahiko00
Copy link

Just in order to be sure, in such a "single-file-mode", does this following file compile?

b.ts

module M {
  export var t = 5;
}

@Steve-Fenton
Copy link

Maybe --segregated, although --singleFileMode seems to say what it does. Maybe put them on the whiteboard until you hate them more than your original ideas :)

@RyanCavanaugh
Copy link
Member Author

Just in order to be sure, in such a "single-file-mode", does this following file compile?

If a.ts is part of the compilation, no. If a.ts is not part of the compilation, then yes.

@yahiko00
Copy link

So, this is not as "single-file" as it would mean... ^_^
Good luck finding an accurate name!

EDIT: I would suggest something like --localBinding

@JamesKovacs
Copy link

So what you're really doing is adding linting flags or increasing the warnings level to disallow certain unsafe constructs. Is tsc.exe the right place to put this functionality as it has nothing to do with compiling and a lot to do with whether other tools can make assumptions about potentially unsafe constructs?

@matijagrcic
Copy link

-singularCompile, -individualCompile, -bounded Compile (bounded context pattern)

@yahiko00
Copy link

Why not --preventConflict since this is the goal of this option.

@RyanCavanaugh
Copy link
Member Author

Is tsc.exe the right place to put this functionality as it has nothing to do with compiling

The entire point of this flag is to enable the compilation of files without examining their dependencies. 99% of the code to do this checking is in tsc already. A hypothetical external tool to do this checking would start with the tsc.js code and make about five edits. So yes, tsc.exe is absolutely the right place for this.

Why not --preventConflict since this is the goal of this option.

This isn't about conflicts. References outside the declaring file to const enums, for example, are disallowed under this flag because they require inlining, but the compiler doesn't know what value to inline without having that other file available.

@Steve-Fenton
Copy link

Maybe --noDependencies or the team suggestion --isolated. Isolated seems to describe the kind of restrictions you may encounter.

@yahiko00
Copy link

@RyanCavanaugh Thanks for your explanations. You know specs better :p

@Steve-Fenton --noDependencies should go on the whiteboard

@JamesKovacs
Copy link

I'm confused. You want to compile single files without worrying about "the world", but you want to provide warnings about re-opening modules, which you can only do by looking at the world. What am I missing here?

@RyanCavanaugh
Copy link
Member Author

which you can only do by looking at the world. What am I missing here?

This isn't a flag that says "ignore the world". It's a flag that says "Compile this normally, but error if you do anything that doesn't work if you are not allowed to look at the world"

@yahiko00
Copy link

In this mode, we are not allowed to call a function defined in another file, are we?

@RyanCavanaugh
Copy link
Member Author

This could be seen as a kind of "strict compilation"

The word "strict" is basically reserved for any "use strict" stuff, especially since many ES6 constructs force their enclosing block to be in strict mode.

In this mode, we are not allowed to call a function defined in another file, aren't we?

It's totally allowed. The only things that would be disallowed are:

  • Re-opening an internal module (see posted example)
  • Using a const enum from another file (since we can't inline them without opening another file)
  • Possibly some edge-case stuff around async/await and the spread operator

@yahiko00
Copy link

Since things that are disallowed are quite special cases, you should maybe consider a term with a vague, broad meaning like --safeCompilation.
This way, you would be able to add/remove stuffs enclosed in this option in the future

@jlanng
Copy link

jlanng commented Apr 10, 2015

--standalone

@Jaykul
Copy link

Jaykul commented Apr 11, 2015

I don't like the "safeFor" stuff, I think names like that are leading to the confusion in the thread -- you're not changing the compilation, you're just adding warnings to the output. How about a flag named a bit more to the point, like: warnForSingleFileUse (or just warnForSingleFile) or even warnOnCrossFileDependencies (or just warnMultiFileSyntax)

@vladima
Copy link
Contributor

vladima commented Apr 11, 2015

What about making it not a boolean flag but rather a two valued enum, something like:
enum CompilationUnit {program, individialFile} so in command line it would be:

 -compileAs program // default one
- compileAs individualFile

@yahiko00
Copy link

@Jaykul If we refer to the cases given by @RyanCavanaugh above, I think any term including "singleFile" or "dependency" a bit too strong since all dependencies are not disallowed but only few of them. Also the compilation would look at the project scope for resolving "simple" dependencies like functions, Thus, the compilation will not be restricted to a "single file", a misleading term I think.

@Jaykul
Copy link

Jaykul commented Apr 11, 2015

@yahiko00 -- have I misunderstood? The idea is a flag which would be passed when compiling all the files which will warn you about stuff that would prevent you from getting the exact same result if you compiled individual files.

That is: warn me if there's anything that will break if I compile individual files one at a time.

Thus, warnForSingleFileUse -- since the only reason you'd care about these warnings is if you want to be able to to compile single files.

@stephanedr
Copy link

What about something like warnEmitDependencies (warn if the emitted JS of a TS file depends on other TS files).

@Steve-Fenton
Copy link

Maybe the flag could be the answer to the question "why would I use this flag?", for example if it is to allow faster single-file compilation, it could be quickLimitedCompile, for example.

@basarat
Copy link
Contributor

basarat commented Apr 13, 2015

Sidetrack: such issues don't exist when only using external modules right ?

@basarat
Copy link
Contributor

basarat commented Apr 13, 2015

Perhaps it's implicit on if module is specified
Implicit off if people use --out.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 13, 2015

@basarat there are still the const enum from an ambient declaration that needs to be flagged here.

@basarat
Copy link
Contributor

basarat commented Apr 13, 2015

@mhegazy can you give me an example of what changes in emit of a file depending on a constant enum in another file.

My follow up question will be "does anything bad happen if we don't do this different emit"

@Steve-Fenton
Copy link

The use of the const enum is replaced by the value (i.e. the number is inlined).

@mhegazy
Copy link
Contributor

mhegazy commented Apr 13, 2015

@basarat emit is not conditioned on the flag. the differences is you will get an error if you use the flag with a full compilation. the example is as follows:

// a.d.ts 
declare const enum E {
   a = 0
}
// b.ts
var x = E.a;

in full program compilation mode:

// b.js 

var x = 0 /* E.a */;

in single file emit:

// b.js 

var x = E.a;

The problem is at single file emit, we have no way to know that E is a const enum, we only emit it as a property access. this might work if a.d.ts was built using ---pereserveConstEnum but will fail with reference error otherwise. The flag will warn you about these cases to ensure your program is safe for single file compilation.

you can see the implementation in PR #2550.

@basarat
Copy link
Contributor

basarat commented Apr 14, 2015

@mhegazy With this new flag, What would be the emit for:

// b.ts
var x = E.a;

(assume E is declared as a const enum but of course with this flag you cannot look at that fact).

@mhegazy
Copy link
Contributor

mhegazy commented Apr 14, 2015

The emit would be the same.

The flag is not "emit one file at a time" it is "tell me what breaks if I build one file at a time"

@basarat
Copy link
Contributor

basarat commented Apr 14, 2015

@mhegazy If I have one file a.ts (the one that I am compiling):

module M {
  var s = t;
}

And another file we can't look at:

module M {
  export var t = 5;
}

And yet another we can't look at:

var t = 5;

Would single compiling a.ts still be considered an error?

@mhegazy
Copy link
Contributor

mhegazy commented Apr 14, 2015

The current behavior is no.

But if you call ts.transpile it will give u an error because a.ts is not an external module.

Would u propose to make that an error in the command line case?

@basarat
Copy link
Contributor

basarat commented Apr 14, 2015

Would u propose to make that an error in the command line case

No.

With that answer, why can't we get people to just use external modules everywhere? Are we making our lives difficult getting --out to work efficiently? Its not going to be safe for cases like :

module M {
  var s = t;
}

Which is essentially anything that's ambient right?


rant.
Because there is no good answer I would suggest that instead of this feature ... we do a different feature:

  • only support isolated compile for files that have external modules
  • Assume any ambient stuff to be valid (this might be coming from lib.d.ts or coming from node.d.ts or something else.... the point is we don't know, its something the build server can sort out)
  • Assume any import to be valid.

But I guess this is exactly what ts.transpile means?

I don't like out

/rant

@mhegazy
Copy link
Contributor

mhegazy commented Apr 14, 2015

But I guess this is exactly what ts.transpile means?

yes :D, that is where we landed. that still leaves you with the const enum from an ambient declaration issue..

@basarat
Copy link
Contributor

basarat commented Apr 14, 2015

that still leaves you with the const enum from an ambient declaration issue..

Got it. We want to expose ts.transpile on some command line flag. Implicit with this flag is:

  • you have an external module loader, i.e. no --out.
  • preserveContantEnums: true
  • Any thing not declared in the file explicitly is assumed to be any.
  • Any import is any

Reason for no --out : you don't want the build server output to be different from any local fast compile.

@basarat
Copy link
Contributor

basarat commented Apr 14, 2015

Again my feature suggestion:

noExternalLookups

locally:

tsc --preserveConstantEnums --module commonjs --noExternalLookups a.ts b.ts

build server

tsc --preserveConstantEnums --module commonjs a.ts b.ts


@mhegazy can you provide a local vs. build server flags split with the proposed (call it isolated) flag? Or am I still understanding it wrong?

@mhegazy
Copy link
Contributor

mhegazy commented Apr 14, 2015

@basarat i was not thinking of the server/local scenario, but that should work as well.. here is the scenario i had in mind.

you are using something like JSPM, or es6-module-loader (with TypeScript support), system.js enabled website with TypeScript as a transpiler ..etc.. in these work flows, there are no build step. transpilation is happening on the fly, no errors reported from the compiler, only runtime errors from your browser. i.e. the compiler is called through ts.transpile..

now, you would have some tooling, e.g. VS, atom, sublime texts, etc.. you would set the flag ---noExternalLookups in your tsconfig.json, and you will get some red squiggles under const enums use sites, for instance. if you fix all these errors in your editor, you are guaranteed you will get no unexpected runtime-errors when you hit F5 in your browser, or deploy your jspm package.

the same can be said in a workflow like atom plugins, in your editor/local build you would use the flag, then deploy to atom and that will grantee that it is safe to transpile using typescript-simple.

@basarat
Copy link
Contributor

basarat commented Apr 14, 2015

here is the scenario i had in mind

@mhegazy That scenario can be served by:

  • enabled website with TypeScript as a transpiler : taken care of by ts.transpile
  • you would have some tooling, e.g. VS, atom, sublime texts, etc : The user uses external modules and has --preserveConstantEnum:true.

This way any errors in the tooling are errors at runtime otherwise runtime will work fine.

Does that not cater for it, or do we still needs this flag?

@mhegazy
Copy link
Contributor

mhegazy commented Apr 15, 2015

even with --perseveContantEnum, if you are consuming a const enum from another .d.ts that was not built with --perserveConstantEnum, it is going to fail at runtime.

@basarat
Copy link
Contributor

basarat commented Apr 15, 2015

Got it. Effectively this new flag is for IDE's to:

  • Warn on const enum usage
  • Warn on reopening internal module usage
  • others later ... hopefully not.

With the purpose of safe for per file transpilation.

So : checkSafeForTranspile.

@mhegazy
Copy link
Contributor

mhegazy commented Jun 29, 2015

for future references, the flag name is --isolatedModules

@altano
Copy link

altano commented Mar 6, 2016

I noticed the autocomplete in VSCode for my tsconfig included the isolatedModules option. The current documentation is "Unconditionally emit imports for unresolved files." This didn't mean anything to me so I searched around and found this conversation, which sounds completely different than the definition in the docs. Can I get some clarification on what this option does?

@mhegazy
Copy link
Contributor

mhegazy commented Mar 7, 2016

I makes the compiler behave as if you called ts.transpile (#2499) on each file sparely.

There are some rules in place, i.e. const enums are always preserved, no namespaces, and the emitter will emit some checks when inlining types for decorators.

if you are not using ts.transpile to transpile your file one at a time, you do not need to care about this flag.

@dazinator
Copy link

dazinator commented Jan 4, 2017

@mhegazy +1 for updating the current docs for this argument, with a better explanation or a link to a more detailed explanation like the one you have just given! :-)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Discussion Issues which may not have code impact
Projects
None yet
Development

No branches or pull requests