fix(aio): support IE via conditionally loaded polyfills

Closes #16519
This commit is contained in:
Peter Bacon Darwin 2017-05-15 21:22:06 +01:00 committed by Pete Bacon Darwin
parent 8a0e5659c0
commit be9e8b99ff
7 changed files with 81 additions and 185 deletions

View File

@ -30,6 +30,7 @@ Here are the most important tasks you might need to use:
* `yarn generate-plunkers` - generate the plunker files that are used by the `live-example` tags in the docs. * `yarn generate-plunkers` - generate the plunker files that are used by the `live-example` tags in the docs.
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs. * `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
* `yarn build-ie-polyfills` - generates a js file of polyfills that can be loaded in Internet Explorer.
## Using ServiceWorker locally ## Using ServiceWorker locally

View File

@ -13,7 +13,7 @@
"test": "yarn check-env && ng test", "test": "yarn check-env && ng test",
"pree2e": "yarn ~~update-webdriver", "pree2e": "yarn ~~update-webdriver",
"e2e": "yarn check-env && ng e2e --no-webdriver-update", "e2e": "yarn check-env && ng e2e --no-webdriver-update",
"setup": "yarn && yarn boilerplate:add && yarn generate-plunkers && yarn generate-zips && yarn docs", "setup": "yarn && yarn build-ie-polyfills && yarn boilerplate:add && yarn generate-plunkers && yarn generate-zips && yarn docs",
"pretest-pwa-score-local": "yarn build", "pretest-pwa-score-local": "yarn build",
"test-pwa-score-local": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score -- http://localhost:4200 90\"", "test-pwa-score-local": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score -- http://localhost:4200 90\"",
"test-pwa-score": "node scripts/test-pwa-score", "test-pwa-score": "node scripts/test-pwa-score",
@ -34,7 +34,8 @@
"generate-zips": "node ./tools/example-zipper/generateZips", "generate-zips": "node ./tools/example-zipper/generateZips",
"sw-manifest": "ngu-sw-manifest --dist dist --in ngsw-manifest.json --out dist/ngsw-manifest.json", "sw-manifest": "ngu-sw-manifest --dist dist --in ngsw-manifest.json --out dist/ngsw-manifest.json",
"sw-copy": "cp node_modules/@angular/service-worker/bundles/worker-basic.min.js dist/", "sw-copy": "cp node_modules/@angular/service-worker/bundles/worker-basic.min.js dist/",
"postinstall": "PATCH_LOCK=node_modules/@angular/cli/models/webpack-configs/.patched; if [ ! -e $PATCH_LOCK ]; then touch $PATCH_LOCK; patch -p0 -i tools/cli-patches/ngo-loader.patch && patch -p0 -i node_modules/purify/angular-cli.patch; fi" "postinstall": "PATCH_LOCK=node_modules/@angular/cli/models/webpack-configs/.patched; if [ ! -e $PATCH_LOCK ]; then touch $PATCH_LOCK; patch -p0 -i tools/cli-patches/ngo-loader.patch && patch -p0 -i node_modules/purify/angular-cli.patch; fi",
"build-ie-polyfills": "webpack -p src/ie-polyfills.js src/generated/ie-polyfills.min.js"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
@ -50,12 +51,14 @@
"@angular/platform-server": "next", "@angular/platform-server": "next",
"@angular/router": "next", "@angular/router": "next",
"@angular/service-worker": "^1.0.0-beta.12", "@angular/service-worker": "^1.0.0-beta.12",
"classlist.js": "^1.1.20150312",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"ng-pwa-tools": "^0.0.10", "ng-pwa-tools": "^0.0.10",
"ngo-loader": "alxhub/ngo", "ngo-loader": "alxhub/ngo",
"purify": "igorminar/purify", "purify": "igorminar/purify",
"rxjs": "^5.2.0", "rxjs": "^5.2.0",
"tslib": "^1.7.0", "tslib": "^1.7.0",
"web-animations-js": "^2.2.5",
"zone.js": "^0.8.4" "zone.js": "^0.8.4"
}, },
"devDependencies": { "devDependencies": {
@ -108,6 +111,7 @@
"unist-util-visit": "^1.1.1", "unist-util-visit": "^1.1.1",
"vrsource-tslint-rules": "^4.0.1", "vrsource-tslint-rules": "^4.0.1",
"watchr": "^3.0.1", "watchr": "^3.0.1",
"webpack": "^2.5.1",
"yargs": "^7.0.2" "yargs": "^7.0.2"
} }
} }

View File

@ -1,159 +0,0 @@
# Design Notes
## Application State
Apart from loading up the navigation and search data, there is only one significant state in the app, which is the document being viewed.
Other state, such as highlighting the current navigation items and bread crumbs can be derived from that state.
The simplest indication of this state is the current `Location#path`, which acts as the single point of truth in the application.
* The document to display is computed from the `path`.
* Navigation to display different documents is achieved by updating the `path`.
* Whether a navigation item should be highlighted as active is computed from the `path`
Navigation between documents can be achieved through changes to the `Location`.
## Concepts
There are three main concepts in the application: documents, navigation and search.
* The document data includes the contents of the document for rendering.
* The search data includes keywords used for building the search index.
* The navigation data contains information about the visual trees that can be rendered in the app to navigate to the documents.
### Documents
Information about documents to be displayed are provided as data that can be fetched from the server.
There are two parts to the data for each document: `DocumentContents`.
* `DocumentContents` contains the actual document that will be rendered. This can be loaded (and then cached) lazily only when the document is to be viewed.
Each document is uniquely identified by the `path` that is also used to fetch its `DocumentContents`.
The `DocumentContents` are stored in JSON files on the server. They are loaded lazily on demand and then cached.
```ts
interface DocumentContents {
title: string;
contents: string;
}
type DocumentContentsJSONFile = Array<DocumentContents>;
```
### Navigation
Within an application there can be multiple navigational views for displaying links to documents.
For example, there might be views for the top level `Toolbar` and for the more detailed `SideBar` navigation.
Each view is represented as a tree of `NavigationNode`s. Each `NavigationNode` may, optionally, have a `url`, a `path` and `children`.
```ts
interface NavigationNode {
title: string;
url?: string;
path?: string;
tooltip?: string;
target?: string;
children?: NavigationNode[];
}
```
The `url` property represents a link to an external url outside of the app.
The `path` property identifies the document to display for this navigation `url`. It is used both as the URL segment in the browser and in the request to fetch the document contents.
It is not valid to have both `url` and `path` properties on a single node.
If neither `url` nor `path` properties are then the node is a pure container, in which case it ought to have
The `NavigationNode` data is stored in a JSON file that is fetched from the server at startup
```ts
interface NavigationViews {
[name: string]: NavigationNode;
}
```
The mapping of a URL back to `NavigationNode` is computed by inverting all the `NavigationNode` structures to produce a `NavigationMap` of URL to node.
```ts
interface NavigationMap {
[url: string]: NavigationMapItem
}
```
Each `NavigationMapItem` contains information for updating the current navigation views and also caches the `DocumentContents` once it has been loaded.
```ts
interface NavigationMapItem {
node: NavigationNode;
parents: NavigationNode[];
document?: DocumentContents;
}
```
### Search
The `SearchTerms` are stored in a single JSON file that is loaded by the search WebWorker at startup and used to generate the search index.
```ts
interface SearchTerms {
url: string;
title: string;
body: string;
}
type SearchTermsJSONFile = Array<SearchTerms>;
```
## LocationService
Since the core Angular `Location` service does not expose a subject, nor emits events when the location is changed programmatically,
we have our own `LocationService` that does this for us.
```
export class LocationService {
currentUrl: Observable<string>;
go(url: string): void
}
```
You can subscribe to the `currentUrl` to be updated when location changes occur.
You should navigate to new documents by calling `go`.
## DocumentService
The `DocumentService` is responsible for monitoring the `LocationService.currentUrl` and updating the `currentDocument`.
When the `currentUrl` changes, the `DocumentService` will fetch the `DocumentContents` from the server if necessary and cache it for the future.
```ts
export class DocumentService {
currentDocument: Observable<DocumentContents>;
}
```
The mapping of URL path to the path to fetch the document contents is simply:
```ts
private computePath(url) {
url = url.startsWith('/') ? url : '/' + url;
return 'generated/docs' + url + '.json';
}
```
## NavigationService
The `NavigationService` is responsible for:
* fetching and exposing the `NavigationViews` for use in displaying navigation UI.
* building the `NavigationMap` from the `NavigationNode` data.
* updating observables for the currently active `NavigationNode`s that can be used to render the navigation UI
when the `LocationService.currentUrl` changes.
```ts
class NavigationService {
navigationViews: Observable<NavigationViews>;
currentNode: Observable<NavigationNode>;
activeNodes: Observable<NavigationNode[]>;
}
```

