├── .gitignore ├── .npm └── plugin │ └── vulcanize │ ├── .gitignore │ ├── README │ └── npm-shrinkwrap.json ├── .versions ├── README.md ├── package.js ├── versions.json ├── vulcanize-tests.js └── vulcanize.js /.gitignore: -------------------------------------------------------------------------------- 1 | .build* 2 | -------------------------------------------------------------------------------- /.npm/plugin/vulcanize/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npm/plugin/vulcanize/README: -------------------------------------------------------------------------------- 1 | This directory and the files immediately inside it are automatically generated 2 | when you change this package's NPM dependencies. Commit the files in this 3 | directory (npm-shrinkwrap.json, .gitignore, and this README) to source control 4 | so that others run the same versions of sub-dependencies. 5 | 6 | You should NOT check in the node_modules directory that Meteor automatically 7 | creates; if you are using git, the .gitignore file tells git to ignore it. 8 | -------------------------------------------------------------------------------- /.npm/plugin/vulcanize/npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "vulcanize": { 4 | "version": "1.14.0", 5 | "dependencies": { 6 | "dom5": { 7 | "version": "1.2.0", 8 | "dependencies": { 9 | "parse5": { 10 | "version": "1.5.0" 11 | } 12 | } 13 | }, 14 | "es6-promise": { 15 | "version": "2.3.0" 16 | }, 17 | "hydrolysis": { 18 | "version": "1.20.3", 19 | "dependencies": { 20 | "doctrine": { 21 | "version": "0.7.1", 22 | "dependencies": { 23 | "esutils": { 24 | "version": "1.1.6" 25 | }, 26 | "isarray": { 27 | "version": "0.0.1" 28 | } 29 | } 30 | }, 31 | "espree": { 32 | "version": "2.2.5" 33 | }, 34 | "estraverse": { 35 | "version": "3.1.0" 36 | }, 37 | "path-is-absolute": { 38 | "version": "1.0.0" 39 | } 40 | } 41 | }, 42 | "nopt": { 43 | "version": "3.0.6", 44 | "dependencies": { 45 | "abbrev": { 46 | "version": "1.0.7" 47 | } 48 | } 49 | }, 50 | "path-posix": { 51 | "version": "1.0.0" 52 | }, 53 | "update-notifier": { 54 | "version": "0.5.0", 55 | "dependencies": { 56 | "chalk": { 57 | "version": "1.1.1", 58 | "dependencies": { 59 | "ansi-styles": { 60 | "version": "2.1.0" 61 | }, 62 | "escape-string-regexp": { 63 | "version": "1.0.3" 64 | }, 65 | "has-ansi": { 66 | "version": "2.0.0", 67 | "dependencies": { 68 | "ansi-regex": { 69 | "version": "2.0.0" 70 | } 71 | } 72 | }, 73 | "strip-ansi": { 74 | "version": "3.0.0", 75 | "dependencies": { 76 | "ansi-regex": { 77 | "version": "2.0.0" 78 | } 79 | } 80 | }, 81 | "supports-color": { 82 | "version": "2.0.0" 83 | } 84 | } 85 | }, 86 | "configstore": { 87 | "version": "1.3.0", 88 | "dependencies": { 89 | "graceful-fs": { 90 | "version": "4.1.2" 91 | }, 92 | "mkdirp": { 93 | "version": "0.5.1", 94 | "dependencies": { 95 | "minimist": { 96 | "version": "0.0.8" 97 | } 98 | } 99 | }, 100 | "object-assign": { 101 | "version": "4.0.1" 102 | }, 103 | "os-tmpdir": { 104 | "version": "1.0.1" 105 | }, 106 | "osenv": { 107 | "version": "0.1.3", 108 | "dependencies": { 109 | "os-homedir": { 110 | "version": "1.0.1" 111 | } 112 | } 113 | }, 114 | "uuid": { 115 | "version": "2.0.1" 116 | }, 117 | "write-file-atomic": { 118 | "version": "1.1.3", 119 | "dependencies": { 120 | "slide": { 121 | "version": "1.1.6" 122 | } 123 | } 124 | }, 125 | "xdg-basedir": { 126 | "version": "2.0.0", 127 | "dependencies": { 128 | "os-homedir": { 129 | "version": "1.0.1" 130 | } 131 | } 132 | } 133 | } 134 | }, 135 | "is-npm": { 136 | "version": "1.0.0" 137 | }, 138 | "latest-version": { 139 | "version": "1.0.1", 140 | "dependencies": { 141 | "package-json": { 142 | "version": "1.2.0", 143 | "dependencies": { 144 | "got": { 145 | "version": "3.3.1", 146 | "dependencies": { 147 | "duplexify": { 148 | "version": "3.4.2", 149 | "dependencies": { 150 | "end-of-stream": { 151 | "version": "1.0.0", 152 | "dependencies": { 153 | "once": { 154 | "version": "1.3.2", 155 | "dependencies": { 156 | "wrappy": { 157 | "version": "1.0.1" 158 | } 159 | } 160 | } 161 | } 162 | }, 163 | "readable-stream": { 164 | "version": "2.0.4", 165 | "dependencies": { 166 | "core-util-is": { 167 | "version": "1.0.1" 168 | }, 169 | "inherits": { 170 | "version": "2.0.1" 171 | }, 172 | "isarray": { 173 | "version": "0.0.1" 174 | }, 175 | "process-nextick-args": { 176 | "version": "1.0.3" 177 | }, 178 | "string_decoder": { 179 | "version": "0.10.31" 180 | }, 181 | "util-deprecate": { 182 | "version": "1.0.2" 183 | } 184 | } 185 | } 186 | } 187 | }, 188 | "infinity-agent": { 189 | "version": "2.0.3" 190 | }, 191 | "is-redirect": { 192 | "version": "1.0.0" 193 | }, 194 | "is-stream": { 195 | "version": "1.0.1" 196 | }, 197 | "lowercase-keys": { 198 | "version": "1.0.0" 199 | }, 200 | "nested-error-stacks": { 201 | "version": "1.0.1", 202 | "dependencies": { 203 | "inherits": { 204 | "version": "2.0.1" 205 | } 206 | } 207 | }, 208 | "object-assign": { 209 | "version": "3.0.0" 210 | }, 211 | "prepend-http": { 212 | "version": "1.0.3" 213 | }, 214 | "read-all-stream": { 215 | "version": "3.0.1", 216 | "dependencies": { 217 | "pinkie-promise": { 218 | "version": "1.0.0", 219 | "dependencies": { 220 | "pinkie": { 221 | "version": "1.0.0" 222 | } 223 | } 224 | }, 225 | "readable-stream": { 226 | "version": "2.0.4", 227 | "dependencies": { 228 | "core-util-is": { 229 | "version": "1.0.1" 230 | }, 231 | "inherits": { 232 | "version": "2.0.1" 233 | }, 234 | "isarray": { 235 | "version": "0.0.1" 236 | }, 237 | "process-nextick-args": { 238 | "version": "1.0.3" 239 | }, 240 | "string_decoder": { 241 | "version": "0.10.31" 242 | }, 243 | "util-deprecate": { 244 | "version": "1.0.2" 245 | } 246 | } 247 | } 248 | } 249 | }, 250 | "timed-out": { 251 | "version": "2.0.0" 252 | } 253 | } 254 | }, 255 | "registry-url": { 256 | "version": "3.0.3", 257 | "dependencies": { 258 | "rc": { 259 | "version": "1.1.5", 260 | "dependencies": { 261 | "deep-extend": { 262 | "version": "0.4.0" 263 | }, 264 | "ini": { 265 | "version": "1.3.4" 266 | }, 267 | "minimist": { 268 | "version": "1.2.0" 269 | }, 270 | "strip-json-comments": { 271 | "version": "1.0.4" 272 | } 273 | } 274 | } 275 | } 276 | } 277 | } 278 | } 279 | } 280 | }, 281 | "repeating": { 282 | "version": "1.1.3", 283 | "dependencies": { 284 | "is-finite": { 285 | "version": "1.0.1", 286 | "dependencies": { 287 | "number-is-nan": { 288 | "version": "1.0.0" 289 | } 290 | } 291 | } 292 | } 293 | }, 294 | "semver-diff": { 295 | "version": "2.0.0", 296 | "dependencies": { 297 | "semver": { 298 | "version": "4.3.6" 299 | } 300 | } 301 | }, 302 | "string-length": { 303 | "version": "1.0.1", 304 | "dependencies": { 305 | "strip-ansi": { 306 | "version": "3.0.0", 307 | "dependencies": { 308 | "ansi-regex": { 309 | "version": "2.0.0" 310 | } 311 | } 312 | } 313 | } 314 | } 315 | } 316 | } 317 | } 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /.versions: -------------------------------------------------------------------------------- 1 | differential:vulcanize@2.0.0 2 | meteor@1.1.6 3 | underscore@1.0.3 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Vulcanize 2 | ============================================================================== 3 | Warning: API Change for version 1.0.0. 4 | 5 | This package a meteor build plugin that wraps the [vulcanize](https://www.npmjs.com/package/vulcanize) npm package, which is used to process web components into a single output file. 6 | 7 | ### Usage 8 | 1. Ensure all your components are located somewhere under your public directory. (via bower, zip, etc) 9 | 2. Include a `config.vulcanize` file in the root of your project. This file will optionally define a path to the polyfill and paths to html imports for your components. For Example: 10 | 11 | ```` 12 | { 13 | "polyfill": "/bower_components/webcomponentsjs/webcomponents.min.js", 14 | "useShadowDom": true, // optional, defaults to shady dom (polymer default) 15 | "imports": [ 16 | "/bower_components/paper-button/paper-button.html", 17 | "/bower_components/paper-checkbox/paper-checkbox.html" 18 | ] 19 | } 20 | ```` 21 | - By specifying a path to the polyfill we can ensure that it is injected into the bundle before any imports. 22 | 23 | - By setting `useShadowDom` to true, we configure polymer to opt out of shady dom and use full shadow dom. This is pretty much required at the moment unless you only use polymer elements as leaf nodes. Any light dom (child elements) that gets rendered by blaze, react, etc will not be accounted for otherwise. 24 | 25 | - Running your app in development as usual will result in individual imports being added to your `` tag, resulting in multiple subsequent HTTP requests (good in development - debugging). 26 | 27 | - Running `meteor`, `meteor build`, `modulus deploy`, etc with the `VULCANIZE=true` environment variable set will result in all your html imports being vulcanized or concatenated into a single html import (good in production). The resulting file will be called `vulcanized-{md5}.html`, which will be automatically added to your `` tag. For example, `VULCANIZE=true meteor`, `VULCANIZE=true modulus deploy`. 28 | 29 | - Setting the `CDN_PREFIX` environment variable will prepend the string to the beginning of the file path that is inserted into your HTML's `` tag. 30 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: 'differential:vulcanize', 3 | summary: 'Vulcanize', 4 | version: '3.0.0', 5 | git: 'https://github.com/Differential/meteor-vulcanize' 6 | }); 7 | 8 | Package.registerBuildPlugin({ 9 | name: 'vulcanize', 10 | use: [ 11 | "underscore@1.0.4" 12 | ], 13 | sources: [ 14 | 'vulcanize.js' 15 | ], 16 | npmDependencies: { 'vulcanize': '1.14.0' } 17 | }); 18 | 19 | Package.onUse(function (api) { 20 | api.use('underscore'); 21 | api.use('isobuild:compiler-plugin@1.0.0'); 22 | }); 23 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | [ 4 | "meteor", 5 | "1.1.3" 6 | ], 7 | [ 8 | "underscore", 9 | "1.0.1" 10 | ] 11 | ], 12 | "pluginDependencies": [ 13 | [ 14 | "vulcanize", 15 | {} 16 | ] 17 | ], 18 | "toolVersion": "meteor-tool@1.0.35", 19 | "format": "1.0" 20 | } -------------------------------------------------------------------------------- /vulcanize-tests.js: -------------------------------------------------------------------------------- 1 | // Write your tests here! 2 | // Here is an example. 3 | Tinytest.add('example', function (test) { 4 | test.equal(true, true); 5 | }); 6 | -------------------------------------------------------------------------------- /vulcanize.js: -------------------------------------------------------------------------------- 1 | var vulcan = Npm.require('vulcanize'); 2 | var crypto = Npm.require('crypto'); 3 | var Future = Npm.require('fibers/future'); 4 | var url = Npm.require('url'); 5 | var fs = Npm.require('fs'); 6 | 7 | /** 8 | * Vulcanize now requires a target input file. 9 | * We need to create a temp file located within the same 10 | * 'abspath' as the actual imports. Otherwise, vulcanize 11 | * gets confused during the process. 12 | */ 13 | var tmpFile = '_imports.html'; 14 | var tmpDir = 'public'; 15 | var tmpPath = tmpDir + '/' + tmpFile; 16 | 17 | /** 18 | * Log 19 | */ 20 | function log() { 21 | args = _.values(arguments); 22 | args.unshift("=> Vulcanize:"); 23 | console.log.apply(this, args); 24 | } 25 | 26 | /** 27 | * Get script tag with specified path. 28 | */ 29 | function scriptTag(path) { 30 | return ''; 31 | } 32 | 33 | /** 34 | * Get link tag with specified path. 35 | */ 36 | function linkTag(path) { 37 | return ''; 38 | } 39 | 40 | /** 41 | * Add config for dom mode to head. 42 | */ 43 | function addShadowDomConfig(file) { 44 | file.addHtml({ 45 | section: 'head', 46 | data: '' 47 | }); 48 | } 49 | 50 | /** 51 | * Add webcomponentsjs script to head. 52 | * @todo read first line and check for script tag 53 | */ 54 | function addPolyfillTag(file, path) { 55 | file.addHtml({ 56 | section: 'head', 57 | data: scriptTag(path) 58 | }); 59 | } 60 | 61 | /** 62 | * Add imports to head. 63 | */ 64 | 65 | function addImportTag(file, path) { 66 | file.addHtml({ 67 | section: 'head', 68 | data: linkTag(path) 69 | }); 70 | } 71 | 72 | /** 73 | * Vulcanize all files and add output file to head. 74 | */ 75 | function vulcanizeImports(file, imports) { 76 | var future = new Future(); 77 | 78 | var tags = _.map(imports, function(path) { 79 | return linkTag(path); 80 | }); 81 | 82 | fs.writeFileSync(tmpPath, tags.join("\n")); 83 | vulcan.setOptions({ abspath: tmpDir }); 84 | 85 | vulcan.process(tmpFile, function(err, html) { 86 | fs.unlinkSync(tmpPath); 87 | var filenameHash = crypto.createHash('md5').update(html).digest('hex'); 88 | var filePath = '/vulcanized-' + filenameHash + '.html'; 89 | 90 | file.addAsset({ 91 | path: filePath, 92 | data: html 93 | }); 94 | 95 | if (_.isString(process.env.CDN_PREFIX)) { 96 | filePath = url.resolve(process.env.CDN_PREFIX, filePath); 97 | } 98 | 99 | addImportTag(file, filePath); 100 | future.return(true); 101 | }); 102 | 103 | return future.wait(); 104 | } 105 | 106 | /** 107 | * Add individual import tags 108 | */ 109 | function individualImports(file, imports) { 110 | _.each(imports, function(path) { 111 | addImportTag(file, path); 112 | }); 113 | } 114 | 115 | function VulcanizeCompiler() {} 116 | VulcanizeCompiler.prototype.processFilesForTarget = function (files) { 117 | files.forEach(function (file) { 118 | // Get JSON file. 119 | var json = JSON.parse(file.getContentsAsString()); 120 | 121 | // Add polyfill to html if defined. 122 | if (_.isString(json.polyfill)) { 123 | addPolyfillTag(file, json.polyfill); 124 | } 125 | 126 | // Optionally opt into shadow dom, rather than shady dom. 127 | if (json.useShadowDom) { 128 | addShadowDomConfig(file); 129 | } 130 | 131 | // Add imports if defined. 132 | if (process.env.VULCANIZE && _.isArray(json.imports)) { 133 | log("Importing vulcanized file..."); 134 | vulcanizeImports(file, json.imports); 135 | } else { 136 | log("Importing individual files..."); 137 | individualImports(file, json.imports); 138 | } 139 | }); 140 | }; 141 | 142 | Plugin.registerCompiler({ 143 | extensions: ["vulcanize"], 144 | filenames: ["config"], 145 | archMatching: 'web', 146 | }, function(){ 147 | return new VulcanizeCompiler(); 148 | }); 149 | --------------------------------------------------------------------------------