├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── example
├── component-server
│ ├── app.js
│ ├── bin
│ │ └── www
│ ├── public
│ │ └── stylesheets
│ │ │ └── style.css
│ ├── routes
│ │ ├── index.js
│ │ └── users.js
│ └── views
│ │ ├── clock.ejs
│ │ ├── error.ejs
│ │ └── navigator.ejs
├── index-server
│ ├── app.js
│ ├── bin
│ │ └── www
│ ├── public
│ │ └── stylesheets
│ │ │ └── style.css
│ ├── routes
│ │ ├── index.js
│ │ └── users.js
│ └── views
│ │ ├── error.ejs
│ │ └── index.ejs
├── jadeWithPostHTML.js
├── list-server
│ ├── app.js
│ ├── bin
│ │ └── www
│ ├── public
│ │ └── stylesheets
│ │ │ └── style.css
│ ├── routes
│ │ ├── index.js
│ │ └── users.js
│ └── views
│ │ ├── error.ejs
│ │ └── index.ejs
└── package.json
├── gulp-test
├── gulpfile.js
├── html
│ ├── hello-world.html
│ └── index.html
└── package.json
├── package.json
├── src
├── LinkImport.js
└── index.js
└── test
├── LinkImport-spec.js
├── fixtures
├── Caculator.html
├── hello-world.html
├── index.html
└── result.txt
└── index-spec.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 | .nyc_output
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
28 | node_modules
29 | dest
30 | package-lock.json
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - "12"
5 | - "10"
6 | - "8"
7 | after_success:
8 | - npm run coveralls
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Zhi Cun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # posthtml-web-component
2 |
3 | [](https://badge.fury.io/js/posthtml-web-component)
4 | [](https://travis-ci.org/posthtml/posthtml-web-component.svg?branch=master)
5 | [](https://coveralls.io/github/posthtml/posthtml-web-component)
6 |
7 | [PostHTML](https://github.com/posthtml/posthtml) plugin for Server Side Web Component Render.
8 |
9 | ## Feature
10 |
11 | - Base Web Component Server Side Rending
12 | - Component as a Sevice
13 |
14 | ## Advantage
15 |
16 | ## Explanation
17 |
18 | ### Web Component
19 |
20 | Must mention that `Web Components` supported by `posthtml-web-component` don't completely follow the [Web Components](http://www.w3.org/TR/components-intro/) draft.
21 |
22 | A typical posthtml web component looks as following:
23 |
24 | ```html
25 |
26 |
45 |
46 |
47 |
HH
48 |
MM
49 |
SS
50 |
51 |
52 |
68 | ```
69 |
70 | This is a runnable component itself. Consider there is a `index.html`:
71 |
72 | ```html
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | ```
87 |
88 | After `posthtml-web-component`'s transforming:
89 |
90 | ```html
91 |
92 |
93 |
94 |
95 |
96 |
97 |
116 |
117 |
118 |
119 |
HH
120 |
MM
121 |
SS
122 |
123 |
124 |
HH
125 |
MM
126 |
SS
127 |
128 |
144 |
145 |
146 | ```
147 |
148 | Work fine!
149 |
150 | ### LinkImport
151 |
152 | We have two types of `LinkImport`, local and remote.
153 |
154 | ```html
155 |
156 |
157 |
158 |
159 |
160 | ```
161 |
162 | The difference of these two types is that remote `LinkImport` could call a remote service, this is to say remote `LinkImport` could be dynamic.
163 |
--------------------------------------------------------------------------------
/example/component-server/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var path = require('path');
3 | var favicon = require('serve-favicon');
4 | var logger = require('morgan');
5 | var cookieParser = require('cookie-parser');
6 | var bodyParser = require('body-parser');
7 |
8 | var routes = require('./routes/index');
9 | var users = require('./routes/users');
10 |
11 | var app = express();
12 |
13 | // view engine setup
14 | app.set('views', path.join(__dirname, 'views'));
15 | // app.engine('ejs', require('../jadeWithPostHTML'));
16 | app.set('view engine', 'ejs');
17 |
18 | // uncomment after placing your favicon in /public
19 | //app.use(favicon(__dirname + '/public/favicon.ico'));
20 | app.use(logger('dev'));
21 | app.use(bodyParser.json());
22 | app.use(bodyParser.urlencoded({ extended: false }));
23 | app.use(cookieParser());
24 | app.use(express.static(path.join(__dirname, 'public')));
25 |
26 | app.use('/', routes);
27 | app.use('/users', users);
28 |
29 | // catch 404 and forward to error handler
30 | app.use(function(req, res, next) {
31 | var err = new Error('Not Found');
32 | err.status = 404;
33 | next(err);
34 | });
35 |
36 | // error handlers
37 |
38 | // development error handler
39 | // will print stacktrace
40 | if (app.get('env') === 'development') {
41 | app.use(function(err, req, res, next) {
42 | res.status(err.status || 500);
43 | res.render('error', {
44 | message: err.message,
45 | error: err
46 | });
47 | });
48 | }
49 |
50 | // production error handler
51 | // no stacktraces leaked to user
52 | app.use(function(err, req, res, next) {
53 | res.status(err.status || 500);
54 | res.render('error', {
55 | message: err.message,
56 | error: {}
57 | });
58 | });
59 |
60 |
61 | module.exports = app;
62 |
--------------------------------------------------------------------------------
/example/component-server/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | var debug = require('debug')('index-server');
3 | var app = require('../app');
4 |
5 | app.set('port', process.env.PORT || 3000);
6 |
7 | var server = app.listen(app.get('port'), function() {
8 | debug('Express server listening on port ' + server.address().port);
9 | });
10 |
--------------------------------------------------------------------------------
/example/component-server/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00B7FF;
8 | }
--------------------------------------------------------------------------------
/example/component-server/routes/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | /* GET navigator component. */
5 | router.get('/navigator', function(req, res) {
6 | res.render('navigator', {
7 | current: req.query.current || 'home'
8 | });
9 | });
10 |
11 | /* GET clock component. */
12 | router.get('/clock', function(req, res) {
13 | res.render('clock');
14 | });
15 |
16 | module.exports = router;
17 |
--------------------------------------------------------------------------------
/example/component-server/routes/users.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | /* GET users listing. */
5 | router.get('/', function(req, res) {
6 | res.send('respond with a resource');
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/example/component-server/views/clock.ejs:
--------------------------------------------------------------------------------
1 |
20 |
21 |
HH
22 |
MM
23 |
SS
24 |
25 |
41 |
--------------------------------------------------------------------------------
/example/component-server/views/error.ejs:
--------------------------------------------------------------------------------
1 | <%= message %>
2 | <%= error.status %>
3 | <%= error.stack %>
4 |
--------------------------------------------------------------------------------
/example/component-server/views/navigator.ejs:
--------------------------------------------------------------------------------
1 |
10 |
14 |
17 |
--------------------------------------------------------------------------------
/example/index-server/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var path = require('path');
3 | var favicon = require('serve-favicon');
4 | var logger = require('morgan');
5 | var cookieParser = require('cookie-parser');
6 | var bodyParser = require('body-parser');
7 |
8 | var routes = require('./routes/index');
9 | var users = require('./routes/users');
10 |
11 | var app = express();
12 |
13 | // view engine setup
14 | app.set('views', path.join(__dirname, 'views'));
15 | app.engine('ejs', require('../jadeWithPostHTML'));
16 | app.set('view engine', 'ejs');
17 |
18 | // uncomment after placing your favicon in /public
19 | //app.use(favicon(__dirname + '/public/favicon.ico'));
20 | app.use(logger('dev'));
21 | app.use(bodyParser.json());
22 | app.use(bodyParser.urlencoded({ extended: false }));
23 | app.use(cookieParser());
24 | app.use(express.static(path.join(__dirname, 'public')));
25 |
26 | app.use('/', routes);
27 | app.use('/users', users);
28 |
29 | // catch 404 and forward to error handler
30 | app.use(function(req, res, next) {
31 | var err = new Error('Not Found');
32 | err.status = 404;
33 | next(err);
34 | });
35 |
36 | // error handlers
37 |
38 | // development error handler
39 | // will print stacktrace
40 | if (app.get('env') === 'development') {
41 | app.use(function(err, req, res, next) {
42 | res.status(err.status || 500);
43 | res.render('error', {
44 | message: err.message,
45 | error: err
46 | });
47 | });
48 | }
49 |
50 | // production error handler
51 | // no stacktraces leaked to user
52 | app.use(function(err, req, res, next) {
53 | res.status(err.status || 500);
54 | res.render('error', {
55 | message: err.message,
56 | error: {}
57 | });
58 | });
59 |
60 |
61 | module.exports = app;
62 |
--------------------------------------------------------------------------------
/example/index-server/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | var debug = require('debug')('index-server');
3 | var app = require('../app');
4 |
5 | app.set('port', process.env.PORT || 5000);
6 |
7 | var server = app.listen(app.get('port'), function() {
8 | debug('Express server listening on port ' + server.address().port);
9 | });
10 |
--------------------------------------------------------------------------------
/example/index-server/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00B7FF;
8 | }
--------------------------------------------------------------------------------
/example/index-server/routes/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | /* GET home page. */
5 | router.get('/', function(req, res) {
6 | res.render('index', { title: 'Index Server' });
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/example/index-server/routes/users.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | /* GET users listing. */
5 | router.get('/', function(req, res) {
6 | res.send('respond with a resource');
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/example/index-server/views/error.ejs:
--------------------------------------------------------------------------------
1 | <%= message %>
2 | <%= error.status %>
3 | <%= error.stack %>
4 |
--------------------------------------------------------------------------------
/example/index-server/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= title %>
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Welcome to <%= title %>
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/example/jadeWithPostHTML.js:
--------------------------------------------------------------------------------
1 | var posthtml = require('posthtml')
2 | module.exports = function (path, options, callback) {
3 |
4 | var html = require('ejs').renderFile(path, options, function(err, html) {
5 | if (err) {
6 | return callback(err)
7 | }
8 | posthtml([
9 | require('../src/index')({
10 | hostURI:path
11 | })
12 | ])
13 | .process(html)
14 | .then(function (result) {
15 | if (typeof callback === 'function') {
16 | var res;
17 | try {
18 | res = result.html;
19 | } catch (ex) {
20 | return callback(ex);
21 | }
22 | return callback(null, res);
23 | }
24 | });
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/example/list-server/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var path = require('path');
3 | var favicon = require('serve-favicon');
4 | var logger = require('morgan');
5 | var cookieParser = require('cookie-parser');
6 | var bodyParser = require('body-parser');
7 |
8 | var routes = require('./routes/index');
9 | var users = require('./routes/users');
10 |
11 | var app = express();
12 |
13 | // view engine setup
14 | app.set('views', path.join(__dirname, 'views'));
15 | app.engine('ejs', require('../jadeWithPostHTML'));
16 | app.set('view engine', 'ejs');
17 |
18 | // uncomment after placing your favicon in /public
19 | //app.use(favicon(__dirname + '/public/favicon.ico'));
20 | app.use(logger('dev'));
21 | app.use(bodyParser.json());
22 | app.use(bodyParser.urlencoded({ extended: false }));
23 | app.use(cookieParser());
24 | app.use(express.static(path.join(__dirname, 'public')));
25 |
26 | app.use('/', routes);
27 | app.use('/users', users);
28 |
29 | // catch 404 and forward to error handler
30 | app.use(function(req, res, next) {
31 | var err = new Error('Not Found');
32 | err.status = 404;
33 | next(err);
34 | });
35 |
36 | // error handlers
37 |
38 | // development error handler
39 | // will print stacktrace
40 | if (app.get('env') === 'development') {
41 | app.use(function(err, req, res, next) {
42 | res.status(err.status || 500);
43 | res.render('error', {
44 | message: err.message,
45 | error: err
46 | });
47 | });
48 | }
49 |
50 | // production error handler
51 | // no stacktraces leaked to user
52 | app.use(function(err, req, res, next) {
53 | res.status(err.status || 500);
54 | res.render('error', {
55 | message: err.message,
56 | error: {}
57 | });
58 | });
59 |
60 |
61 | module.exports = app;
62 |
--------------------------------------------------------------------------------
/example/list-server/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | var debug = require('debug')('index-server');
3 | var app = require('../app');
4 |
5 | app.set('port', process.env.PORT || 4000);
6 |
7 | var server = app.listen(app.get('port'), function() {
8 | debug('Express server listening on port ' + server.address().port);
9 | });
10 |
--------------------------------------------------------------------------------
/example/list-server/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00B7FF;
8 | }
--------------------------------------------------------------------------------
/example/list-server/routes/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | /* GET home page. */
5 | router.get('/', function(req, res) {
6 | res.render('index', { title: 'List Server' });
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/example/list-server/routes/users.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | /* GET users listing. */
5 | router.get('/', function(req, res) {
6 | res.send('respond with a resource');
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/example/list-server/views/error.ejs:
--------------------------------------------------------------------------------
1 | <%= message %>
2 | <%= error.status %>
3 | <%= error.stack %>
4 |
--------------------------------------------------------------------------------
/example/list-server/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= title %>
5 |
6 |
7 |
8 |
9 |
10 |
11 | Welcome to <%= title %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compnent-server",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www"
7 | },
8 | "dependencies": {
9 | "express": "~4.9.0",
10 | "body-parser": "~1.8.1",
11 | "cookie-parser": "~1.3.3",
12 | "morgan": "~1.3.0",
13 | "serve-favicon": "~2.1.3",
14 | "debug": "~2.0.0",
15 | "ejs": "~0.8.5"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/gulp-test/gulpfile.js:
--------------------------------------------------------------------------------
1 | var posthtml = require('gulp-posthtml');
2 | var gulp = require('gulp');
3 |
4 | gulp.task('posthtml', function (done) {
5 | var plugins = [
6 | require('../')()
7 | ];
8 | return gulp.src('./html/index.html')
9 | .pipe(posthtml(plugins))
10 | .pipe(gulp.dest('./dest'));
11 | });
12 |
--------------------------------------------------------------------------------
/gulp-test/html/hello-world.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | Hello :)
10 |
11 |
12 |
60 |
--------------------------------------------------------------------------------
/gulp-test/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | hello-world
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/gulp-test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gulp-test",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "gulpfile.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "gulp": "^3.9.0",
13 | "gulp-posthtml": "^1.5.2"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "posthtml-web-component",
3 | "version": "0.1.0",
4 | "description": "Server Side Web Component Render.",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "test": "nyc mocha",
8 | "coverage": "nyc report --reporter=text-lcov | coveralls"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/island205/posthtml-web-component.git"
13 | },
14 | "keywords": [
15 | "web",
16 | "component",
17 | "posthtml",
18 | "posthtml-plugin",
19 | "html",
20 | "webcomponent",
21 | "node"
22 | ],
23 | "author": "island205@gmail.com",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/island205/posthtml-web-component/issues"
27 | },
28 | "homepage": "https://github.com/island205/posthtml-web-component#readme",
29 | "dependencies": {
30 | "debug": "^2.2.0",
31 | "posthtml": "^0.11.6",
32 | "request": "^2.67.0"
33 | },
34 | "devDependencies": {
35 | "chai": "^3.4.1",
36 | "coveralls": "^2.13.3",
37 | "mocha": "^6.2.0",
38 | "nyc": "^14.1.1"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/LinkImport.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var posthtml = require('posthtml')
3 | var request = require('request')
4 | var debug = require('debug')('posthtml-web-component:LinkImport')
5 | var fs = require('fs')
6 | var url = require('url')
7 |
8 | function LinkImport(customElementTagName, uri, originURI) {
9 | this.customElementTagName = customElementTagName
10 | this.uri = uri
11 | this.originURI = originURI
12 | }
13 |
14 | LinkImport.prototype.load = function() {
15 | debug('load LinkImport', this.customElementTagName)
16 | return new Promise(function (resolve, reject) {
17 | if (/^http:\/\//.test(this.uri)) {
18 | request(this.uri, function(error, response, body) {
19 | debug('LinkImport', this.customElementTagName, 'loaded')
20 | if (error) {
21 | reject(error)
22 | } else {
23 | this.source = body
24 | resolve('loaded')
25 | }
26 | }.bind(this))
27 | } else {
28 | fs.readFile(this.uri, 'utf-8', function (error, data) {
29 | debug('LinkImport', this.customElementTagName, 'loaded')
30 | if (error) {
31 | reject(error)
32 | } else {
33 | this.source = data
34 | resolve('loaded')
35 | }
36 | }.bind(this))
37 | }
38 | }.bind(this))
39 | }
40 |
41 | LinkImport.prototype.prepare = function () {
42 | var parts = this.parts = {
43 | styles: [],
44 | scripts: [],
45 | html: null
46 | }
47 | posthtml().use(function(tree) {
48 | tree.walk(function (node) {
49 | if (node.tag === 'script') {
50 | parts.scripts.push(node)
51 | // remove script node from template
52 | return undefined
53 | } else if (node.tag === 'style' || (node.tag === 'link' && node.attrs.rel ==='stylesheet')) {
54 | parts.styles.push(node)
55 | // remove style node from template
56 | return undefined
57 | } else if (node.tag === 'template') {
58 | // if LinkImport has an template tag, use it's innerHTML as custom element's html
59 | parts.html = node.content
60 | }
61 | return node
62 | })
63 | // if no template tag, use itself as custom element's html
64 | this.parts.html || (this.parts.html = tree)
65 |
66 | // https://github.com/posthtml/posthtml-render/pull/2
67 | this.parts.html = {
68 | tag: 'div',
69 | attrs: {
70 | 'class': this.getCustomElementTagName()
71 | },
72 | content: this.parts.html
73 | }
74 |
75 | }.bind(this)).process(this.source, {sync: true})
76 | }
77 | LinkImport.parse = function (node, options) {
78 | if (!(options && options.hostURI)) {
79 | throw new Error('The hostURI is needed in options')
80 | }
81 | var customElementTagName, uri, originURI
82 | originURI = node.attrs.href
83 | var pathname
84 | if (/^(http|https):\/\//.test(originURI)) {
85 | uri = originURI
86 | pathname = url.parse(uri).pathname
87 | } else {
88 | uri = pathname = path.resolve(path.dirname(options.hostURI), originURI)
89 | }
90 | customElementTagName = path.parse(pathname).name
91 | return new LinkImport(customElementTagName, uri, originURI)
92 | }
93 |
94 |
95 | LinkImport.prototype.loaded = function () {
96 | return !!this.source
97 | }
98 |
99 | LinkImport.prototype.getCustomElementTagName = function () {
100 | return this.customElementTagName
101 | }
102 |
103 | LinkImport.prototype.getStyles = function () {
104 | return this.parts.styles
105 | }
106 |
107 | LinkImport.prototype.getScripts = function () {
108 | return this.parts.scripts
109 | }
110 |
111 | LinkImport.prototype.getHTML = function () {
112 | return this.parts.html
113 | }
114 |
115 | module.exports = LinkImport
116 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | var LinkImport = require('./LinkImport')
2 | var debug = require('debug')('posthtml-web-component:index')
3 |
4 | module.exports = function (options) {
5 | options = options || {}
6 | return function webComponent(tree, cb) {
7 | var LinkImports = []
8 | tree.walk(function (node) {
9 | if (node.tag === 'link' &&
10 | node.attrs.rel === 'import' &&
11 | node.attrs.href) {
12 | LinkImports.push(LinkImport.parse(node, {
13 | hostURI: options.hostURI || tree.options.path || ''
14 | }))
15 | // remove LinkImport from origin html
16 | return undefined
17 | }
18 | return node
19 | })
20 | debug('parse all LinkImports', LinkImports)
21 | Promise.all(LinkImports.map(function (linkImport) {
22 | return linkImport.load()
23 | })).then(onAllLoaded, onAllLoaded)
24 |
25 | function onAllLoaded() {
26 | debug('onAllLoaded')
27 | var resources = {
28 | styles: [],
29 | scripts: []
30 | }
31 | LinkImports.filter(function (linkImport) {
32 | return linkImport.loaded()
33 | }).reduce(function (resources, currentLinkImport) {
34 | currentLinkImport.prepare()
35 | resources.styles.push.apply(resources.styles, currentLinkImport.getStyles())
36 | resources.scripts.push.apply(resources.scripts, currentLinkImport.getScripts())
37 | tree.match({tag: currentLinkImport.getCustomElementTagName()}, function (node) {
38 | return currentLinkImport.getHTML()
39 | })
40 | return resources
41 | }, resources)
42 | debug('prepare all resources', resources, 'done')
43 | tree.walk(function(node) {
44 | if (node && node.tag === 'head') {
45 | node.content.push.apply(node.content, resources.styles)
46 | }
47 | if (node && node.tag === 'body') {
48 | node.content.push.apply(node.content, resources.scripts)
49 | }
50 | return node
51 | })
52 | cb(null, tree)
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/test/LinkImport-spec.js:
--------------------------------------------------------------------------------
1 | var posthtml = require('posthtml')
2 | var fs = require('fs')
3 | var path = require('path')
4 | var expect = require('chai').expect
5 |
6 | var LinkImport = require('../src/LinkImport')
7 |
8 | function fixture(filePath) {
9 | return fs.readFileSync(path.join(__dirname, filePath), 'utf-8')
10 | }
11 |
12 | var indexHTML = fixture('fixtures/index.html')
13 |
14 | describe('LinkImport', function () {
15 | describe('.parse', function () {
16 | it('should parse LinkImport info from an relative link node', function () {
17 | var node = {
18 | tag: 'link',
19 | attrs: {
20 | rel: 'import',
21 | href: 'hello-world.html'
22 | }
23 | }
24 | var linkImport = LinkImport.parse(node, {
25 | hostURI: path.join(__dirname, './fixtures/index.html')
26 | })
27 |
28 | expect({
29 | customElementTagName: linkImport.getCustomElementTagName(),
30 | originURI: linkImport.originURI,
31 | uri: linkImport.uri
32 | }).to.eql({
33 | customElementTagName: 'hello-world',
34 | originURI: 'hello-world.html',
35 | uri: path.join(__dirname, './fixtures/hello-world.html')
36 | })
37 |
38 | })
39 | it('should parse LinkImport info from remote link node', function () {
40 | var node = {
41 | tag: 'link',
42 | attrs: {
43 | rel: 'import',
44 | href: 'https://google.com/hello-world.html'
45 | }
46 | }
47 | var linkImport = LinkImport.parse(node, {
48 | hostURI: path.join(__dirname, './fixtures/index.html')
49 | })
50 |
51 | expect({
52 | customElementTagName: linkImport.getCustomElementTagName(),
53 | originURI: linkImport.originURI,
54 | uri: linkImport.uri
55 | }).to.eql({
56 | customElementTagName: 'hello-world',
57 | originURI: 'https://google.com/hello-world.html',
58 | uri: 'https://google.com/hello-world.html'
59 | })
60 | })
61 | })
62 | describe('.prototype.load', function () {
63 | it('should load file from file system', function (done) {
64 | var linkImport = new LinkImport('', path.join(__dirname, './fixtures/index.html'), '')
65 | linkImport.load()
66 | .then(function () {
67 | expect(linkImport.source).to.eql(indexHTML)
68 | }).then(done, done)
69 | })
70 |
71 | it('should load file from remote server', function(done) {
72 | var linkImport = new LinkImport('', 'http://island205.github.io/ReactUnitTesting/Caculator/', '')
73 | linkImport.load()
74 | .then(function () {
75 | expect(linkImport.source).to.eql(fixture('fixtures/Caculator.html'))
76 | }).then(done, done)
77 | })
78 | })
79 |
80 | describe('.prototype.prepare', function () {
81 |
82 | it('should seperate LinkImport source into style script and html parts', function () {
83 | var linkImport = new LinkImport()
84 | linkImport.source = fixture('fixtures/hello-world.html')
85 | linkImport.prepare()
86 | expect(linkImport.getScripts().length).to.eql(1)
87 | expect(linkImport.getStyles().length).to.eql(1)
88 | expect(linkImport.getHTML()).to.not.be.undefined
89 | })
90 | })
91 | })
92 |
--------------------------------------------------------------------------------
/test/fixtures/Caculator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Caculator
4 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test/fixtures/hello-world.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | Hello :)
10 |
11 |
12 |
60 |
--------------------------------------------------------------------------------
/test/fixtures/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <hello-world>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/result.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <hello-world>
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
22 |
23 |
71 |
72 |
--------------------------------------------------------------------------------
/test/index-spec.js:
--------------------------------------------------------------------------------
1 | var posthtml = require('posthtml')
2 | var fs = require('fs')
3 | var path = require('path')
4 | var expect = require('chai').expect
5 |
6 |
7 | function fixture(filePath) {
8 | return fs.readFileSync(path.join(__dirname, filePath), 'utf-8')
9 | }
10 |
11 | var indexHTML = fixture('fixtures/index.html')
12 |
13 | describe('WebComponent', function () {
14 | it('should parse web component', function (done) {
15 | var webComponent = posthtml().use(require('../src/index')({
16 | hostURI: path.join(__dirname, './fixtures/index.html')
17 | }))
18 | webComponent.process(indexHTML)
19 | .then(function (result) {
20 | expect(result.html).to.eql(fixture('fixtures/result.txt'))
21 | }).then(done, done)
22 | })
23 | })
24 |
--------------------------------------------------------------------------------