angular / angular-cli

CLI tool for Angular
https://cli.angular.io
MIT License
26.72k stars 11.98k forks source link

importing is cumbersome for deeply nested components, how do I set up a map to the app root? #865

Closed NullVoxPopuli closed 8 years ago

NullVoxPopuli commented 8 years ago
$ ng --version
Could not start watchman; falling back to NodeWatcher for file system events.
Visit http://ember-cli.com/user-guide/#watchman for more info.
angular-cli: 1.0.0-beta.2-mobile.3
node: 5.10.1
os: linux x64

how do I set up a mapping for my app root to import? I'm trying to avoid doing:

import { stuff } from '../../../../../../../components/some/path-to/what/iwant

and would like to do:

import { stuff } from 'myAppName/components/some/path-to/what/iwant

it is more intuitive, to specify an app-relative path, especially when refactoring (so you don't need to open the file tree, and count how many ../ you have...

I've tried adding mappings in my system-config.ts, but I haven't found anything to work.

const map: any = {
  '@app': 'src'
};

doesn't work -- as in:

import { stuff } from '@app/components/mystuff' 

errors on build.

cdarken commented 8 years ago

I see that in system-config.ts we have '@angular': 'vendor/@angular'. This seems to be relative to the dist/ directory. Maybe you can try using directly app/components/stuff. Just a wild guess, as I'm new at this, still trying to figure things out.

NullVoxPopuli commented 8 years ago

I had also tried adding '@app': 'app', and it can't find any files using that. :-\

Also, I deleted the dist folder, so it appears that however this needs to be configured, it is pre-build (because the error occurs before the dist folder is even created)

serhiisol commented 8 years ago

you should use map + packages in system-config.js

/** Map relative paths to URLs. */
const map:any = {
  '@web-app': './app/'
};

/** User packages configuration. */
const packages:any = {
  '@web-app': {
    format: 'cjs',
    defaultExtension: 'js',
    main: 'index'
  }
};

Also tsconfig.json in compilerOptions section

"paths": {
      "@web-app": ["./app/index.ts"],
      "@web-app/*": [
        "./app/*"
      ]
    }

and then you can use something like that:

import {environment} from '@web-app';

Note: it works without -prod option, with -prod it toggles error:

$ ng build -prod
Could not start watchman; falling back to NodeWatcher for file system events.
Visit http://ember-cli.com/user-guide/#watchman for more info.
⠹ Building"{{content-for}}" has been deprecated and will be removed before RC.
Build failed.
The Broccoli Plugin: [BundlePlugin] failed with:
Error tracing app/+login/login.component.js at file:///Users/serhiysolonko/Development/Projects/web-app/tmp/bundle_plugin-input_base_path-kOVkldZW.tmp/0/app/+login/login.component.js
    Loading app/+login/index.js
    Loading app/web-app.component.js
    Loading app/index.js
    Loading main.js
    Error: Unable to calculate canonical name to bundle file:///Users/serhiysolonko/Development/Projects/web-app/app//index.js. Ensure that this module sits within the baseURL or a wildcard path config.
    at getCanonicalNamePlain (/Users/serhiysolonko/Development/Projects/web-app/node_modules/systemjs-builder/lib/utils.js:220:13)
    at getCanonicalName (/Users/serhiysolonko/Development/Projects/web-app/node_modules/systemjs-builder/lib/utils.js:145:19)
    at /Users/serhiysolonko/Development/Projects/web-app/node_modules/systemjs-builder/lib/trace.js:471:36

The broccoli plugin was instantiated at:
    at BundlePlugin.Plugin (/Users/serhiysolonko/Development/Projects/web-app/node_modules/broccoli-plugin/index.js:10:31)
    at BundlePlugin.CachingWriter [as constructor] (/Users/serhiysolonko/Development/Projects/web-app/node_modules/broccoli-caching-writer/index.js:21:10)
    at BundlePlugin (/Users/serhiysolonko/Development/Projects/web-app/node_modules/angular-cli/lib/broccoli/angular-broccoli-bundle.js:10:36)
    at Angular2App._getBundleTree (/Users/serhiysolonko/Development/Projects/web-app/node_modules/angular-cli/lib/broccoli/angular2-app.js:421:22)
    at Angular2App._buildTree (/Users/serhiysolonko/Development/Projects/web-app/node_modules/angular-cli/lib/broccoli/angular2-app.js:159:21)
    at new Angular2App (/Users/serhiysolonko/Development/Projects/web-app/node_modules/angular-cli/lib/broccoli/angular2-app.js:53:23)
    at module.exports (/Users/serhiysolonko/Development/Projects/web-app/angular-cli-build.js:6:10)
    at Class.module.exports.Task.extend.setupBroccoliBuilder (/Users/serhiysolonko/Development/Projects/web-app/node_modules/angular-cli/lib/models/builder.js:55:19)
    at Class.module.exports.Task.extend.init (/Users/serhiysolonko/Development/Projects/web-app/node_modules/angular-cli/lib/models/builder.js:89:10)
    at new Class (/Users/serhiysolonko/Development/Projects/web-app/node_modules/core-object/core-object.js:18:12)
    at Class.module.exports.Task.extend.run (/Users/serhiysolonko/Development/Projects/web-app/node_modules/angular-cli/lib/tasks/build.js:15:19)
    at /Users/serhiysolonko/Development/Projects/web-app/node_modules/angular-cli/lib/commands/build.js:32:24
    at lib$rsvp$$internal$$tryCatch (/Users/serhiysolonko/Development/Projects/web-app/node_modules/rsvp/dist/rsvp.js:1036:16)
    at lib$rsvp$$internal$$invokeCallback (/Users/serhiysolonko/Development/Projects/web-app/node_modules/rsvp/dist/rsvp.js:1048:17)
    at /Users/serhiysolonko/Development/Projects/web-app/node_modules/rsvp/dist/rsvp.js:331:11
    at lib$rsvp$asap$$flush (/Users/serhiysolonko/Development/Projects/web-app/node_modules/rsvp/dist/rsvp.js:1198:9)
NullVoxPopuli commented 8 years ago

@SergeySolonko can you explain a bit what each option does, and how it affects the various parts of the build process?

I tried copy-pasting what you had, and I still get the error "Cannot find module '@web-app/anything'"

serhiisol commented 8 years ago

@NullVoxPopuli so tsconfig.json option responsible for compilation, system-config.js responsible for js loading.

this config works for standard folder configuration, at least first part. It should be something like this:

screenshot at may 23 15-02-25

with system-config you're creating kind-a symlink to get access to specific module, with tsconfig.json you're saying which file you're expecting to get and that it should be compiled

now @web-app basically it's a link to src/app/index.ts module

@web-app/* it's a link to rest of the files, e.g. @web-app/shared/index

I hope I've explained it clearly :)

hansl commented 8 years ago
  '@web-app/*': {
    format: 'cjs',
    defaultExtension: 'js'
  }

does nothing. The * is not understood in SystemJS packages.

hansl commented 8 years ago

The path mapping is only used by TypeScript compiler to know which import corresponds to which modules. SystemJs operates on another level.

serhiisol commented 8 years ago

yes correct that was redundant, but still when you're trying to compile with _prod mode - it fails

tydanielson commented 8 years ago

I was able to get the system-config.js file to work correctly, but I added paths to the compilerOptions section of tsconfig.json and am unable to import, the compiler throws this error: Cannot find module '@app' . I also looked at the schema for tsconfig.json and can't find anything about paths ... https://www.typescriptlang.org/docs/handbook/compiler-options.html

Am I missing something? Here is my tsconfig.json...

{
  "compileOnSave": false,
  "compilerOptions": {
      "paths": {
        "@app" : ["//test.externalhost.com/app/index.ts"],
        "@app/*": [
            "//test.externalhost.com/app/*"
        ]
      },
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "mapRoot": "@app",
    "module": "commonjs",
    "moduleResolution": "node",
    "noEmitOnError": true,
    "noImplicitAny": false,
    "outDir": "../dist/",
    "rootDir": ".",
    "sourceMap": true,
    "target": "es5",
    "inlineSources": true
  },

  "files": [
    "main.ts",
    "typings.d.ts"
  ]
}
colltoaction commented 8 years ago

I'm just starting with ng2, and it seemed very intuitive to me to try to use a full path. Why do people tend to use relative paths instead?

NullVoxPopuli commented 8 years ago

@tinchou, how do you use a full path? how do you ref your app root?

aciccarello commented 8 years ago

@tinchou Using relative paths can be easier when referencing a file in the same folder or a folder above. If you move a folder you don't have to fix all of the imports within that folder. But when the files are deeply nested and you are referencing something that is in the root of your source directory, I agree that full path tends to be simpler.

Also, the import system is related to ES2015 modules/TypeScript/System JS more than Angular 2 itself.

colltoaction commented 8 years ago

@NullVoxPopuli I haven't set it up, I'm just saying it didn't work by default.

@aciccarello true, but maybe having an alias to the root in the default template would help.

serhiisol commented 8 years ago

@tinchou that's what we are trying to get - to have a reference to very top folder (app folder). It works well for non-production build, for production - it fails. https://github.com/angular/angular-cli/issues/865#issuecomment-220969521

colltoaction commented 8 years ago

@SergeySolonko this is my +1 to having an alias to the app root by default with ng new (that works in production too, of course :stuck_out_tongue:)

Blasz commented 8 years ago

The following works for me for both ng build and ng build -prod.

$ ng --version
(node:5016) fs: re-evaluating native module sources is not supported. If you are using the graceful-fs module, please update it to a more recent version.
Could not start watchman; falling back to NodeWatcher for file system events.
Visit http://ember-cli.com/user-guide/#watchman for more info.
angular-cli: 1.0.0-beta.5
node: 6.2.1
os: linux x64

tsconfig.json - Add the following paths property to compilerOptions

"compilerOptions": {
  ...
  "paths": {
    "app/*": [
      "./app/*"
    ]
  }
}

Now the app directory should be able to be imported from as follows:

import { Logger } from 'app/shared/index';

Caveats

App specific barrels aren't able to be referenced by directory name when imported from an app-relative path regardless of whether a barrel definition exists in system-config.ts (not sure why).

import { Logger } from 'app/shared';       // won't work
import { Logger } from 'app/shared/index'; // will work

As an aside, I have no idea why the paths entry in tsconfig.json works when it should only be available in typescript@next (with the target milestone of 2.0) - https://github.com/Microsoft/TypeScript/pull/5728. As far as I can see both angular-cli and the default ng project scaffold use typescript 1.8.10

chapati23 commented 8 years ago

yeah, we definitely need aliases. webpack makes that pretty straightforward: https://webpack.github.io/docs/configuration.html#resolve-alias

obviously there's still the typescript compilation step in between which makes it a bit more tricky but the newly introduced paths compiler option should be able to solve this.

i have a relatively small app and already have super ugly relative import-paths. all the nice encapsulation through componentisation is basically destroyed because if you have relative paths everywhere, your app is basically a microlith with super high coupling.

cdarken commented 8 years ago

Yeah, I've realized just recently that the import must work for the compile step and then for the dist version, where the files are loaded by SystemJs. The search paths for the typescript compiler seem to be kind of hardcoded, you can't tell it to use aliased paths like with SystemJs.

Blasz commented 8 years ago

Anyone know how to do this using the webpack version?

filipesilva commented 8 years ago

A more broad issue that includes this one is being tracked in https://github.com/angular/angular-cli/issues/1465.

Settings paths in tsconfig as shown in https://github.com/angular/angular-cli/issues/865#issuecomment-225812336 should work. We've also removed barrels (they've become a bit unecessary with NgModules) so the problem @Blasz was experiencing with referencing them shouldn't be an overall thing.

Blasz commented 8 years ago

@filipesilva Note that https://github.com/angular/angular-cli/issues/865#issuecomment-225812336 no longer works for me in the majority of cases with the webpack version. I can no longer import files from using the root-relative path when the file being imported contains other common imports (which it will in 95% of cases). It looks like a false positive circular-dependency related bug.

filipesilva commented 8 years ago

@Blasz is that a CLI or a typescript bug though?

Blasz commented 8 years ago

@filipesilva I'm not quite sure but would lean towards CLI rather than typescript since the errors are being thrown by the webpack module resolution code rather than the typescript compiler. I'll try to investigate further and lodge a proper issue once I verify what the issue is exactly.

filipesilva commented 8 years ago

@Blasz appreciated!

dedeibel commented 7 years ago

@Blasz thanks a lot.

Had to write a little script to convert our existing import paths. Maybe someone can use it too: https://gist.github.com/dedeibel/65ebfc4ccb53a758952e4d812c832831 (perl)

angular-automatic-lock-bot[bot] commented 5 years ago

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.