├── test ├── file.txt ├── fixtures │ ├── empty-import.js │ ├── empty-require.js │ ├── import-txt.js │ ├── require-txt.js │ └── import-export-txt.js ├── .eslintrc └── index.spec.js ├── circle.yml ├── .babelrc ├── .npmignore ├── .eslintignore ├── .gitignore ├── .eslintrc ├── LICENSE.md ├── src └── index.js ├── package.json └── README.md /test/file.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 9.1.0 4 | -------------------------------------------------------------------------------- /test/fixtures/empty-import.js: -------------------------------------------------------------------------------- 1 | import '../file.txt' 2 | -------------------------------------------------------------------------------- /test/fixtures/empty-require.js: -------------------------------------------------------------------------------- 1 | require('../file.txt') 2 | -------------------------------------------------------------------------------- /test/fixtures/import-txt.js: -------------------------------------------------------------------------------- 1 | import file from '../file.txt'; 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/es2015", "@babel/stage-2"] 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/require-txt.js: -------------------------------------------------------------------------------- 1 | const file = require('../file.txt'); 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .babelrc 3 | .eslintrc 4 | test 5 | circle.yml 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # node_modules ignored by default 2 | 3 | lib/ 4 | test/fixtures 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules/ 4 | lib/ 5 | yarn-error.log 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "env": { 4 | "mocha": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/import-export-txt.js: -------------------------------------------------------------------------------- 1 | import file from '../file.txt'; 2 | export { file }; 3 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "import/no-extraneous-dependencies": [ 4 | "error", { 5 | "devDependencies": true, 6 | "peerDependencies": true 7 | } 8 | ], 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Jake Murzy 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 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { dirname, isAbsolute, resolve } from 'path'; 2 | 3 | const defaultOptions = { 4 | name: '[name].[ext]?[sha512:hash:base64:7]', 5 | }; 6 | 7 | export default function transformAssets({ types: t }) { 8 | function resolveModulePath(filename) { 9 | const dir = dirname(filename); 10 | 11 | if (isAbsolute(dir)) { 12 | return dir; 13 | } 14 | 15 | if (process.env.PWD) { 16 | return resolve(process.env.PWD, dir); 17 | } 18 | 19 | return resolve(dir); 20 | } 21 | 22 | return { 23 | visitor: { 24 | CallExpression(path, { file, opts }) { 25 | const currentConfig = { 26 | ...defaultOptions, 27 | ...opts, 28 | }; 29 | 30 | if (typeof currentConfig.name !== 'string') { 31 | return; 32 | } 33 | 34 | currentConfig.extensions = currentConfig.extensions || []; 35 | 36 | /* eslint-disable global-require */ 37 | require('asset-require-hook')(currentConfig); 38 | /* eslint-enable */ 39 | 40 | const { 41 | callee: { 42 | name: calleeName, 43 | }, 44 | arguments: args, 45 | } = path.node; 46 | 47 | if (calleeName !== 'require' || !args.length || !t.isStringLiteral(args[0])) { 48 | return; 49 | } 50 | 51 | if (currentConfig.extensions.find(ext => args[0].value.endsWith(ext))) { 52 | const [{ value: filePath }] = args; 53 | 54 | if (!t.isExpressionStatement(path.parent)) { 55 | const from = resolveModulePath(file.opts.filename); 56 | /* eslint-disable global-require, import/no-dynamic-require, new-cap */ 57 | const p = require(resolve(from, filePath)); 58 | 59 | path.replaceWith(t.StringLiteral(p)); 60 | /* eslint-enable */ 61 | } else { 62 | path.remove(); 63 | } 64 | } 65 | }, 66 | }, 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-transform-assets", 3 | "version": "1.0.2", 4 | "description": "Transforms importing of asset files at compile time using Babel", 5 | "main": "lib/index.js", 6 | "author": "Jake Murzy ", 7 | "license": "MIT", 8 | "homepage": "https://github.com/jmurzy/babel-plugin-transform-assets", 9 | "keywords": [ 10 | "babel", 11 | "webpack", 12 | "file-loader", 13 | "babel-plugin", 14 | "asset", 15 | "require", 16 | "import", 17 | "hook", 18 | "plugin", 19 | "universal", 20 | "isomorphic", 21 | "asset-require-hook" 22 | ], 23 | "contributors": [ 24 | { 25 | "name": "Jake Murzy", 26 | "email": "jake@murzy" 27 | } 28 | ], 29 | "engines": { 30 | "node": ">=6.0.0" 31 | }, 32 | "scripts": { 33 | "clean": "rimraf lib", 34 | "build": "npm run clean && node node_modules/.bin/babel src --out-dir lib", 35 | "lint": "node node_modules/.bin/eslint src test", 36 | "test": "npm run lint && node node_modules/.bin/mocha --compilers js:@babel/register 'test/**/*.spec.js'" 37 | }, 38 | "repository": { 39 | "type": "git", 40 | "url": "https://github.com/jmurzy/babel-plugin-transform-assets.git" 41 | }, 42 | "bugs": { 43 | "url": "https://github.com/jmurzy/babel-plugin-transform-assets/issues" 44 | }, 45 | "dependencies": { 46 | "asset-require-hook": "^1.0.2" 47 | }, 48 | "devDependencies": { 49 | "@babel/cli": "7.0.0-beta.42", 50 | "@babel/core": "7.0.0-beta.42", 51 | "@babel/preset-es2015": "7.0.0-beta.42", 52 | "@babel/preset-stage-2": "7.0.0-beta.42", 53 | "@babel/register": "^7.0.0-beta.42", 54 | "babel-eslint": "8.2.2", 55 | "chai": "^3.4.1", 56 | "eslint": "^3.8.1", 57 | "eslint-config-airbnb": "^12.0.0", 58 | "eslint-plugin-import": "^1.16.0", 59 | "eslint-plugin-jsx-a11y": "^2.2.3", 60 | "eslint-plugin-react": "^6.4.1", 61 | "gulp-babel": "^8.0.0-beta.2", 62 | "gulp-util": "^3.0.7", 63 | "mocha": "^3.1.2", 64 | "rimraf": "^2.5.4" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-transform-assets 2 | 3 | Transforms importing of asset files at compile time using Babel. This plugin removes the need to run your server code through [Webpack](https://github.com/webpack/webpack) module bundler when using loaders such as file-loader, url-loader and building isomorphic universal apps. Aids in creating a cleaner, maintainable build process at the cost of yet another [Babel](https://github.com/babel/babel) [plugin](https://babeljs.io/docs/plugins/). 4 | 5 | [![CircleCI](https://img.shields.io/circleci/project/jmurzy/babel-plugin-transform-assets.svg)](https://circleci.com/gh/jmurzy/babel-plugin-transform-assets) 6 | [![npm version](https://img.shields.io/npm/v/babel-plugin-transform-assets.svg?style=flat-square)](https://www.npmjs.com/package/babel-plugin-transform-assets) 7 | [![npm](https://img.shields.io/npm/l/babel-plugin-transform-assets.svg)](https://github.com/jmurzy/babel-plugin-transform-assets/blob/master/LICENSE.md) 8 | 9 | ## Example 10 | 11 | ```js 12 | import file from '../file.txt'; 13 | ``` 14 | 15 | will be transformed to 16 | 17 | ```js 18 | var file = 'file.txt?9LDjftP'; 19 | ``` 20 | 21 | See the spec for [more examples](https://github.com/jmurzy/babel-plugin-transform-assets/blob/master/test/index.spec.js). 22 | 23 | ## Requirements 24 | [Babel](https://github.com/babel/babel) v6 or higher. 25 | 26 | ## Installation 27 | 28 | ```sh 29 | npm install -D babel-plugin-transform-assets 30 | ``` 31 | 32 | ## Usage 33 | 34 | ### Via `.babelrc` 35 | 36 | **.babelrc** 37 | 38 | ```json 39 | { 40 | "plugins": [ 41 | ["transform-assets", { 42 | "extensions": ["svg"], 43 | "name": "[name].[ext]?[sha512:hash:base64:7]" 44 | }] 45 | ] 46 | } 47 | ``` 48 | 49 | ### Via Node API 50 | 51 | ```javascript 52 | require('babel-core').transform('code', { 53 | plugins: [ 54 | ['transform-assets', { 55 | extensions: ['svg'], 56 | name: '[name].[ext]?[sha512:hash:base64:7]', 57 | }] 58 | ] 59 | }); 60 | ``` 61 | 62 | ### Contributing 63 | 64 | Contributions are very welcome—bug fixes, features, documentation, tests. Just make sure the tests are passing. 65 | 66 | ### Related Projects 67 | 68 | [babel-plugin-css-modules-transform](https://github.com/michalkvasnicak/babel-plugin-css-modules-transform) 69 | -------------------------------------------------------------------------------- /test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { transformFileSync } from '@babel/core'; 2 | import { expect } from 'chai'; 3 | import path from 'path'; 4 | import fs from 'fs'; 5 | import gulpUtil from 'gulp-util'; 6 | import gulpBabel from 'gulp-babel'; 7 | 8 | describe('transforms assets', () => { 9 | const transform = (filename, config = {}) => 10 | transformFileSync(path.resolve(__dirname, filename), { 11 | babelrc: false, 12 | presets: ['@babel/es2015'], 13 | plugins: [ 14 | ['./src/index.js', config], 15 | ], 16 | }); 17 | 18 | it('replaces require statements with filename', () => { 19 | expect(transform('fixtures/require-txt.js', { 20 | extensions: ['txt'], 21 | }).code).to.be.equal(`"use strict"; 22 | 23 | var file = "file.txt?9LDjftP";`); 24 | }); 25 | 26 | it('replaces import statements with filename', () => { 27 | expect(transform('fixtures/import-txt.js', { 28 | extensions: ['txt'], 29 | }).code).to.be.equal(`"use strict"; 30 | 31 | var _file = _interopRequireDefault("file.txt?9LDjftP"); 32 | 33 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }`); 34 | }); 35 | 36 | it('replaces import statements with filename and then exports', () => { 37 | expect(transform('fixtures/import-export-txt.js', { 38 | extensions: ['txt'], 39 | }).code).to.be.equal(`"use strict"; 40 | 41 | Object.defineProperty(exports, "__esModule", { 42 | value: true 43 | }); 44 | Object.defineProperty(exports, "file", { 45 | enumerable: true, 46 | get: function get() { 47 | return _file.default; 48 | } 49 | }); 50 | 51 | var _file = _interopRequireDefault("file.txt?9LDjftP"); 52 | 53 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }`); 54 | }); 55 | 56 | 57 | it('replaces import statement with filename via gulp', (cb) => { 58 | const stream = gulpBabel({ 59 | babelrc: false, 60 | presets: ['@babel/es2015'], 61 | plugins: [ 62 | ['./src/index.js', { extensions: ['txt'] }], 63 | ], 64 | }); 65 | 66 | stream.on('data', (file) => { 67 | expect(file.contents.toString()).to.be.equal(`"use strict"; 68 | 69 | var _file = _interopRequireDefault("file.txt?9LDjftP"); 70 | 71 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }`); 72 | }); 73 | 74 | stream.on('end', cb); 75 | 76 | stream.write(new gulpUtil.File({ 77 | cwd: __dirname, 78 | base: path.join(__dirname, 'fixtures'), 79 | path: path.join(__dirname, 'fixtures/import-txt.js'), 80 | contents: fs.readFileSync(path.join(__dirname, 'fixtures/import-txt.js')), 81 | })); 82 | 83 | stream.end(); 84 | }); 85 | 86 | it('removes global requires', () => { 87 | expect(transform('fixtures/empty-require.js', { 88 | extensions: ['txt'], 89 | }).code).to.be.equal('"use strict";'); 90 | }); 91 | 92 | it('fixtures/empty-import.js', () => { 93 | expect(transform('fixtures/empty-import.js', { 94 | extensions: ['txt'], 95 | }).code).to.be.equal('"use strict";'); 96 | }); 97 | }); 98 | --------------------------------------------------------------------------------