├── .editorconfig ├── .eslintrc ├── .gitignore ├── .jscsrc ├── .travis.yml ├── README.md ├── index.js └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | ; editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.json] 13 | indent_size = 2 14 | 15 | [*.yml] 16 | indent_size = 2 17 | 18 | [*.md] 19 | indent_size = 2 20 | 21 | [{.eslintrc,.jscsrc}] 22 | indent_size = 2 23 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | # https://github.com/eslint/eslint/blob/master/docs/configuring/README.md 3 | 4 | env: 5 | node: true 6 | browser: true 7 | 8 | # https://github.com/eslint/eslint/tree/master/docs/rules 9 | rules: 10 | # possible errors 11 | no-cond-assign: 1 12 | no-console: 1 13 | no-constant-condition: 2 14 | comma-dangle: 1 15 | no-control-regex: 1 16 | no-debugger: 2 17 | no-dupe-keys: 2 18 | no-empty: 2 19 | no-empty-character-class: 2 20 | no-ex-assign: 2 21 | no-extra-boolean-cast: 1 22 | no-extra-parens: 1 23 | no-extra-semi: 2 24 | no-func-assign: 2 25 | no-inner-declarations: 1 26 | no-invalid-regexp: 2 27 | no-negated-in-lhs: 1 28 | no-obj-calls: 2 29 | no-regex-spaces: 1 30 | no-sparse-arrays: 2 31 | no-unreachable: 2 32 | use-isnan: 2 33 | valid-jsdoc: 1 34 | valid-typeof: 2 35 | 36 | # best practices 37 | block-scoped-var: 1 38 | complexity: 0 39 | consistent-return: 1 40 | curly: 2 41 | default-case: 1 42 | dot-notation: 0 43 | eqeqeq: 2 44 | guard-for-in: 1 45 | no-alert: 2 46 | no-caller: 2 47 | no-div-regex: 0 48 | no-else-return: 1 49 | no-eq-null: 2 50 | no-eval: 2 51 | no-extend-native: 2 52 | no-fallthrough: 1 53 | no-floating-decimal: 1 54 | no-implied-eval: 2 55 | no-labels: 2 56 | no-iterator: 2 57 | no-lone-blocks: 2 58 | no-loop-func: 1 59 | no-multi-str: 1 60 | no-native-reassign: 2 61 | no-new: 0 62 | no-new-func: 2 63 | no-new-wrappers: 1 64 | no-octal: 2 65 | no-octal-escape: 2 66 | no-proto: 2 67 | no-redeclare: 2 68 | no-return-assign: 2 69 | no-script-url: 2 70 | no-self-compare: 2 71 | no-sequences: 1 72 | no-unused-expressions: 2 73 | # Definition for rule 'no-void' was not found 74 | # no-void: 2 75 | no-warning-comments: 0 76 | no-with: 2 77 | radix: 1 78 | wrap-iife: 1 79 | yoda: 0 80 | 81 | # strict mode 82 | # Definition for rule 'global-strict' was not found 83 | # global-strict: [2, "always"] 84 | strict: 2 85 | 86 | # variables 87 | no-catch-shadow: 2 88 | no-delete-var: 2 89 | no-label-var: 2 90 | no-shadow: 1 91 | no-shadow-restricted-names: 2 92 | no-undef: 2 93 | no-undefined: 2 94 | no-undef-init: 1 95 | no-unused-vars: 1 96 | no-use-before-define: 2 97 | 98 | # Node.js 99 | handle-callback-err: 2 100 | no-mixed-requires: 0 101 | no-path-concat: 2 102 | no-process-exit: 2 103 | no-restricted-modules: 0 104 | no-sync: 0 105 | 106 | # stylistic issues, disabled because of JSCS 107 | brace-style: 0 108 | camelcase: 0 109 | consistent-this: 0 110 | eol-last: 0 111 | func-names: 0 112 | func-style: 0 113 | new-cap: 1 114 | new-parens: 1 115 | no-nested-ternary: 0 116 | no-array-constructor: 1 117 | no-lonely-if: 1 118 | no-new-object: 1 119 | no-spaced-func: 0 120 | no-space-before-semi: 0 121 | no-ternary: 0 122 | no-trailing-spaces: 0 123 | no-underscore-dangle: 0 124 | no-wrap-func: 0 125 | no-mixed-spaces-and-tabs: 0 126 | quotes: 0 127 | quote-props: 0 128 | semi: 2 129 | sort-vars: 0 130 | space-after-keywords: 0 131 | space-in-brackets: 0 132 | space-in-parens: 0 133 | space-infix-ops: 0 134 | keyword-spacing: 1 135 | space-unary-ops: 1 136 | max-nested-callbacks: 0 137 | one-var: 0 138 | wrap-regex: 0 139 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | tmp/ 3 | *.sublime-* 4 | *.log 5 | .DS_Store 6 | .idea/ 7 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "requireCurlyBraces": [ 3 | "if", 4 | "else", 5 | "for", 6 | "while", 7 | "do", 8 | "try", 9 | "catch", 10 | "case", 11 | "default" 12 | ], 13 | "requireSpaceAfterKeywords": [ 14 | "if", 15 | "else", 16 | "for", 17 | "while", 18 | "do", 19 | "switch", 20 | "return", 21 | "try", 22 | "catch" 23 | ], 24 | "requireSpaceBeforeBlockStatements": true, 25 | "requireParenthesesAroundIIFE": true, 26 | "requireSpacesInConditionalExpression": { 27 | "afterTest": true, 28 | "beforeConsequent": true, 29 | "afterConsequent": true, 30 | "beforeAlternate": true 31 | }, 32 | "requireSpacesInAnonymousFunctionExpression": { 33 | "beforeOpeningCurlyBrace": true 34 | }, 35 | "disallowSpacesInAnonymousFunctionExpression": { 36 | "beforeOpeningRoundBrace": true 37 | }, 38 | "requireSpacesInNamedFunctionExpression": { 39 | "beforeOpeningCurlyBrace": true 40 | }, 41 | "disallowSpacesInNamedFunctionExpression": { 42 | "beforeOpeningRoundBrace": true 43 | }, 44 | "requireBlocksOnNewline": true, 45 | "requireSpacesInsideObjectBrackets": "all", 46 | "requireSpacesInsideArrayBrackets": "all", 47 | "disallowSpaceAfterObjectKeys": true, 48 | "requireCommaBeforeLineBreak": true, 49 | "requireOperatorBeforeLineBreak": [ 50 | "?", 51 | "=", 52 | "+", 53 | "-", 54 | "/", 55 | "*", 56 | "==", 57 | "===", 58 | "!=", 59 | "!==", 60 | ">", 61 | ">=", 62 | "<", 63 | "<=" 64 | ], 65 | "disallowSpaceAfterPrefixUnaryOperators": [ "++", "--", "+", "-", "~", "!" ], 66 | "disallowSpaceBeforePostfixUnaryOperators": [ "++", "--" ], 67 | "requireSpaceBeforeBinaryOperators": [ 68 | "=", 69 | "+", 70 | "-", 71 | "/", 72 | "*", 73 | "==", 74 | "===", 75 | "!=", 76 | "!==" 77 | ], 78 | "requireSpaceAfterBinaryOperators": [ 79 | "=", 80 | ",", 81 | "+", 82 | "-", 83 | "/", 84 | "*", 85 | "==", 86 | "===", 87 | "!=", 88 | "!==" 89 | ], 90 | "requireCamelCaseOrUpperCaseIdentifiers": true, 91 | "disallowMultipleLineBreaks": true, 92 | "validateLineBreaks": "LF", 93 | "validateQuoteMarks": "'", 94 | "validateIndentation": 4, 95 | "disallowMixedSpacesAndTabs": true, 96 | "disallowTrailingWhitespace": true, 97 | "disallowTrailingComma": true, 98 | "disallowKeywordsOnNewLine": ["else"], 99 | "requireLineFeedAtFileEnd": true, 100 | "requireCapitalizedConstructors": true, 101 | "safeContextKeyword": ["that"], 102 | "disallowYodaConditions": true, 103 | "requireSpaceAfterLineComment": true, 104 | "requireAnonymousFunctions": true, 105 | "disallowNewlineBeforeBlockStatements": true, 106 | "disallowMultipleVarDecl": true 107 | } 108 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 4.4 5 | - 4.5 6 | - 4.6 7 | - 4 8 | - 6.8 9 | - 6.7 10 | - 6.6 11 | - 6.5 12 | - 6 13 | - 5 14 | 15 | branches: 16 | only: 17 | - master 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## [Autopolyfiller](https://github.com/azproduction/autopolyfiller) loader for [webpack](https://webpack.github.io/) 2 | 3 | [![npm](http://img.shields.io/npm/v/autopolyfiller-loader.svg?style=flat-square)](https://www.npmjs.org/package/autopolyfiller-loader) 4 | [![travis](http://img.shields.io/travis/deepsweet/autopolyfiller-loader.svg?style=flat-square)](https://travis-ci.org/deepsweet/autopolyfiller-loader) 5 | [![climate](http://img.shields.io/codeclimate/github/deepsweet/autopolyfiller-loader.svg?style=flat-square)](https://codeclimate.com/github/deepsweet/autopolyfiller-loader/code) 6 | [![deps](http://img.shields.io/david/deepsweet/autopolyfiller-loader.svg?style=flat-square)](https://david-dm.org/deepsweet/autopolyfiller-loader) 7 | [![gratipay](http://img.shields.io/gratipay/deepsweet.svg?style=flat-square)](https://gratipay.com/deepsweet/) 8 | 9 | > This is like [Autoprefixer](https://github.com/ai/autoprefixer), but for JavaScript polyfills. It scans your code and applies only required polyfills. 10 | 11 | ### Install 12 | 13 | ```sh 14 | $ npm i -S autopolyfiller-loader 15 | ``` 16 | 17 | ### Usage 18 | 19 | ```js 20 | module: { 21 | rules: [ { 22 | enforce: 'post', 23 | test: /\.js$/, 24 | exclude: /\/(node_modules|bower_components)\//, 25 | loader: 'autopolyfiller-loader', 26 | query: { 27 | browsers: [ 'last 2 versions', 'ie >= 9' ], //list of browsers to polyfill 28 | withParser: ['acorn@0.11.0', {ecmaVersion: 6}], //allow use custom parser 29 | parserOptions: {ecmaVersion: 6}, // only if no #withParser specified, 30 | // allow to use custom options with acorn v4 parser 31 | exclude: ['Promise'], //exclude some polyfills 32 | include: ['Object.create'], //force include some polifills 33 | use: [{ 34 | // AST tree pattern matching 35 | // It may "grep" multiply polyfills 36 | test: function (ast) { 37 | return query('Object.newFeature(_$)', ast).length > 0 ? ['Object.newFeature'] : []; 38 | }, 39 | 40 | // Your polyfills code 41 | polyfill: { 42 | 'Object.newFeature': 'Object.newFeature = function () {};' 43 | }, 44 | 45 | // This list means "apply this feature to the " 46 | // For more examples see https://github.com/jonathantneal/polyfill/blob/master/agent.js.json 47 | support: { 48 | // For chrome 29 only apply Object.newFeature polyfill 49 | 'Chrome': [{ 50 | only: '29', 51 | fill: 'Object.newFeature' 52 | }] 53 | }, 54 | 55 | // This is optional. By default autopolyfiller will use 56 | // polyfill's name to generate condition's code: 57 | wrapper: { 58 | 'Object.newFeature': { 59 | 'before': 'if (!("newFeature" in Object)) {', 60 | 'after': '}' 61 | } 62 | } 63 | }] //add custom polyfills 64 | } 65 | } ] 66 | } 67 | ``` 68 | 69 | [Documentation: Using loaders](https://webpack.github.io/docs/using-loaders.html). 70 | 71 | ### License 72 | [WTFPL](http://www.wtfpl.net/wp-content/uploads/2012/12/wtfpl-strip.jpg) 73 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var loaderUtils = require('loader-utils'); 4 | var autopolyfiller = require('autopolyfiller'); 5 | var SourceMap = require('source-map'); 6 | var fs = require('fs'); 7 | var loadedDetects = {}; 8 | 9 | var formatMessage = function(name, message) { 10 | return (name ? name + ': ' : '') + message + '\n'; 11 | }; 12 | 13 | var AutopolyfillerLoaderError = function(name, message, error) { 14 | Error.call(this); 15 | Error.captureStackTrace(this, AutopolyfillerLoaderError); 16 | 17 | this.name = 'AutopolyfillerLoaderError'; 18 | this.message = formatMessage(name, message); 19 | this.error = error; 20 | }; 21 | 22 | AutopolyfillerLoaderError.prototype = Object.create(Error.prototype); 23 | AutopolyfillerLoaderError.prototype.constructor = AutopolyfillerLoaderError; 24 | 25 | var getPolyfillPath = function(polyfill) { 26 | polyfill = polyfill 27 | .replace(/^Window\.prototype\./, '') 28 | .replace(/^base64$/, 'atob'); // fix #15 29 | if (!/^document\..+/.test(polyfill)) { 30 | polyfill = polyfill.replace(/\./g, '/'); 31 | } 32 | 33 | return require.resolve('polyfill-service/polyfills/' + polyfill + '/polyfill'); 34 | }; 35 | 36 | var getPolyfillDetectPath = function(polyfill) { 37 | polyfill = polyfill 38 | .replace(/^Window\.prototype\./, '') 39 | .replace(/^base64$/, 'atob'); // fix #15 40 | if (!/^document\..+/.test(polyfill)) { 41 | polyfill = polyfill.replace(/\./g, '/'); 42 | } 43 | 44 | return require.resolve('polyfill-service/polyfills/' + polyfill + '/detect'); 45 | }; 46 | 47 | var getPolyfillDetect = function(polyfill) { 48 | if (!loadedDetects[polyfill]) { 49 | var path = getPolyfillDetectPath(polyfill); 50 | loadedDetects[polyfill] = fs.readFileSync(path, { encoding: 'utf8' }); 51 | } 52 | return loadedDetects[polyfill]; 53 | }; 54 | 55 | module.exports = function(source, sourceMap) { 56 | var polyfills; 57 | 58 | try { 59 | var query = loaderUtils.parseQuery(this.query); 60 | // array of browsers 61 | var browsers = query.browsers || []; 62 | // array of excluded polyfills 63 | var exclude = query.exclude || []; 64 | // array of included polyfills 65 | var include = query.include || []; 66 | // use custom parser 67 | var customParse = query.withParser || [ 68 | require('acorn'), 69 | query.parserOptions || {} 70 | ]; 71 | // use custom polyfills 72 | var uses = query.use || []; 73 | 74 | // array of polyfills names 75 | var prePolyfills = autopolyfiller(browsers); 76 | 77 | if (customParse.length) { 78 | prePolyfills = prePolyfills.withParser.apply(prePolyfills, customParse); 79 | } 80 | uses.forEach(function(use) { 81 | prePolyfills.use(use); 82 | }); 83 | 84 | polyfills = prePolyfills.exclude(exclude).include(include).add(source).polyfills; 85 | 86 | if (this.cacheable) { 87 | this.cacheable(); 88 | } 89 | 90 | } 91 | catch (err) { 92 | throw new AutopolyfillerLoaderError(err.name, 'Can\'t get polyfills list (' + err.message + ')', err); 93 | } 94 | if (polyfills.length) { 95 | var inject = '\n/* injects from autopolyfiller-loader */\n'; 96 | 97 | try { 98 | // append require()s with absoluted paths to neccessary polyfills 99 | polyfills.forEach(function(polyfill) { 100 | var path = getPolyfillPath(polyfill); 101 | var test = getPolyfillDetect(polyfill); 102 | inject += 'if (!(' + test + ')) require(' + JSON.stringify(path) + ');'; 103 | inject += '\n'; 104 | }); 105 | 106 | inject += '\n'; 107 | } 108 | catch (err) { 109 | throw new AutopolyfillerLoaderError(err.name, 'Can\'t get polyfill\'s source (' + err.message + ')', err); 110 | } 111 | 112 | // support existing SourceMap 113 | // https://github.com/mozilla/source-map#sourcenode 114 | // https://github.com/webpack/imports-loader/blob/master/index.js#L34-L44 115 | // https://webpack.github.io/docs/loaders.html#writing-a-loader 116 | if (sourceMap) { 117 | var currentRequest = loaderUtils.getCurrentRequest(this); 118 | var SourceNode = SourceMap.SourceNode; 119 | var SourceMapConsumer = SourceMap.SourceMapConsumer; 120 | var sourceMapConsumer = new SourceMapConsumer(sourceMap); 121 | var node = SourceNode.fromStringWithSourceMap(source, sourceMapConsumer); 122 | 123 | node.prepend(inject); 124 | 125 | var result = node.toStringWithSourceMap({ 126 | file: currentRequest 127 | }); 128 | 129 | return void this.callback(null, result.code, result.map.toJSON()); 130 | } 131 | 132 | // prepend collected inject at the top of file 133 | return inject + source; 134 | } 135 | 136 | // return the original source and sourceMap 137 | if (sourceMap) { 138 | return void this.callback(null, source, sourceMap); 139 | } 140 | 141 | // return the original source 142 | return source; 143 | }; 144 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autopolyfiller-loader", 3 | "version": "1.1.0", 4 | "description": "Autopolyfiller loader for webpack", 5 | "keywords": [ 6 | "webpack", 7 | "loader", 8 | "autopolyfiller" 9 | ], 10 | "homepage": "https://github.com/deepsweet/autopolyfiller-loader", 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/deepsweet/autopolyfiller-loader.git" 14 | }, 15 | "author": { 16 | "name": "Kir Belevich", 17 | "email": "kir@soulshine.in", 18 | "url": "https://github.com/deepsweet" 19 | }, 20 | "main": "index.js", 21 | "files": [ 22 | "index.js" 23 | ], 24 | "peerDependencies": { 25 | "acorn": "4.x.x", 26 | "webpack": "^1.4.3 || ^2.2.x" 27 | }, 28 | "dependencies": { 29 | "autopolyfiller": "^1.6.0", 30 | "loader-utils": "^0.2.16", 31 | "polyfill-service": "^3.12.1", 32 | "source-map": "^0.5.6" 33 | }, 34 | "devDependencies": { 35 | "eslint": "^3.6.1", 36 | "jscs": "^3.0.7" 37 | }, 38 | "scripts": { 39 | "test": "eslint index.js && jscs index.js" 40 | }, 41 | "engines": { 42 | "node": ">=0.10.0" 43 | }, 44 | "license": "WTFPL" 45 | } 46 | --------------------------------------------------------------------------------