├── .gitignore ├── .npmignore ├── test ├── bundle │ ├── a.js │ ├── c.js │ ├── error.js │ ├── b.js │ ├── index.js │ └── react-flow.js ├── pkg-app │ ├── index.js │ └── node_modules │ │ └── app │ │ ├── package.json │ │ └── index.js ├── error.js ├── options.js ├── browserify-package.js ├── .swcrc ├── event.js ├── babel-source-maps.js ├── aaa.js ├── source-maps-absolute.js ├── source-maps-relative.js ├── browserify-cli.js └── browser-pack-source-maps.js ├── .travis.yml ├── package.json ├── LICENSE ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | test 3 | -------------------------------------------------------------------------------- /test/bundle/a.js: -------------------------------------------------------------------------------- 1 | export default 'a is for apple'; 2 | -------------------------------------------------------------------------------- /test/pkg-app/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('app'); 2 | -------------------------------------------------------------------------------- /test/bundle/c.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | return 'c'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/bundle/error.js: -------------------------------------------------------------------------------- 1 | class A { 2 | constructor () { 3 | super(); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/bundle/b.js: -------------------------------------------------------------------------------- 1 | export default () => ({ 2 | catch: `catch`, 3 | delete: `delete` 4 | }); 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "10" 5 | - "8" 6 | - "6" 7 | -------------------------------------------------------------------------------- /test/bundle/index.js: -------------------------------------------------------------------------------- 1 | export {default as a} from './a'; 2 | export {default as b} from './b'; 3 | export {default as c} from './c'; 4 | -------------------------------------------------------------------------------- /test/pkg-app/node_modules/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "browserify": { 3 | "transform": [ 4 | "../../../../" 5 | ] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/bundle/react-flow.js: -------------------------------------------------------------------------------- 1 | module.exports = (React) => { 2 | var TestComponent = React.createClass({ 3 | envLength() { 4 | return process.env.NODE_ENV.length; 5 | }, 6 | render() { 7 | return
; 8 | } 9 | }); 10 | return TestComponent; 11 | } 12 | -------------------------------------------------------------------------------- /test/pkg-app/node_modules/app/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (React) => { 2 | var TestComponent = React.createClass({ 3 | envLength() { 4 | return process.env.NODE_ENV.length; 5 | }, 6 | render() { 7 | return
; 8 | } 9 | }); 10 | return TestComponent; 11 | } 12 | -------------------------------------------------------------------------------- /test/error.js: -------------------------------------------------------------------------------- 1 | var browserify = require('browserify'); 2 | var path = require('path'); 3 | var test = require('tape'); 4 | var babelify = require('../'); 5 | 6 | test('emits error', function(t) { 7 | t.plan(2); 8 | 9 | var b = browserify(path.join(__dirname, 'bundle/error.js')); 10 | 11 | b.transform(babelify.configure({})); 12 | 13 | b.bundle(function (err, src) { 14 | t.notOk(src); 15 | t.ok(err); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/options.js: -------------------------------------------------------------------------------- 1 | var browserify = require('browserify'); 2 | var test = require('tape'); 3 | var path = require('path'); 4 | var babelify = require('../'); 5 | 6 | test('passes options via configure', function(t) { 7 | t.plan(3); 8 | 9 | var b = browserify(path.join(__dirname, 'bundle/index.js')); 10 | 11 | b.transform(babelify.configure({ 12 | // plugins: ['@babel/plugin-transform-property-literals'] 13 | })); 14 | 15 | b.bundle(function (err, src) { 16 | t.error(err); 17 | t.ok(src.toString().match(/'?catch'?: `catch`/)); 18 | t.ok(src.toString().match(/'?delete'?: `delete`/)); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/browserify-package.js: -------------------------------------------------------------------------------- 1 | var browserify = require('browserify'); 2 | var path = require('path'); 3 | var test = require('tape'); 4 | var vm = require('vm'); 5 | 6 | test('browserify package', function (t) { 7 | t.plan(3); 8 | 9 | process.env.NODE_ENV = 'development'; 10 | 11 | var b = browserify(); 12 | b.require(path.join(__dirname, 'pkg-app/index.js'), {expose: 'pkgApp'}); 13 | 14 | b.bundle(function (err, src) { 15 | t.error(err); 16 | 17 | var c = {}; 18 | vm.runInNewContext(src, c); 19 | 20 | c.require('pkgApp')({ 21 | createClass: function(obj) { 22 | t.equal(obj.envLength(), process.env.NODE_ENV.length); 23 | t.equal(obj.displayName, 'TestComponent') 24 | } 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "jsc": { 3 | "parser": { 4 | "syntax": "ecmascript", 5 | "jsx": true, 6 | "numericSeparator": false, 7 | "classPrivateProperty": false, 8 | "privateMethod": false, 9 | "classProperty": true, 10 | "functionBind": false, 11 | "decorators": false, 12 | "decoratorsBeforeExport": false 13 | }, 14 | "transform": { 15 | "react": { 16 | "pragma": "React.createElement", 17 | "pragmaFrag": "React.Fragment", 18 | "throwIfNamespace": true, 19 | "development": true, 20 | "useBuiltins": true 21 | }, 22 | "optimizer": { 23 | "globals": { 24 | "vars": { 25 | "__DEBUG__": "true" 26 | } 27 | } 28 | } 29 | } 30 | }, 31 | "module": { 32 | "type": "commonjs", 33 | "strict": false, 34 | "strictMode": true, 35 | "lazy": false, 36 | "noInterop": false 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/event.js: -------------------------------------------------------------------------------- 1 | var browserify = require('browserify'); 2 | var path = require('path'); 3 | var test = require('tape'); 4 | var swcify = require('../'); 5 | 6 | var files = [ 7 | path.join(__dirname, 'bundle/a.js'), 8 | path.join(__dirname, 'bundle/b.js'), 9 | path.join(__dirname, 'bundle/c.js'), 10 | path.join(__dirname, 'bundle/index.js') 11 | ]; 12 | 13 | test('event', function (t) { 14 | t.plan(7); 15 | 16 | var swcified = []; 17 | 18 | var b = browserify(path.join(__dirname, 'bundle/index.js')); 19 | b.transform([swcify, {}]); 20 | 21 | b.on('transform', function(tr) { 22 | if (tr instanceof swcify) { 23 | tr.once('swcify', function(result, filename) { 24 | swcified.push(filename); 25 | t.equal(typeof result.code, 'string'); 26 | }); 27 | } 28 | }); 29 | 30 | b.bundle(function (err, src) { 31 | t.error(err); 32 | t.ok(src); 33 | t.deepEqual(swcified.sort(), files); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/babel-source-maps.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var swc = require('@swc/core'); 3 | var convert = require('convert-source-map'); 4 | var fs = require('fs'); 5 | var path = require('path'); 6 | var test = require('tape'); 7 | 8 | // Validate assumptions about swc's source maps. 9 | 10 | var sourceFile = path.join(__dirname, 'bundle/index.js'); 11 | assert(path.isAbsolute(sourceFile)); 12 | 13 | var sourceSrc = fs.readFileSync(sourceFile, 'utf8'); 14 | 15 | test('swc source maps (filename and sourceFileName)', function(t) { 16 | var result = swc.transformSync(sourceSrc, { 17 | sourceMaps: 'inline', 18 | filename: sourceFile, 19 | sourceFileName: sourceFile, 20 | }); 21 | 22 | // With "sourceFileName", the source path is "sourceFileName". 23 | var sm = convert 24 | .fromJSON(result.map) 25 | // .fromSource(result.code.toString()) 26 | .toObject(); 27 | 28 | t.deepEqual(sm.sources, [sourceFile]); 29 | 30 | t.end(); 31 | }); 32 | -------------------------------------------------------------------------------- /test/aaa.js: -------------------------------------------------------------------------------- 1 | var browserify = require('browserify'); 2 | var test = require('tape'); 3 | var path = require('path'); 4 | var vm = require('vm'); 5 | var swcify = require('../'); 6 | 7 | test('aaa', function (t) { 8 | t.plan(2); 9 | 10 | var b = browserify(); 11 | 12 | b.require(path.join(__dirname, 'bundle/index.js'), {expose: 'bundle'}); 13 | b.transform([swcify]); 14 | 15 | b.bundle(function (err, src) { 16 | t.error(err); 17 | var c = {}; 18 | vm.runInNewContext(src, c); 19 | 20 | t.equal(c.require('bundle').a, 'a is for apple'); 21 | }); 22 | }); 23 | 24 | test('basedir', function (t) { 25 | t.plan(2); 26 | 27 | var b = browserify({basedir: __dirname}); 28 | 29 | b.require(path.join(__dirname, 'bundle/index.js'), {expose: 'bundle'}); 30 | b.transform([swcify]); 31 | 32 | b.bundle(function (err, src) { 33 | t.error(err); 34 | var c = {}; 35 | vm.runInNewContext(src, c); 36 | 37 | t.equal(c.require('bundle').a, 'a is for apple'); 38 | }); 39 | }) 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swcify", 3 | "description": "SWC browserify transform", 4 | "version": "1.0.1", 5 | "author": "Dmitry Yvanov ", 6 | "license": "MIT", 7 | "homepage": "https://github.com/swc-project/swcify", 8 | "engines": { 9 | "node": ">=6.9.0" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/swc-project/swcify.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/swc-project/swcify/issues" 17 | }, 18 | "peerDependencies": { 19 | "@swc/core": "^1.0.25" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.3.4", 23 | "@swc/core": "^1.0.28", 24 | "browserify": "^16.2.3", 25 | "convert-source-map": "^1.5.1", 26 | "cross-spawn": "^6.0.5", 27 | "lodash.zipobject": "^4.1.3", 28 | "tape": "^4.10.1" 29 | }, 30 | "scripts": { 31 | "test": "tape test/*.js" 32 | }, 33 | "dependencies": { 34 | "inline-source-map-comment": "^1.0.5", 35 | "source-map-to-comment": "^1.1.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Dmitry Ivanov 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /test/source-maps-absolute.js: -------------------------------------------------------------------------------- 1 | var browserify = require('browserify'); 2 | var convert = require('convert-source-map'); 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var zipObject = require('lodash.zipobject'); 6 | var test = require('tape'); 7 | var babelify = require('../'); 8 | 9 | var sources = [ 10 | path.join(__dirname, 'bundle/index.js'), 11 | path.join(__dirname, 'bundle/a.js'), 12 | path.join(__dirname, 'bundle/b.js'), 13 | path.join(__dirname, 'bundle/c.js') 14 | ].reduce(function(acc, file) { 15 | acc[file] = fs.readFileSync(file, 'utf8'); 16 | return acc; 17 | }, {}); 18 | 19 | test.skip('sourceMapsAbsolute', function(t) { 20 | t.plan(2); 21 | 22 | var b = browserify({ 23 | entries: [path.join(__dirname, 'bundle/index.js')], 24 | debug: true 25 | }); 26 | 27 | b.transform(babelify.configure({ 28 | // presets: ['@babel/preset-env'], 29 | // sourceMapsAbsolute: true 30 | })); 31 | 32 | b.bundle(function (err, src) { 33 | t.error(err); 34 | 35 | var sm = convert 36 | .fromSource(src.toString()) 37 | .toObject(); 38 | 39 | // remove the prelude 40 | sm.sources.shift(); 41 | sm.sourcesContent.shift(); 42 | 43 | t.match(zipObject(sm.sources, sm.sourcesContent), sources); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swcify [![Build Status](https://travis-ci.org/dy/swcify.svg?branch=master)](https://travis-ci.org/dy/swcify) 2 | 3 | [SWC](https://github.com/swc-project/swc) [browserify](https://github.com/substack/node-browserify) transform. Enables ES6/JSX/react for browserify times faster than babelify. 4 | 5 | ## Installation 6 | 7 | ```sh 8 | $ npm install --save-dev swcify @swc/core 9 | ``` 10 | 11 | ## Usage 12 | 13 | ### CLI 14 | 15 | ```sh 16 | $ browserify script.js -t swcify > bundle.js 17 | ``` 18 | 19 | By default, swcify reads `.swcrc` file in the basedir with SWC config. 20 | 21 | ### Node 22 | 23 | ```javascript 24 | var fs = require("fs") 25 | var browserify = require("browserify") 26 | 27 | var b = browserify("./script.js") 28 | .transform("swcify", config) 29 | .bundle() 30 | .pipe(fs.createWriteStream("bundle.js")); 31 | ``` 32 | 33 | The optional argument `config` can be an object with options for SWC. 34 | 35 | 36 | ## FAQ 37 | 38 | ### Why aren't files in `node_modules` being transformed? 39 | 40 | This is the default browserify behavior. 41 | 42 | A possible solution is to add: 43 | 44 | ```json 45 | { 46 | "browserify": { 47 | "transform": ["swcify"] 48 | } 49 | } 50 | ``` 51 | 52 | to the root of all your modules `package.json` that you want to be transformed. 53 | 54 | 55 | ### Why am I not getting source maps? 56 | 57 | To use source maps, enable them in browserify with the [`debug`](https://github.com/substack/node-browserify#browserifyfiles--opts) option: 58 | 59 | ```js 60 | browserify({debug: true}).transform("swcify"); 61 | ``` 62 | 63 | ```sh 64 | $ browserify -d -t swcify 65 | ``` 66 | -------------------------------------------------------------------------------- /test/source-maps-relative.js: -------------------------------------------------------------------------------- 1 | var browserify = require('browserify'); 2 | var convert = require('convert-source-map'); 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var zipObject = require('lodash.zipobject'); 6 | var test = require('tape'); 7 | var swcify = require('../'); 8 | 9 | process.chdir(path.join(__dirname, '..')); 10 | 11 | var sources = [ 12 | path.join(__dirname, 'bundle/index.js'), 13 | path.join(__dirname, 'bundle/a.js'), 14 | path.join(__dirname, 'bundle/b.js'), 15 | path.join(__dirname, 'bundle/c.js') 16 | ].reduce(function(acc, file) { 17 | acc[file] = fs.readFileSync(file, 'utf8'); 18 | return acc; 19 | }, {}); 20 | 21 | test.skip('source maps relative (cwd)', function(t) { 22 | t.plan(2); 23 | 24 | var b = browserify({ 25 | entries: [path.join(__dirname, 'bundle/index.js')], 26 | debug: true 27 | }); 28 | 29 | b.transform(swcify.configure({ 30 | //presets: ['@babel/preset-env'] 31 | })); 32 | 33 | b.bundle(function(err, src) { 34 | t.error(err); 35 | 36 | var sm = convert 37 | .fromSource(src.toString()) 38 | .toObject(); 39 | 40 | // remove the prelude 41 | sm.sources.shift(); 42 | sm.sourcesContent.shift(); 43 | 44 | var actual = zipObject(sm.sources, sm.sourcesContent); 45 | 46 | var expected = Object.keys(sources).reduce(function(acc, file) { 47 | acc[path.relative(process.cwd(), file)] = sources[file]; 48 | return acc; 49 | }, {}); 50 | t.equal(actual, expected); 51 | }); 52 | }); 53 | 54 | test.skip('source maps relative (basedir)', function(t) { 55 | t.plan(2); 56 | 57 | var b = browserify({ 58 | entries: [path.join(__dirname, 'bundle/index.js')], 59 | basedir: __dirname, 60 | debug: true 61 | }); 62 | 63 | b.transform(swcify.configure({ 64 | presets: ['@babel/preset-env'] 65 | })); 66 | 67 | b.bundle(function(err, src) { 68 | t.error(err); 69 | 70 | var sm = convert 71 | .fromSource(src.toString()) 72 | .toObject(); 73 | 74 | // remove the prelude 75 | sm.sources.shift(); 76 | sm.sourcesContent.shift(); 77 | 78 | var actual = zipObject(sm.sources, sm.sourcesContent); 79 | 80 | var expected = Object.keys(sources).reduce(function(acc, file) { 81 | acc[path.relative(__dirname, file)] = sources[file]; 82 | return acc; 83 | }, {}); 84 | 85 | t.match(actual, expected); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/browserify-cli.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var spawn = require('cross-spawn'); 3 | var test = require('tape'); 4 | var vm = require('vm'); 5 | 6 | test('browserify-cli no subargs', function (t) { 7 | t.plan(5); 8 | 9 | var cmd = require.resolve('browserify/bin/cmd.js'); 10 | var args = [ 11 | '-r', path.join(__dirname, '/bundle/index.js') + ':bundle', 12 | '-t', path.join(__dirname, '../') 13 | ]; 14 | 15 | var out = ''; 16 | var err = ''; 17 | 18 | var ps = spawn(cmd, args); 19 | ps.stdout.on('data', function(buf) { out += buf; }); 20 | ps.stderr.on('data', function(buf) { err += buf; }); 21 | 22 | ps.on('error', function(err) { throw err; }); 23 | ps.on('exit', function(code) { 24 | t.notOk(err); 25 | t.equal(code, 0); 26 | 27 | var c = {}; 28 | vm.runInNewContext(out, c); 29 | 30 | t.equal(c.require('bundle').a, 'a is for apple'); 31 | t.ok(out.toString().match(/'?catch'?: `catch`/)); 32 | t.ok(out.toString().match(/'?delete'?: `delete`/)); 33 | }); 34 | }); 35 | 36 | test.skip('browserify-cli with subargs', function (t) { 37 | // FIXME: react-flow is not supported for now 38 | 39 | t.plan(4); 40 | 41 | process.env.NODE_ENV = 'development'; 42 | 43 | var cmd = require.resolve('browserify/bin/cmd.js'); 44 | var args = [ 45 | '-r', path.join(__dirname, '/bundle/react-flow.js') + ':reactFlow', 46 | '-t', '[', 47 | path.join(__dirname, '../'), 48 | // '--presets', '[', '@swc/preset-env', '@swc/preset-react', '@swc/preset-flow', ']', 49 | // '--plugins', '[', 50 | // '@swc/plugin-transform-react-display-name', 51 | // 'transform-node-env-inline', 52 | // ']', 53 | // '--config=123', 54 | ']' 55 | ]; 56 | 57 | var out = ''; 58 | var err = ''; 59 | 60 | var ps = spawn(cmd, args); 61 | ps.stdout.on('data', function(buf) { console.log(buf.toString()); out += buf; }); 62 | ps.stderr.on('data', function(buf) { err += buf; }); 63 | 64 | ps.on('error', function(err) {throw err; }); 65 | ps.on('exit', function(code) { 66 | console.log(err) 67 | t.notOk(err); 68 | t.equal(code, 0); 69 | 70 | var c = {}; 71 | vm.runInNewContext(out, c); 72 | 73 | c.require('reactFlow')({ 74 | createClass: function(obj) { 75 | t.equal(obj.envLength(), process.env.NODE_ENV.length); 76 | t.equal(obj.displayName, 'TestComponent'); 77 | } 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/browser-pack-source-maps.js: -------------------------------------------------------------------------------- 1 | var browserify = require('browserify'); 2 | var convert = require('convert-source-map'); 3 | var path = require('path'); 4 | var zipObject = require('lodash.zipobject'); 5 | var test = require('tape'); 6 | var swcify = require('../'); 7 | 8 | // Validate assumptions about browserify's browser-pack source maps. Without 9 | // intermediate source maps, the source is a relative path from the "basedir". 10 | // When "basedir" is not set, it's `process.cwd()`. 11 | 12 | test('browserify source maps (no basedir)', function(t) { 13 | t.plan(14); 14 | 15 | // normalize cwd 16 | process.chdir(path.join(__dirname, '..')); 17 | 18 | var b = browserify({ 19 | entries: [path.join(__dirname, 'bundle/index.js')], 20 | debug: true 21 | }); 22 | 23 | b.transform(swcify, { 24 | // presets: ['@babel/preset-env'], 25 | sourceMaps: false // no intermediate source maps 26 | }); 27 | 28 | var deps = {}; 29 | b.on('dep', function(dep) { 30 | t.ok(path.isAbsolute(dep.file)); 31 | deps[dep.file] = dep.source; 32 | }); 33 | 34 | b.bundle(function (err, src) { 35 | t.error(err); 36 | 37 | var sm = convert 38 | .fromSource(src.toString()) 39 | .toObject(); 40 | 41 | // remove the prelude 42 | sm.sources.shift(); 43 | sm.sourcesContent.shift(); 44 | 45 | // source paths are relative to the basedir (cwd if not set) 46 | sm.sources.forEach(function(source) { 47 | t.ok(!path.isAbsolute(source)); 48 | var aSource = path.join(__dirname, '..', source); 49 | t.ok(deps.hasOwnProperty(aSource)); 50 | }); 51 | 52 | var smDeps = zipObject( 53 | sm.sources.map(function(x) { return path.join(__dirname, '..', x); }), 54 | sm.sourcesContent 55 | ); 56 | 57 | t.deepEqual(sort(smDeps), sort(deps)); 58 | }); 59 | }); 60 | 61 | test('browserify source maps (with basedir)', function(t) { 62 | t.plan(14); 63 | 64 | // normalize cwd 65 | process.chdir(path.join(__dirname, '..')); 66 | 67 | var b = browserify({ 68 | entries: [path.join(__dirname, 'bundle/index.js')], 69 | basedir: __dirname, 70 | debug: true 71 | }); 72 | 73 | b.transform(swcify, { 74 | // presets: ['@babel/preset-env'], 75 | sourceMaps: false // no intermediate source maps 76 | }); 77 | 78 | var deps = {}; 79 | b.on('dep', function(dep) { 80 | t.ok(path.isAbsolute(dep.file)); 81 | deps[dep.file] = dep.source; 82 | }); 83 | 84 | b.bundle(function (err, src) { 85 | t.error(err); 86 | 87 | var sm = convert 88 | .fromSource(src.toString()) 89 | .toObject(); 90 | 91 | // remove the prelude 92 | sm.sources.shift(); 93 | sm.sourcesContent.shift(); 94 | 95 | // source paths are relative to the basedir (cwd if not set) 96 | sm.sources.forEach(function(source) { 97 | t.ok(!path.isAbsolute(source)); 98 | var aSource = path.join(__dirname, source); 99 | t.ok(deps.hasOwnProperty(aSource)); 100 | }); 101 | 102 | var smDeps = zipObject( 103 | sm.sources.map(function(x) { return path.join(__dirname, x); }), 104 | sm.sourcesContent 105 | ); 106 | 107 | t.deepEqual(sort(smDeps), sort(deps)); 108 | }); 109 | }); 110 | 111 | function sort(obj) { 112 | return Object.keys(obj).sort().reduce(function(acc, k) { 113 | acc[k] = obj[k]; 114 | return acc; 115 | }, {}); 116 | } 117 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var stream = require("stream"); 4 | var util = require("util"); 5 | var path = require("path"); 6 | var swc = require("@swc/core"); 7 | var fs = require("fs"); 8 | var inlineSourcemap = require('inline-source-map-comment') 9 | 10 | var defaults = { 11 | "jsc": { 12 | "parser": { 13 | "syntax": "ecmascript", 14 | "jsx": true, 15 | "numericSeparator": false, 16 | "classPrivateProperty": true, 17 | "privateMethod": false, 18 | "classProperty": true, 19 | "functionBind": false, 20 | "decorators": false, 21 | "decoratorsBeforeExport": false 22 | }, 23 | "target": "es2016", 24 | "transform": { 25 | "react": { 26 | "pragma": "React.createElement", 27 | "pragmaFrag": "React.Fragment", 28 | "throwIfNamespace": true, 29 | "development": false, 30 | "useBuiltins": false 31 | }, 32 | "optimizer": { 33 | "globals": { 34 | "vars": { 35 | "__DEBUG__": "true" 36 | } 37 | } 38 | } 39 | } 40 | }, 41 | "module": { 42 | "type": "commonjs", 43 | "strict": false, 44 | "strictMode": true, 45 | "lazy": false, 46 | "noInterop": false 47 | } 48 | } 49 | 50 | module.exports = buildTransform(); 51 | module.exports.defaults = defaults; 52 | module.exports.configure = buildTransform; 53 | 54 | 55 | // TODO: essentially we have to handle all CLI-tool options 56 | // some of them are taken from browserify flags 57 | 58 | // Allow projects to import this module and check `foo instanceof swcify` 59 | // to see if the current stream they are working with is one created 60 | // by SWCify. 61 | Object.defineProperty(module.exports, Symbol.hasInstance, { 62 | value: function hasInstance(obj) { 63 | return obj instanceof SwcifyStream; 64 | }, 65 | }); 66 | 67 | function buildTransform(opts) { 68 | var configCache = {} 69 | 70 | return function (filename, config) { 71 | 72 | var _flags = config._flags || {} 73 | config = Object.assign({}, config) 74 | delete config._flags 75 | 76 | // unwrap nested config 77 | if (config.config) { 78 | config = config 79 | } 80 | 81 | var basedir = path.resolve(_flags.basedir || "."); 82 | var configPath = path.resolve(basedir, '.swcrc'); 83 | 84 | var configJSON, parsedConfig = configCache[configPath]; 85 | 86 | // if no cached config found - try to read from file 87 | if (!parsedConfig && fs.existsSync(configPath)) { 88 | // read filepath config 89 | // read .swcrc relative to basedir 90 | // Browserify doesn't actually always normalize the filename passed 91 | // to transforms, so we manually ensure that the filename is relative 92 | configJSON = fs.readFileSync(configPath, 'utf-8'); 93 | // bad config will throw error 94 | parsedConfig = JSON.parse(configJSON) 95 | configCache[configPath] = parsedConfig 96 | } 97 | 98 | // no config found, falling back to default options 99 | // create current config from options extended with the config 100 | config = Object.assign({ 101 | sourceMaps: _flags.debug 102 | }, defaults, opts, parsedConfig, config) 103 | 104 | // normalize config quirks 105 | if (!config.sourceMaps) delete config.sourceMaps 106 | 107 | return new SwcifyStream({ 108 | config: config, 109 | filename: path.resolve(basedir, filename) 110 | }); 111 | }; 112 | } 113 | 114 | class SwcifyStream extends stream.Transform { 115 | constructor(opts) { 116 | super(); 117 | this._code = []; 118 | this._sourceMap = []; 119 | if (!opts) opts = {} 120 | this._opts = opts; 121 | } 122 | 123 | _transform(buf, enc, callback) { 124 | var self = this 125 | swc.transform(buf.toString(), this._opts.config).then(function(result) { 126 | var code = result !== null ? result.code : data; 127 | 128 | if (result.map) { 129 | code += inlineSourcemap(result.map) 130 | } 131 | self.push(code); 132 | self._code.push(code); 133 | self._sourceMap.push(result && result.map) 134 | callback(); 135 | }, callback) 136 | } 137 | 138 | _flush(callback) { 139 | // Merge the buffer pieces after all are available, instead of one at a time, 140 | // to avoid corrupting multibyte characters. 141 | this.emit("swcify", { 142 | code: this._code.join('') 143 | // FIXME: how to join sourcemaps 144 | }, this._opts.filename); 145 | callback() 146 | } 147 | } 148 | --------------------------------------------------------------------------------