20
aio/src/ie-polyfills.js Normal file
View File

@ -0,0 +1,20 @@
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
import 'core-js/es6/symbol';
import 'core-js/es6/object';
import 'core-js/es6/function';
import 'core-js/es6/parse-int';
import 'core-js/es6/parse-float';
import 'core-js/es6/number';
import 'core-js/es6/math';
import 'core-js/es6/string';
import 'core-js/es6/date';
import 'core-js/es6/array';
import 'core-js/es6/regexp';
import 'core-js/es6/map';
import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
import 'classlist.js';
/** IE10 and IE11 requires the following to support `@angular/animation`. */
import 'web-animations-js';

View File

@ -23,7 +23,7 @@
<!-- NOTE: These need to be kept in sync with `ngsw-manifest.json`. --> <!-- NOTE: These need to be kept in sync with `ngsw-manifest.json`. -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"rel="stylesheet"> <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- --> <!-- -->
<link rel="manifest" href="pwa-manifest.json"> <link rel="manifest" href="pwa-manifest.json">
@ -35,8 +35,17 @@
</script> </script>
<script async src='https://www.google-analytics.com/analytics.js'></script> <script async src='https://www.google-analytics.com/analytics.js'></script>
<!-- End Google Analytics --> <!-- End Google Analytics -->
<script>
if (window.document.documentMode) {
var script = document.createElement('script');
script.src = 'generated/ie-polyfills.min.js';
document.head.appendChild(script);
}
</script>
</head> </head>
<body> <body>
<aio-shell></aio-shell> <aio-shell></aio-shell>
<noscript> <noscript>

