Skip to content
This repository has been archived by the owner on Dec 18, 2019. It is now read-only.

Commit

Permalink
Merge branch 'meteor-1.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
MacRusher committed Oct 5, 2015
2 parents 8831c81 + f917998 commit 2fe43ab
Show file tree
Hide file tree
Showing 13 changed files with 2,004 additions and 2,479 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@

## 0.5

### 0.5.0

- [BREAKING CHANGE] Meteor 1.2 is required
- [BREAKING CHANGE] **All non-relative paths to modules must be absolute and start with `/`, `{}` or `{package:name}`**
- [BREAKING CHANGE] There is no more need to configure System's package to load default file, and some settings like `map: {'.': System.normalizeSync('XXX')}` can cause infinite loop!
- [BREAKING CHANGE] `!vars` was rewritten and replaced by `!exports`
- [BREAKING CHANGE] Remove backward compatibility for deprecated package syntax `author:package`
- [BREAKING CHANGE] Remove compatibility for `filename.import` syntax

- Change in internal module naming (Potentially breaking change)
- Make use of new build plugins API
- Option to import file only on selected platform with `@client` `@server` syntax
- Loading a directory (ending module name with `/`) will load `index` module from this directory
- Providing only package name `{me:my-package}` will load index module from package
- Use Meteor Promise polyfill instead of this shipped with SystemJS
- Improve error handling (stack traces are now easier to read when errors are thrown inside module)
- Provide loader as a module instead of overwriting System's methods (but we still overwrite import, normalize etc.)
- Update `SystemJS` to 0.19.0
- Update `babel-compiler` to 5.8.24_1

## 0.4

### 0.4.1
Expand Down
102 changes: 54 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,37 @@ Just add this package to your app:

meteor add universe:modules

### Upgrading from 0.4 to 0.5

Version 0.5 introduces some breaking changes, and most probably your app won't work out of the box.
For more details check CHANGELOG.md

All paths need to be either absolute (starting with `/`, `{}/`, `{author:package}` etc.) or relative (starting with `./`, `../` etc.)

If after upgrade you got error `RangeError: Maximum call stack size exceeded` it can be caused by invalid System's package config.
There is no more need for syntax like:
```
System.config({
packages: {
'{me:my-package}': {
main: 'index',
format: 'register',
map: {
'.': System.normalizeSync('{me:my-package}')
}
}
}
});
```
Instead, index module will be loaded by default if you pass only package name inside brackets, or if you end module name with `/` (you will link to directory and not a file)

## Usage

### Complete app example

If you want to see it in action, see our todo example app:

- Source code: https://github.com/vazco/demo_modules
- Source code: https://github.com/vazco/demo_modules (note: this example is little outdated and will be updated soon)
- Live demo: http://universe-modules-demo.meteor.com

