In 0.0.30, one cannot create a project within a project (ng new says no), nor it seems, can one have a project take a dependency on another project.
For example, on the command line
md MyAwesomeGamesSite
cd MyAwesomeGamesSite
ng new core
ng new game1
ng new game2
Creates
MyAwesomeGamesSite/core #angular2 library of shared classes, components, w/ tests/coverage etc
MyAwesomeGamesSite/game1 # should depend on core
MyAwesomeGamesSite/game2 # should depend on core
Referencing core components from typescript in game1 looks like
import * as CoreStuff from './../../../../core/src/client/app/core/core.service; // 4 directories up./../../../..
However, the output into (for example) game1/tmp/broccoli_type_script_compiler-input_base_path7Z0eLNNd.tmp does not align the import statements correctly
broccoli_type_script_compiler-input_base_path7Z0eLNNd.tmp needs to be
import * as CoreStuff from './../../../../../core/src/client/app/core/core.service; // 5 directories up
Resulting in the following error message from ng serve
The Broccoli Plugin: [BroccoliTypeScriptCompiler] failed with: Error: Typescript found the following errors:
C:/src/[...]/tmp/broccoli_type_script_compiler-input_base_path7Z0eLNNd.tmp/0/src/client/app/test.ts (4, 28): Cannot find module './../../../../core/src/client/app/core/core.service
node_modules\angular-cli\addon\ng2\utilities\dynamicpathparser.js might also be an issue here..
if (outputPath.indexOf(rootPath) < 0) {
throw `Invalid path: "${entityName}" cannot be ` +
`above the "${path.join('src', 'app')}" directory`;
}
If one could have sub-projects and build these seperately that would be awesome too :)
eg.
core // create with ng new core
/lib
/core.service.ts
/games
/game1 // need to build/deploy this without game 2 assets
/game2 // need to build/deploy this without game 1 assets
EDIT: Perhaps being able to do
ng serve arbitrary-component
ng build arbitrary-component
Would do the job too.
Cheers,
Matt
I think I see what you're trying to do. I think we haven't been paying much attention to the 'project within project' usecase, because usually that's solved via npm packages instead.
For local development, I understand you don't want published npm packages though, nor would you want to go through the 'version dance' required to change stuff in core and see it in each site. For this you can use npm link.
For instance:
md MyAwesomeGamesSite
cd MyAwesomeGamesSite
ng new core
ng new game1
ng new game2
cd game1
npm link ../core
cd ../game2
npm link ../core
This should give you your core project as a node module inside each of game1/2. You could then import whatever is needed from core via import {stuff} from 'core'.
That being said, I think what would _really_ suit you is nested routes instead of nested projects. We're looking at making ng g route able to take on nested routes, and also making them able to be lazy loaded.
Under this scenario, you'd have a single project with a top-level route folder. This would have inside of it a folder for game1, one for game2 and one for core (although the name we're looking at atm is shared).
game1 and game2 would have access to core - but not to each other. They could also have their own internal nested routes and their own shared folder, and so forth.
This isn't done yet but we're looking on having it soon.
EDIT: I think we haven't instead of I think we've.
Thanks for the quick and thoughtful reply, I think the npm link solution will be just about perfect, I can't wait to try that tomorrow.
Nested routes sounds like it might work too (at least for my use case) as long as the production build for game1 didn't include game2 assests.
Glad to help :D
I'd also like to add that currently there isn't library specific build process, but we are looking at it.
:) I hacked on angular2app.js (from memory) and taught it to merge a brocolli tree looking like
Projects/
core/src/client
game1/src/client
Rather than just
Src/client
But it wasn't elegant...
Is there any, or likely to be any easy way to customize or hook the build process to include custom build steps do you think?
I could give you some info about that, but I think it would be inaccurate. The build process has undergone a lot of changes in the last weeks.
@hansl and @jkuri are the more knowledgeable people on that topic.
Hm, no joy
ng new core
cd core
npm link
cd..
ng new game1
cd game1
npm link ../core
ng serve
Results in
The Broccoli Plugin: [BroccoliTypeScriptCompiler] failed with:
Error: EEXIST: file already exists, link 'C:\temp\game1\node_modules\core\src\client\app\core.service.js.map' -> 'C:\temp\game1\node_modules\core\src\client\app\core.service.js.map'
at Error (native)
at Object.fs.linkSync (fs.js:966:18)
at BroccoliTypeScriptCompiler._outputFile (C:\temp\game1\node_modules\angular-cli\lib\broccoli\broccoli-typescript.js:205:8)
at Object.writeFile (C:\temp\game1\node_modules\angular-cli\lib\broccoli\broccoli-typescript.js:163:14)
at Object.writeFile (C:\temp\game1\node_modules\typescript\lib\typescript.js:6515:14)
at writeEmittedFiles (C:\temp\game1\node_modules\typescript\lib\typescript.js:32726:24)
at doEmit (C:\temp\game1\node_modules\typescript\lib\typescript.js:32594:17)
at emitFile (C:\temp\game1\node_modules\typescript\lib\typescript.js:39322:17)
at onSingleFileEmit (C:\temp\game1\node_modules\typescript\lib\typescript.js:6483:13)
at Object.forEachExpectedEmitFile (C:\temp\game1\node_modules\typescript\lib\typescript.js:6457:21)
The broccoli plugin was instantiated at:
at BroccoliTypeScriptCompiler.Plugin (C:\temp\game1\node_modules\angular-cli\node_modules\broccoli-caching-writer\node_modules\broccoli-plugin\index.js:10:31)
at BroccoliTypeScriptCompiler.CachingWriter [as constructor] (C:\temp\game1\node_modules\angular-cli\node_modules\broccoli-caching-writer\index.js:21:10)
at BroccoliTypeScriptCompiler (C:\temp\game1\node_modules\angular-cli\lib\broccoli\broccoli-typescript.js:25:35)
at Angular2App.toTree.Angular2App._getTsTree (C:\temp\game1\node_modules\angular-cli\lib\broccoli\angular2-app.js:249:16)
at Angular2App.toTree (C:\temp\game1\node_modules\angular-cli\lib\broccoli\angular2-app.js:37:21)
at module.exports (C:\temp\game1\angular-cli-build.js:9:14)
at Class.module.exports.Task.extend.setupBroccoliBuilder (C:\temp\game1\node_modules\angular-cli\node_modules\angular-cli\lib\models\builder.js:55:19)
at Class.module.exports.Task.extend.init (C:\temp\game1\node_modules\angular-cli\node_modules\angular-cli\lib\models\builder.js:89:10)
at new Class (C:\temp\game1\node_modules\angular-cli\node_modules\angular-cli\node_modules\core-object\core-object.js:18:12)
at Class.module.exports.Task.extend.run (C:\temp\game1\node_modules\angular-cli\node_modules\angular-cli\lib\tasks\serve.js:15:19)
Giving _outputFile(absoluteFilePath, fileContent, registry) {...} in brocolli-typescript.js a try/catch, will let it serve, but Chrome says
http://localhost:4200/core/src/client/app/core.service.js 404 (Not Found)
At which point I figured perhaps we have to load it with SystemJs somehow because tsc compiles it just fine (with intellisense in the IDE even) so...
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
},
core: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app.js').then(null, console.error.bind(console));
</script>
I can change for example, default extension for core to .ts in the script tag and see it show up in the browser as http://localhost:4200/core/src/client/app/core.service.ts 404 (Not Found)
So I'm thinking that SystemJs is configured properly..
I'm assuming brocolli doesn't merge and include core on it's merge/serve? I can't see core included in the browsers source files..
I got it working but it's pretty nasty...
Step 1 : Setup npm link
Step 2 : Alter angular2-app.js as follows
Angular2App.prototype._getVendorNpmTree = function () {
var vendorNpmFiles = [
'systemjs/dist/system-polyfills.js',
'systemjs/dist/system.src.js',
...
'core/src/client/app/core.service.js'
];
Step 3 : Add <script src="vendor/core/src/client/app/core.service.js"></script> to index.html
Step 4 : ng serve
Step 5 : Manually edit core.service.js from (this is really problematic because you can't alter core.service.ts without manually editing core.service.js)
System.register([], function(exports_1, context_1) { .. }
To
System.register("core/src/client/app/core.service", [ "core/src/client/app/core.service" ], function(exports_1, context_1) { .. }
Step 6 - Reload the browser
game1 Works!
It looks like angular2-app.js will take a vendorNpmFiles option which will fix Step 2.
Step 3 would need a wiredep or similar?
Step 5 I'm not really sure how tsc is creating this...
Mostly just FYI, but unless I'm missing something, it's a blocking issue for having a library project shared between front-ends.
I think at this point, the problem is rather the 3rd party libs since your core package is, as far as the other ones are, a external lib.
IIRC that isn't the easiest thing at the moment, the freshest info I got is https://github.com/angular/angular-cli/issues/330.
@jkuri can you advise?
Actually, looking at other issues, the EEXIST errors you were getting might be related to https://github.com/angular/angular-cli/issues/403
Hmm... #330 is similar, the main difference being I'm trying to reference a typescript file, ideally created with ng new, which means that the resultant js must play nice with SystemJs.
Yeah, on #403 throwing a try/catch around the link does the job in a scrappy kind of a way.
I'd have guessed that using whatever the npm equivalent for wiredep is (I've only used it with bower) and generating Funnels for npm --save'd packages might do the job. It could also potentially be solved by having brocollis folders at the same folder depth as the original source code too...
Heya, I've been working on making 3rd party imports more explicit and improving the SystemJS configuration in https://github.com/angular/angular-cli/pull/481.
It this could help in your scenario. Basically, you'd add the path to your npm module in vendorNpmFiles in angular-cli-build.js and then hook up src/client/system-config.ts to use it.
This isn't completely new functionality, but before we were wiring a lot of stuff internally so all of this wasn't explicit. By putting it in plain sight I think it's easier for users to take the example and configure SystemJS and vendorNpmFiles for libs.
Heya, I'll check it out tomorrow if I get a chance. In the meantime I've been working with https://github.com/cmelion/generator-ng2-webpack.
The biggest issue with treating shared libraries as npm modules is that you need to npm link/unlink them (kind of horrible), and do watch notification. Ie, if i change the library I'm developing it should live reload the dependant app.
Yeah the link/unlink is a pain. I have an a webpack setup (using babel) where it actually traces the dependencies and reloads when I modify the library it does reload though, so there must be a way.
Although all that is a pain, for the app that I'm talking about it was worth it. The lib repo is used in 3 frontends and without it being a npm module, we couldn't keep versions. Often we updated the lib to fix something on frontend1, only to break frontend2 because it wasn't versioned (just using latest master).
Yup, doable but painful, https://github.com/cmelion/generator-ng2-webpack does it perfectly, though I haven't dug into the implementation details.
Haha, write more tests! ;)
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
I think I see what you're trying to do. I think we haven't been paying much attention to the 'project within project' usecase, because usually that's solved via npm packages instead.
For local development, I understand you don't want published npm packages though, nor would you want to go through the 'version dance' required to change stuff in core and see it in each site. For this you can use npm link.
For instance:
This should give you your
coreproject as a node module inside each ofgame1/2. You could then import whatever is needed from core viaimport {stuff} from 'core'.That being said, I think what would _really_ suit you is nested routes instead of nested projects. We're looking at making
ng g routeable to take on nested routes, and also making them able to be lazy loaded.Under this scenario, you'd have a single project with a top-level route folder. This would have inside of it a folder for
game1, one forgame2and one forcore(although the name we're looking at atm isshared).game1andgame2would have access tocore- but not to each other. They could also have their own internal nested routes and their ownsharedfolder, and so forth.This isn't done yet but we're looking on having it soon.
EDIT:
I think we haven'tinstead ofI think we've.