Open RyanCavanaugh opened 6 years ago
A couple of things:
1) watch doesn't currently output an initial build. It waits until the first file change. This differs to non build mode. 2) How is build order determined? I found I had to order the project references in dependency order to get the build to complete first time without error
@timfish thanks! Re 1 - PR up at #25610. For the other issue, can you sketch out the files you have? The order is supposed to be a simple topological sort that should always yield a correct ordering. I'll try to repro locally in the meantime based on what you've described
Edit: Not sure how to repro - I changed the TypeScript repo's src/tsconfig.json
to be in effectively random order and it still figured out a valid build order. Will need details on this.
@RyanCavanaugh Were you all able to land the cross-project rename for the RC?
@EisenbergEffect we have "upstream" renames (renaming a usage in client
affects declarations in shared
) but not "downstream" (renames in shared
affecting uses in server
and client
) yet
Gotcha. Any ideas is you'll be able to get the downstream rename in for the final release? or have you already determined that it needs to push out to 3.1 or beyond?
It looks like we can probably get downstream renames for loaded projects in by 3.0 final. Detecting which other projects on your computer need to be loaded to do an "exhaustive downstream rename" is looking dicey - we haven't found any mechanisms that would let us short-circuit that work when the symbol being renamed is exported from the current file.
I am using the API to build Typescript projects, including createWatchProgram and createWatchCompilerHost. 1) Are these updated to use the new project references and 2) do I need to do anything else in my code other than update the tsconfig.json?
If you're hosting the compiler API, you don't need to do anything new for project references; everything should work as-is. If you want to take advantage of the new --build
mode features, the entry point is createSolutionBuilder
and you'll need to provide a BuildHost
and CompilerHost
along with the file timestamp APIs on the latter.
To migrate to using project references in your code itself, updating your tsconfig.json
to a) add references and b) add composite: true
may be sufficient - if not, you'll see errors informing you what else needs to happen. Writing a comprehensive migration guide has been a difficult task because project setups are so varied - I'm hoping we can find migrations from early adopters as something to point people to, but it's hard to give guidance without seeing specific build layouts.
Thanks @RyanCavanaugh
Are there any examples for "createSolutionBuilder" and "timestamp API"?
@RyanCavanaugh ref: Build order.
I'll try to get a repo reproducing this although it won't be until next week.
If I'm just adding all references to a root tsconfig, it should work out the dependency tree from that?
@newtack the compiler source code itself (src/compiler/sys.ts
for timestamp APIs and src/compiler/tsc.ts
& tsbuild.ts
) are good references.
@timfish
If I'm just adding all references to a root tsconfig, it should work out the dependency tree from that?
Correct
Trying this out with the 3.0 RC.
declarationMaps We've also added support for declaration source maps. If you enable --declarationMap, you'll be able to use editor features like "Go to Definition" and Rename to transparently navigate and edit code across project boundaries in supported editors.
I tried using this but for some reason "Go to Definition" didn't seem to work. I'm probably doing something wrong, but filed an issue with a minimal reproduction case here: #25662
Will this also handle "Find All References"? If not, is there any way that could be supported?
Based on @RyanCavanaugh and @rosskevin's work to demonstrate Project References within a Lerna-based monorepo123, I've made another not-for-merging Pull Request which uses both Yarn workspaces and Babel 7: https://github.com/RyanCavanaugh/learn-a/pull/4
Hopefully this example helps others learn! I've been working on a Lerna-based monorepo similar to this, and now (I think) I have a better idea of how to configure Project References with it.
—
I was hoping to get some advice on how to handle path resolutions. My directory currently looks something like this:
- client/
- assets/
- views/
- src/
- tsconfig.json
- webpack.config.js
- shared/
- src/
- tsconfig.json
- server/
- src/
- tsconfig.json
Im struggling to reference the shared
directory. I was surprised to learn that the "paths" section along with baseURL doesn't allow for path aliasing like webpack does. So now I'm not sure how to reference the shared directory at all.
For example, the following doesn't work:
// server/src/index.ts
import { mySharedFunc } from "../../shared/src"
// This doesn't work either. "shared" is still shared after compilation
import { mySharedFunc } from "shared"
Because the project compiles down to:
- dist/
- client/
- bundle.js
- shared/
- index.js
- server/
- index.js
As you can see, it is invalid to reference the src dir as it doesn't exist after compilation.
Should I restructure my repo somehow? Is it possible to get my path aliasing to work?
My struggles appear to be related to #25682
I've updated my folder structure to have client
, server
, and shared
under one src
directory. This resolved my issue.
@RyanCavanaugh any luck with my code? https://github.com/Microsoft/TypeScript/issues/3469#issuecomment-403978372
I assumed that with "composite": true
, "declaration": true
would always be forced. However this only seems to apply with tsc --build
.
I found this out while doing a normal build (tsc
) and not seeing expected definition files.
Is this expected behaviour? It seems counter-intuitive to me.
I found myself using the same technique as the references system in our temporary custom incremental build system (comparing d.ts files) to decide what to rebuild in a non incremental scenario.
However, after finding strange results, I've discovered that the typescript compiler (3.0-RC) do NOT generate deterministically d.ts files. Indeed, when Unions are involded, the displayed order of those seems to depend on the nodeJS run, which makes to system detect false positives.
ex: { status: "400"; body: KoStatus; } | { status: "200"; body: OkStatus; }
vs:
{ status: "200"; body: OkStatus; } | { status: "400"; body: KoStatus; }
I think you may have found yourselves those issues and wonder if that's a know/accepted limitation between runs or if I should open an issue (have not found an existing one yet).
Trying it out on the codebase I'm working on, I ran into a lot of Output file '.../_.d.ts' has not been built from source file '.../_.ts'
errors. I managed to strip down a repro to almost nothing, and found that when rootDir
and outDir
are involved as well as a project reference, source files within an individual project that has a dependency project would be compiled in alphabetical order.
The consequence is that an A.ts
that imports from B.ts
will fail with Output file '.../B.d.ts' has not been built from source file '.../B.ts'
but if I rename A.ts
to C.ts
everything works, as does removing the project reference or not using root/outDir. The problem occurs in 3.0.0-rc
as well as next
right now.
Edit: In retrospect this is clearly enough a bug to warrant its own issue, so I made #25864.
I just tested TS 3 RC project references in a typical npm package setup with included tests. The tests were set up as a TS project referencing the implementation TS project.
I surprisingly found that project references do not adhere to the outDir compilerOption of referenced or referencing projects, and that the relative paths for module imports in the compiled referencing project's JS files therefore are wrong; paths point to the TS folder, not the compiled output folder.
A somewhat simplified view of my PoC:
Expected behavior: Relative paths of module imports in the compiled code for the referencing project should be calculated from:
I put up a small repository to demonstrate the problem: https://github.com/josundt/tsprojects-outdirproblem
UPDATE: Separate issue created: https://github.com/Microsoft/TypeScript/issues/26036
Is <TypeScriptBuildMode>
msbuild parameter pllaned for 3.0.1? It's not working in 3.0.0, tsc.exe
still runs with --project
, even if it's set to true
.
@RyanCavanaugh This project thing looks really cool so I decided to give it a try. It hasn't been going so well for me so I thought I'd share my experiences and the code-base in case it's helpful for you guys.
Basically, I have the following folders in my project.
My previous set-up (TypeScript v2.x) was to have a tsconfig.json in the root with the most "forgiving" settings and that would give my IDE all the intellisense etc and cross project renames. I then had a compile.json in back-end (which was a tsconfig file in disguise), and another compile.json in the front-end (same concept). The reason for these files is that I wanted to only compile front-end code (for webpack) or back-end code (for ts-node) separately from the rest. Needless to say, they wouldn't have any issues picking up the code that I referenced in the shared folder as the paths were all relative etc. This all worked perfectly fine for my work flow and I've been super happy with it (except about a year and a half ago the Angular CLI didn't like it at all, but I'm doing webpack manually and haven't retried since...)
So, I upgraded TypeScript to the @next
version and switched over to this whole tsconfig per folder thing and removed the tsconfig.json that was at the root. I'm using the latest version of the VSCode Insiders build, but it doesn't really seem to work at all. In the sense that when I'm looking at some back-end code that references something in the shared folder, it says that thing simply doesn't exist.
The ts-node for my back-end only works if I compile the shared folder and emit to disk, something that I'd rather not have to do because my back-end compiles down to ES6 and my front-end down to ES5. Although I guess it wouldn't be so bad if it emitted declarations only?
Anyway, I've checked it all in, would you mind taking a look and seeing what you think? I wouldn't mind if you wanted to use the repo (or part of it) as some kind of example project. Thanks!
Here's the link: https://github.com/SMH110/Pizza-website/tree/TS_Projects
The previous working setup not using the new projects functionality is at this commit: https://github.com/SMH110/Pizza-website/commit/9548bdd2917439f66e787dcc41e8f99e267e6fce
Hi @RyanCavanaugh! I'm very excited to start using this feature at Canva, so have been having a play. I have run into one bug.
When a project contains a reference to project in a ancestor folder, includes source files must be listed in dependency order.
Sample project: https://github.com/WearyMonkey/ts3-file-order-bug
e.g.
{
"compilerOptions": {
"composite": true,
"declaration": true
},
"references": [
{ "path": ".." }
],
"files": [
"./imports.ts",
"./exports.ts"
]
}
Where imports.ts
is:
import { foo } from './exports';
and exports.ts
is:
export const foo = 'foo';
Fails with the error
error TS6305: Output file '/Users/toby/dev/ts3_bug/proj1/proj2/exports.d.ts' has not been built from source file '/Users/toby/dev/ts3_bug/proj1/proj2/exports.ts'.
Changing the order of files to:
"files": [
"./exports.ts",
"./imports.ts"
]
Succeeds compilation as expected.
I've tried on 3.0-rc, 3.0.1-insiders.20180726, and 3.1.0-dev
Some more general feedback on my experience so far:
rootDir
, outDir
and how that affects cross project imports is quite confusing, and I was only able to find the correct (I think) configuration via trial and error.has not been built from source file
error, and theres no help to find the cause, forcing me to fix via trial and error.Just as a heads up for everyone: @RyanCavanaugh recently became a father! 🎉 👶 🍼 🎉
He'll be out for a while, but in the meantime if you are running into anything bug-like, I'd encourage you to file separate issues and the team will try to take a look.
Otherwise, this is still a good venue for general discussion. Thanks all!
This feature looks like holy-grail we were looking for from the very beginning of TS: Since compiling every single .ts into single .js (1:1) was too fragmented (and would had resulted in enormous
I try to create composite project for testing this feature. I think, this feature still need a lot of implement in term of tooling.
In VS Code insider build, I can use renaming tool to rename "add" function in source project. However, I cannot go to definition of reference function in test project. It navigated to definition file instead.
In VS2017 15.8 Preview 5, it didn't change exported function name in test project after I renamed "add" function in source project. Moreover, it showed definition file instead of real source code when I clicked go to implementation in test project.
export function add(x: number, y: number) {
return x + y;
}
export function remove(x: number, y: number) {
return x - y;
}
export const defaultSettings: AppPlatform.Setting = {
width: 50,
height: 50
};
import { add, defaultSettings } from "../src/utils";
export function testAdd(){
const result = add(5, 4);
return result;
}
export function getDefaultSetting() {
return defaultSettings;
}
https://github.com/Soul-Master/typescript3-composite-project
I just tried out the composite project feature.
It all compiles, and runs with ts-node
successfully.
However, when I try to "Go to Definition (F12)" with VS Code, it just says "No definition found". Hovering my cursor over a function of the imported project gives the correct type declarations, though.
TS 3.0.1
VS Code:
Version: 1.25.1
Commit: 1dfc5e557209371715f655691b1235b6b26a06be
Date: 2018-07-11T15:40:20.190Z
Electron: 1.7.12
Chrome: 58.0.3029.110
Node.js: 7.9.0
V8: 5.8.283.38
Architecture: x64
[EDIT] Here's my attempt at using TS 3's project composition https://github.com/AnyhowStep/TS3ProjectComposition
For anyone combing this thread looking for sample setups, Aurelia vNext is now in full swing 😄 You can find our monorepo here: https://github.com/aurelia/aurelia It's setup with TypeScript 3 projects, Lerna 3, Mocha/Chai/Sinon/Karma for unit tests and CircleCI 2.0 for continuous integration.
Big thanks to the TypeScript team for all their hard work. Aurelia vNext is fully committed to TS now and we're loving the new productivity and contribution-pleasant setup we are able to achieve with this new combination of tools. Keep it up!
Feedback 1: I'd like tsc -b -w
to show what has been rebuilt in the CLI (similar to tsc -p -w
). Currently it just silently rebuilds. -v
is too noisy.
Feedback 2: Can you have an official example (as in, GitHub project) that illustrates how one should structure a project, linked from the handbook? Nobody is going find the example projects you are linking in this issue.
Feedback 3: The blog post has a misleading comment // Needed for project references.
for composite
, when composite
is only required for referenced project.
Feedback 4: The blog post does not include --watch
in the --build mode
section (IMO it's the most useful one). This is inconsistent with the handbook.
Has anybody else had any issues with auto-importing from a project reference? VSCode always wants to import from my dist folder instead of the src folder.
Example:
// Auto imports from here
import { MyFunc } from "../../../../dist/shared";
// Instead of here
import { MyFunc } from "../../../shared";
@drew-y I'm pretty sure this is the intention with --build
mode. Because you're essentially referencing a separate TypeScript project (shared
in this case), you must import the built output of it.
@ryanstaniforth That does make sense. And I now realize that "../../../../dist/shared"
technically works. However "../../../shared"
is still preferable, at the very least is terms of aesthetics. So it would be nice if there was still some sort of option to prefer that directory. If possible.
@drew-y I see where you're coming from, I first assumed I should import from the source directory. If that was the case though, what would be the point of TypeScript ensuring dependencies are compiled first. There must be a reason that we're not aware of for this design decision.
@ryanstaniforth Agreed. Although technically, in my case, "../../../shared"
is an import from the dist
directory.
At least for me, my dist directory directly mirrors my src directory. I.E.:
src/
client/
server/
shared/
dist/
client/
server/
shared/
But I'm realizing it would be tough for the typescript compiler to know that. Unless there was a way for me to specify it.
@drew-y Oh I see, so you actually only have a single project, i.e. only one tsconfig.json
? In that case I see you're problem, it shouldn't favour dist
.
Is there an example of using this with a multirepo? My development environment is working great while working across projects but each one is broken into separate repos and NPM packages. When I try to build them via CI, the references aren't pulled down so it complains and refuses to build.
It would be great if there was an option that lets me fallback to normal resolution (via node_modules/), but I don't think there is one.
Or is this really only useful for monorepos?
Thank you for the fantastic feature.
I just tried VSCode 1.26.0 with my composite project. With TypeScript 3.0, nothing works. I cannot go to definition or rename method in all referenced files.
However, it works fine when I switch TypeScript to 2.7.1 for unknown reason.
https://github.com/Soul-Master/typescript3-composite-project
I've never seen the need for build before / after hooks for TypeScript until now, but after successfully getting project references working in my project I'm wondering if they're worth considering.
I use NPM scripts to automatically bump the patch version of my packages every time I build, e.g.:
"build": "npm version patch --no-git-tag-version && tsc"
But of course I'm not the one building these packages any more, the TypeScript build mode is.
Some useful hooks for this scenario would be preBuild
and postBuild
which only run if something has actually changed in the project (which presumably --build
can distinguish up front).
If I have project A with a reference to project B and I change a single file in project B, I am seeing the following:
tsc "touches" every file in project B outDir. tsc "touches" every file in project A outDir after that.
In a big project with two different nodemon processes being watched, this is causing some mayhem. For some reason I actually end up in a loop of restarts (debugging that led me to realize the above), but even beyond that, wouldn't this make a lot more sense and be a lot more gentle?
tsc outputs just the changed file in project B to outDir tsc does nothing in project A
Having serious problems with only a single depth of referenced projects in vscode:
I am using outDir with a common build folder that mirrors my source folder.
However, vscode seems entirely dependent on the .d.ts files that are generated by tsc -b -w
running in the background.
One issue this causes is that often vscode will have a completely wrong code comment: i.e. when I mouse-over in the editor, its showing a comment for the wrong element (as if the internal source mapping typescript is using for the current file is old).
Another issue is that the type system seems to break quickly: i.e. I can reload vscode and all will be well and all the comments will be correct. Then as soon as I type anthing in the file, some of the type information breaks (some of the types from the dependent project will go to any
etc.). This happens even when I can F12 directly to the .d.ts file in the build folder (however, I did notice that although the .d.ts code is correct, the color scheme is wrong for the broken type.)
Anyway, at this point typescript 3.0 references seem to work ok with tsc
but vscode is not stable enough to use it.
It seems that my problem had to do with importing 2 files from the referenced project that reference each other:
import React from 'react';
// BACKGROUND:
// 'common-components' is a referenced ts project
// 'list-editor' imports 'common-deps'
// ERROR: If I try to import a 'common-deps' here, it breaks the type system in vscode (actually it works for a short time immediately after reloading vscode)
// import { CommonComps } from '../../common-components/src/common-deps';
import * as ListEditorModule from '../../common-components/src/list-editor';
// WORKAROUND: However, if I export a 'common-deps' type from within 'list-editor', and bring in the type that way, it works fine
type CommonComps = ListEditorModule.CommonComps;
Another issue: It seems the tsc -b -w
will not build child (referenced) projects if the main project has an error. I would expect the build tree to execute the dependencies first (and generate their outputs) even if the main project has a problem that prevents the build (assuming the tsconfig references are correct). However, it doesn't seem to be working like this.
With vscode dependent on the outputs of the referenced projects, it is not possible to get code comments and helps to work without manually building the referenced projects.
Found the problem and added a bug report: https://github.com/Microsoft/TypeScript/issues/26703
I attempted to use project references in a monorepo, so I don't know if this should be here or the other discussion. I made a fork of learn-a project https://github.com/panjiesw/learn-a to better describe what I found.
One of monorepo pattern using lerna + NPM is to reference subprojects inter dependencies using NPM's own "package": "file:<location>"
instead of their version in package.json
. The learn-a fork above have been modified to this structure. Some modified preview:
// root package.json
{
"dependencies": {
"@ryancavanaugh/pkg1": "file:packages/pkg1", // direct sub-package reference
"@ryancavanaugh/pkg2": "file:packages/pkg2", // direct sub-package reference
"@ryancavanaugh/pkg3": "file:packages/pkg3" // direct sub-package reference
},
"devDependencies": {
"lerna": "^3.2.1",
"typescript": "^3.0.3"
}
}
// pkg2 package.json
{
//<... unmodified stuffs ...>
"dependencies": {
"@ryancavanaugh/pkg1": "file:../pkg1" // direct inter sub-package reference
}
}
This way, even without --hoist
option in lerna bootstrap
, NPM will hoist all the sub-package dependencies in the root's node_modules
. Combine this with the newly addition of TypeScript build mode for project reference, we can have a powerful build orchestration from the root package only, instead of repeating build instruction in every package.
There are issues though, in VSCode v1.26.1
, the auto-import and refactoring experience is still not ideal for me. For sub-package that has "references"
to other package in their tsconfig
(pkg2
and pkg3
), somehow, the auto-import always use absolute path, even for something (say, interface
) in a sibling .ts file. Even if my "typescript.preferences.importModuleSpecifier"
setting is set to "auto"
or "relative"
.
// pkg2/src/baz/interface.ts
export interface Baz {
message: string;
}
// pkg2/src/baz/index.ts
import { Baz } from '@ryancavanaugh/pkg2/src/baz/interface'; // auto-imported
export default function baz(): Baz {
return { name: 'baz', message: 'Hello Baz' };
}
This doesn't happen in pkg1
which doesn't reference other sub-package
// pkg1/src/bar/interface.ts
export interface Bar {
message: string;
}
// pkg1/src/bar/index.ts
import { Bar } from './interface'; // correct auto-import
export default function bar(): Bar {
return {name: 'bar', message: 'Hello World!'};
}
Maybe it has to do with the fact that all sub-packages are referenced in root's package.json
so they're all also appeared in root's node_modules
as symlink.
To run my learn-a fork, you need lerna
version >=3.0.0
and do lerna bootstrap
instead of npm install
for the first time.
@RyanCavanaugh I'm pretty sure I'm missing something, or it's a bug, but project references seem not to work when using esnext
. Reproduction based on learn-a
yarn workspaces here: #26819. Result is a dreaded error TS2307: Cannot find module
, and removing the module/target
from compiler options makes the sample compilable again.
I filed another issue for direct file imports not working in #26823.
It seems quite unworkable if true that with project references you can only resolve from the index, not direct to a file e.g.
// import * as p1 from '@ryancavanaugh/pkg1'; // works
import { fn } from '@ryancavanaugh/pkg1/foo'; // does not work
export function fn4() {
// p1.fn();
fn();
}
@rosskevin The reason you only can resolve from the index is because tsc imports the built files when using project references. This means that your import must be import { fn } from '@ryancavanaugh/pkg1/lib/foo'
because the tsconfig for pkg1 has "outDir": "lib"
and the package.json has "typings": "lib/index.d.ts"
. The import would work as you expected if outDir was unset and typings was set to just index.d.ts
.
Also, the same problem would occur when importing any package that was built out of source unless a prepackage script changed the location of the built files. In that case the package.json would also have to be changed so that the "main" and "typings" keys did not reference the build directory.
EDIT: This was my understanding of the issue, and how I resolved your example with learn-a. If I'm missing something, just ignore this I guess
That may be true @WhiteAbeLincoln, but I did not try unsetting outDir
because build artifacts would pollute the src
directory, and that isn't something that is acceptable to us.
Yeah, I can understand that. The only solution I can think of is to use your current workaround with paths, or use the —listEmittedFiles option and have a script that will parse that output and delete the emissions in the src directory
On Sep 18, 2018, at 08:34, Kevin Ross notifications@github.com wrote:
That may be true @WhiteAbeLincoln, but I did not try unsetting outDir because build artifacts would pollute the src directory, and that isn't something that is acceptable to us.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
Should tsc -b --watch
rebuild projects incrementally?
Checked now (typescript@3.1.rc
), on any change in one file tsc
building the whole project and all dependent projects.
Tested with two parallel tsc --watch
processes - works much faster, rebuilds only changed file.
So, is incremental rebuild not implemented yet or it's a bug?
I think that typescript Types signature exported in d.ts files are not equivalent between nodejs runs (the order of union members can change for example), that may explain some strange rebuild behaviours.
Continuation of #3469 "Medium-sized Projects" which has grown too large for GitHub to display comfortably
Please use this thread for feedback, general discussion, questions, or requests for help / "is this a bug" discussions.
Active bugs / PRs in this area:
Possible next steps:
tsc -b
tsc -b
Other interesting links:
--outFile
learn-a
repo usingyarn
workspaces!