View File

@ -18,27 +18,14 @@
* BROWSER POLYFILLS * BROWSER POLYFILLS
*/ */
/** IE9, IE10 and IE11 requires all of the following polyfills. **/ /**
// import 'core-js/es6/symbol'; * INTERNET EXPLORER
// import 'core-js/es6/object'; *
// import 'core-js/es6/function'; * All the Internet Explorer polyfills are defined separately, in the `ie-polyfills.js` file.
// import 'core-js/es6/parse-int'; * They are also built separately from the main app, via the `yarn build-ie-polyfills` task.
// import 'core-js/es6/parse-float'; * The output of this build is conditionally loaded (only if we are running IE) in the browser,
// import 'core-js/es6/number'; * and executed before the rest of the application files are executed.
// import 'core-js/es6/math'; */
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/** IE10 and IE11 requires the following to support `@angular/animation`. */
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/** HACK: force import of environment.ts/environment.prod.ts to load env specific polyfills */ /** HACK: force import of environment.ts/environment.prod.ts to load env specific polyfills */
import './environments/environment'; import './environments/environment';

View File

@ -1085,6 +1085,10 @@ clap@^1.0.9:
dependencies: dependencies:
chalk "^1.1.3" chalk "^1.1.3"
classlist.js@^1.1.20150312:
version "1.1.20150312"
resolved "https://registry.yarnpkg.com/classlist.js/-/classlist.js-1.1.20150312.tgz#1d70842f7022f08d9ac086ce69e5b250f2c57789"
clean-css@4.0.x: clean-css@4.0.x:
version "4.0.13" version "4.0.13"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.0.13.tgz#feb2a176062d72a6c3e624d9213cac6a0c485e80" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.0.13.tgz#feb2a176062d72a6c3e624d9213cac6a0c485e80"
@ -7147,11 +7151,11 @@ typescript@2.1:
version "2.1.6" version "2.1.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.1.6.tgz#40c7e6e9e5da7961b7718b55505f9cac9487a607" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.1.6.tgz#40c7e6e9e5da7961b7718b55505f9cac9487a607"
typescript@2.2.0: typescript@2.2.0, "typescript@>=2.0.0 <2.4.0":
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.2.0.tgz#626f2fc70087d2480f21ebb12c1888288c8614e3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.2.0.tgz#626f2fc70087d2480f21ebb12c1888288c8614e3"
"typescript@>=2.0.0 <2.4.0", typescript@^2.2.1: typescript@^2.2.1:
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.2.tgz#f0f045e196f69a72f06b25fd3bd39d01c3ce9984" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.2.tgz#f0f045e196f69a72f06b25fd3bd39d01c3ce9984"
@ -7554,6 +7558,10 @@ wbuf@^1.1.0, wbuf@^1.4.0:
dependencies: dependencies:
minimalistic-assert "^1.0.0" minimalistic-assert "^1.0.0"
web-animations-js@^2.2.5:
version "2.2.5"
resolved "https://registry.yarnpkg.com/web-animations-js/-/web-animations-js-2.2.5.tgz#26ca1b34c1347332a0813f8b2bfe69664efa80aa"
webdriver-js-extender@^1.0.0: webdriver-js-extender@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/webdriver-js-extender/-/webdriver-js-extender-1.0.0.tgz#81c533a9e33d5bfb597b4e63e2cdb25b54777515" resolved "https://registry.yarnpkg.com/webdriver-js-extender/-/webdriver-js-extender-1.0.0.tgz#81c533a9e33d5bfb597b4e63e2cdb25b54777515"
@ -7636,6 +7644,32 @@ webpack-sources@^0.2.3:
source-list-map "^1.1.1" source-list-map "^1.1.1"
source-map "~0.5.3" source-map "~0.5.3"
webpack@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.5.1.tgz#61742f0cf8af555b87460a9cd8bba2f1e3ee2fce"
dependencies:
acorn "^5.0.0"
acorn-dynamic-import "^2.0.0"
ajv "^4.7.0"
ajv-keywords "^1.1.1"
async "^2.1.2"
enhanced-resolve "^3.0.0"
interpret "^1.0.0"
json-loader "^0.5.4"
json5 "^0.5.1"
loader-runner "^2.3.0"
loader-utils "^0.2.16"
memory-fs "~0.4.1"
mkdirp "~0.5.0"
node-libs-browser "^2.0.0"
source-map "^0.5.3"
supports-color "^3.1.0"
tapable "~0.2.5"
uglify-js "^2.8.5"
watchpack "^1.3.1"
webpack-sources "^0.2.3"
yargs "^6.0.0"
webpack@~2.4.0: webpack@~2.4.0:
version "2.4.1" version "2.4.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.4.1.tgz#15a91dbe34966d8a4b99c7d656efd92a2e5a6f6a" resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.4.1.tgz#15a91dbe34966d8a4b99c7d656efd92a2e5a6f6a"