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

Implement loading of CSS Modules. Resolves #1991. #1997

Merged
merged 7 commits into from
Aug 25, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The minimal [1.5KB s.js loader](dist/s.min.js) provides a workflow where code wr
Since the ES module semantics such as live bindings, circular references, contextual metadata, dynamic import and top-level await [can all be fully supported this way](docs/system-register.md#semantics), while supporting CSP and cross-origin support, this workflow can be relied upon as a polyfill-like path.

* Loads and resolves modules as URLs, throwing for bare specifier names (eg `import 'lodash'`) like the native module loader.
* Loads System.register and JSON modules.
* Loads System.register, JSON, and CSS modules.
* Core hookable extensible loader supporting [custom extensions](docs/hooks.md).

#### 2. system.js loader
Expand Down Expand Up @@ -61,6 +61,7 @@ npm install systemjs
* [API](docs/api.md)
* [System.register](docs/system-register.md)
* [Loader Hooks](docs/hooks.md)
* [Module Types](docs/module-types.md)

## Example Usage

Expand Down
87 changes: 87 additions & 0 deletions docs/module-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Module Types

Both system.js and s.js support loading javascript modules, json modules, and css modules.

## Limitations

The browser spec calls for checking a module's mime type to know whether it is JSON, CSS, or Javascript. SystemJS does not do that,
since it has to choose upfront whether to append a script element (for js modules) or make a fetch request (for a JSON/CSS module).
So instead of the mime type, the file extension is used to determine the type of the module.

## Javascript modules

SystemJS supports loading javascript modules that are in the following formats:

| Module Format | s.js | system.js |
| ------------- | ---- | --------- |
| [System.register](/docs/system-register.md) | :heavy_check_mark: | :heavy_check_mark: |
| Global variable | [global extra](/README.md#extras) | :heavy_check_mark: |
| [ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) | [transform extra](/README.md#extras) | [transform extra](/README.md#extras) | | [AMD](https://github.com/amdjs/amdjs-api/wiki/AMD) | [AMD extra](/README.md#extras) | [AMD extra](/README.md#extras) |
| [UMD](https://github.com/umdjs/umd) | [AMD extra](/README.md#extras) | [AMD extra](/README.md#extras) |

## JSON Modules

[JSON modules](https://github.com/whatwg/html/pull/4407) are supported in both s.js and system.js.

### Example

**file.json**
```json
{
"some": "json value"
}
```

```js
System.import('file.json').then(function (module) {
console.log(module.default); // The json as a js object.
});
```

## CSS Modules

[CSS Modules](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/css-modules-v1-explainer.md) are supported in both
s.js and system.js, [when a Constructable Style Sheets polyfill is present for browsers other than Chromium](#constructed-style-sheets-polyfill).

Note that the term CSS Modules refers to two separate things: (1) the browser spec, or (2) the webpack / postcss plugin.
The CSS modules implemented by SystemJS are the browser spec.

### Example
```css
/* file.css */
.brown {
color: brown;
}
```

```js
System.import('file.css').then(function (module) {
const styleSheet = module.default; // A CSSStyleSheet object
document.adoptedStyleSheets = [...document.adoptedStyleSheets, styleSheet]; // now your css is available to be used.
});
```

### Constructed Style Sheets Polyfill

CSS modules export a [Constructable Stylesheet](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet) instance as their
default export when imported.

Currently these are only available in new versions of Chromium based browsers (e.g., Chrome 73+), so usage in any other browsers will require a polyfill, such as the one at https://www.npmjs.com/package/construct-style-sheets-polyfill.

_Note that this polyfill does not currently work in IE11._

The polyfill can be conditionally loaded with an approach like:

```html
<script src="system.js"></script>
<script>
try { new CSSStyleSheet() }
catch (e) {
document.head.appendChild(Object.assign(document.createElement('script'), {
src: 'https://unpkg.com/browse/construct-style-sheets-polyfill@2.1.0/adoptedStyleSheets.min.js'
}));
}
</script>
```

If the polyfill is not included, CSS modules will not work in other browsers.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
],
"devDependencies": {
"bluebird": "^3.5.1",
"construct-style-sheets-polyfill": "^2.1.0",
"esm": "^3.2.25",
"mocha": "^5.2.0",
"rollup": "^0.64.1",
Expand Down
25 changes: 19 additions & 6 deletions src/features/script-load.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ systemJSPrototype.register = function (deps, declare) {
systemJSPrototype.instantiate = function (url, firstParentUrl) {
const loader = this;
if (url.substr(-5) === '.json') {
return fetch(url).then(function (resp) {
return resp.text();
}).then(function (source) {
return [[], function(_export) {
return {execute: function() {_export('default', JSON.parse(source))}};
}];
return loadDynamicModule(url, function (_export, source) {
_export('default', JSON.parse(source));
});
} else if (url.substr(-4) === '.css') {
joeldenning marked this conversation as resolved.
Show resolved Hide resolved
return loadDynamicModule(url, function (_export, source) {
// Relies on a Constructable Stylesheet polyfill
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync(source);
_export('default', stylesheet);
});
joeldenning marked this conversation as resolved.
Show resolved Hide resolved
} else {
return new Promise(function (resolve, reject) {
Expand Down Expand Up @@ -55,3 +58,13 @@ systemJSPrototype.instantiate = function (url, firstParentUrl) {
});
}
};

function loadDynamicModule (url, createExec) {
return fetch(url).then(function (resp) {
return resp.text();
}).then(function (source) {
return [[], function (_export) {
return {execute: createExec(_export, source)};
}];
});
}
6 changes: 6 additions & 0 deletions test/browser/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,10 @@ suite('SystemJS Standard Tests', function() {
assert.equal(m.addTwo(1, 1), 2);
});
});

test('should load a css module', async function () {
const m = await System.import('./css-modules/a.css')
assert.ok(m);
assert.ok(m.default instanceof CSSStyleSheet);
});
});
3 changes: 3 additions & 0 deletions test/fixtures/css-modules/a.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.hello {
background-color: peru;
}
1 change: 1 addition & 0 deletions test/test.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
if (typeof fetch === 'undefined')
document.write('<script src="../node_modules/whatwg-fetch/fetch.js"><\/script>');
</script>
<script src="../node_modules/construct-style-sheets-polyfill/adoptedStyleSheets.js"></script>
<script>
// TODO IE11 URL polyfill testing
// if (typeof URL === 'undefined')
Expand Down