You can also check out great `meteor-react-example` app by [optilude](https://github.com/optilude).
Expand Down Expand Up @@ -103,7 +126,7 @@ If you want to execute this inside Meteor app, you need to use SystemJS API:

Some normal `file.js`:

System.import('finalComponent').then(function(module){
System.import('/finalComponent').then(function(module){

// default export is attached as default property
// all named exports are attached by their name
Expand All @@ -114,37 +137,46 @@ Some normal `file.js`:
});

This assumes that file `finalComponent.import.js` is inside main app directory.
If you have it somewhere else you have to provide full path relative to meteor app directory,
e.g. `client/components/finalComponent`.
If you have it somewhere else you have to provide full path starting with meteor app directory,
e.g. `/client/components/finalComponent`.


### Loading file only on the client or server

Because ES2015 specification won't allow you to write `import` statements inside a condition, you cannot import file selectively only on client or server.

In some cases this could be useful, so we introduced syntax that will allow you to do it. Just add `@client` or `@server` suffix after module name.
On selected platform this will behave like normal import, on the other platform import will return empty module, so every imported variable will be undefined.

### Loading modules from packages

To load files from packages prefix path with full package name in brackets, e.g:

import Lib from '{author:package}/lib'

This syntax will be also introduced in Meteor 1.2 to allow importing less/stylus files between packages.
import foo from '{author:package}'

Packages can also register nice module names in SystemJS.
to load index file from package, or to load selected module:

An example could be [universe:react-bootstrap](https://atmospherejs.com/universe/react-bootstrap).
Once added to Meteor project, you can write:
import foo from '{author:package}/foo'

This syntax was also introduced in Meteor 1.2 to allow importing less/stylus files between packages.

Inside package paths are absolute to package root. To import from main package use `{}/foo` syntax. `{}` Selects main app.

import { Button } from 'bootstrap';
If you wish you can inside package import modules from other packages (you need to have dependencies on them!)

and use components from [ReactBootstrap](https://react-bootstrap.github.io/) packaged for Meteor projects.
### Loading some package exports

### Loading package-level variables
To load exported variables by meteor package, prefix package name like before and add `!vars` on the end(after bracket):
To load variables exported by some Meteor package, add `!exports` after package name in brackets:

```
import {DDP} from '{ddp}!vars'
import {UniCollection, UniUsers} from '{vazco:universe-collection}!vars'
import {DDP} from '{ddp}!exports'
import {UniCollection, UniUsers} from '{universe:collection}!exports'
```
be sure that if you use import from another package, you must have dependency to this package.

Remember that if you want to import from another package, you must have dependency on this package.

### Loading from npm repository

There is extension for this package that adds a possibility of importing from npm repositories.
[universe:modules-npm](https://atmospherejs.com/universe/modules-npm) / [Github repo](https://github.com/vazco/meteor-universe-modules-npm/)

Expand All @@ -159,38 +191,12 @@ You can map alternative name for a module, but remember that you have to provide
// some_config_file.js
System.config({
map: {
myComponent: System.normalizeSync('normal/path/to/my/component')
myComponent: System.normalizeSync('/normal/path/to/my/component')
}
});

// some_component.import.js
import myComponent from 'myComponent'; // this will load component from normal/path/to/my/component

### SystemJS packages

SystemJS has a packages concept that plays well with Meteor idea of packages.

Example usage from [universe:react-bootstrap](https://atmospherejs.com/universe/react-bootstrap):

System.config({
packages: {
bootstrap: {
main: 'main',
format: 'register',
map: {
'.': System.normalizeSync('{universe:react-bootstrap}')
}
}
}
});

This will map:

- `bootstrap` -> `{universe:react-bootstrap}/main` (main is set as a default by... `main` config option :))
- `bootstrap/foo` -> `{universe:react-bootstrap}/foo`
- `bootstrap/foo/bar` -> `{universe:react-bootstrap}/foo/bar`

etc...
import myComponent from 'myComponent'; // this will load component from /normal/path/to/my/component

## Troubleshooting

Expand All @@ -200,7 +206,7 @@ You misspelled import name/path. SystemJS tries to download this file from remot

Check if all files are at their location and import paths are OK.

Unfortunately file loading order is still important!
**Unfortunately file loading order is still important!**

You need to be sure that all `XXX.import.js` files you want to use are loaded before executing `System.import('XXX')`.
This normally isn't a issue as putting them into subdirectory is enough (it doesn't have to be a `lib`!)
Expand All @@ -212,13 +218,13 @@ You also don't have to worry about this when using `import` inside `*.import.js`

### Roadmap

- [ ] Full tests coverage
- [ ] Allow opt-in for other Babel modules (decorators etc)
- [ ] Support for lazy loading modules on the client instead of bundling them with main Meteor app
- [ ] Full tests coverage

### Changelog

You can find changelog in CHANGELOG.md file.
You can find changelog and breaking changes in CHANGELOG.md file.

### Issues

Expand Down
126 changes: 77 additions & 49 deletions build-plugin.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,90 @@
var handler = function (compileStep) {
var source = compileStep.read().toString('utf8');
var outputFile = compileStep.inputPath + '.js';
class UniverseBabelCompiler extends BabelCompiler {

var path = compileStep.inputPath.split('.import.');
var moduleId = path[0];

if(process.platform === 'win32') {
// windows support, replace backslashes with forward slashes
moduleId = moduleId.replace(/\\/g, '/');
processFilesForTarget (inputFiles) {
inputFiles.forEach(this.processFile);
}

if (compileStep.packageName) {
// inside package, prefix module
moduleId = '{' + compileStep.packageName + '}/' + moduleId;
}
processFile (inputFile) {

var extraWhitelist = [
'es6.modules',
// @todo make this configurable:
'es7.decorators'
];
if (path[1] === 'jsx') {
// add support for React in *.import.jsx files
extraWhitelist.push('react');
}
// Full contents of the file as a string
const source = inputFile.getContentsAsString();

// Relative path of file to the package or app root directory (always uses forward slashes)
const filePath = inputFile.getPathInPackage();

// Options from api.addFile
const fileOptions = inputFile.getFileOptions();

// Name of the package or null if the file is not in a package.
const packageName = inputFile.getPackageName();

// moduleId - Module name (full patch without extension)
// ext - File extension (either js or jsx)
let [moduleId, ext] = filePath.split('.import.');

// prefix module name accordingly
if (packageName) {
// inside package
moduleId = '/_modules_/packages/' + packageName.replace(':', '/') + '/' + moduleId;
} else {
// inside main app
moduleId = '/_modules_/app/' + moduleId;
}

try {
var result = Babel.transformMeteor(source, {
const extraWhitelist = [
'es6.modules',
// @todo make this configurable:
'es7.decorators',
'regenerator'
];

if (ext === 'jsx') {
// add support for React in *.import.jsx files
extraWhitelist.push('react');
}

const babelDefaultOptions = Babel.getDefaultOptions(this.extraFeatures);

const babelOptions = _({}).extend(babelDefaultOptions, {
sourceMap: true,
filename: compileStep.pathForSourceMap,
sourceMapName: compileStep.pathForSourceMap,
extraWhitelist: extraWhitelist,
filename: filePath,
sourceFileName: '/' + filePath,
sourceMapName: '/' + filePath + '.map',
modules: 'system',
moduleIds: true,
moduleId: moduleId
moduleId,
whitelist: _.union(babelDefaultOptions.whitelist, extraWhitelist)
});
} catch (e) {
if (e.loc) {
// Babel error
compileStep.error({
message: e.message,
sourcePath: compileStep.inputPath,
line: e.loc.line,
column: e.loc.column
});
return;
} else {

try {
var result = Babel.compile(source, babelOptions);
} catch (e) {
if (e.loc) {
inputFile.error({
message: e.message,
sourcePath: filePath,
line: e.loc.line,
column: e.loc.column
});
return;
}
throw e;
}
}

compileStep.addJavaScript({
path: outputFile,
sourcePath: compileStep.inputPath,
data: result.code,
sourceMap: JSON.stringify(result.map)
});
};
inputFile.addJavaScript({
sourcePath: filePath,
path: filePath,
data: result.code,
hash: result.hash,
sourceMap: result.map,
bare: !!fileOptions.bare
});
}
}

Plugin.registerSourceHandler('import.js', handler);
Plugin.registerSourceHandler('import.jsx', handler);
Plugin.registerCompiler({
extensions: ['import.js', 'import.jsx'],
filenames: []
}, function () {
return new UniverseBabelCompiler();
});
39 changes: 39 additions & 0 deletions extensions/exports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Following script allows to import variables exported from Meteor packages
* @example `import {UniCollection, UniUsers} from '{universe:collection}!exports'`
* @example `import {DDP} from '{ddp}!exports'`
*/

System.set('exports', System.newModule({
locate ({name, metadata}) {
return new Promise((resolve, reject) => {
let [, dir,, author, packageName] = name.split('/');

// check if we're in valid namespace
if (dir !== '_modules_') {
reject(new Error('[Universe Modules]: trying to get exported values from invalid package: ' + name));
return;
}

// construct package name in Meteor's format
let meteorPackageName = (author ? author + ':' : '') + packageName;

if (!Package[meteorPackageName]) {
// ups, there is no such package
reject(new Error(`[Universe Modules]: Cannot find Meteor package exports for {${meteorPackageName}}`));
return;
}

// everything is ok, proceed
metadata.meteorPackageName = meteorPackageName;
resolve(name);
});
},
fetch () {
// we don't need to fetch anything for this to work
return '';
},
instantiate ({metadata}) {
return Package[metadata.meteorPackageName];
}
}));
Loading

0 comments on commit 2fe43ab

Please sign in to comment.