├── .gitattributes
├── pax
├── examples
│ ├── es6-import-meta
│ │ └── index.mjs
│ ├── simple
│ │ ├── math.js
│ │ ├── index.js
│ │ ├── package.json
│ │ └── package-lock.json
│ ├── es6-simple
│ │ ├── math.mjs
│ │ ├── package.json
│ │ ├── index.mjs
│ │ └── package-lock.json
│ ├── es6-reexport
│ │ ├── inner.mjs
│ │ ├── counter.mjs
│ │ ├── counter2.mjs
│ │ ├── outer.mjs
│ │ └── index.mjs
│ ├── es6-everywhere-simple
│ │ ├── math.js
│ │ ├── package.json
│ │ ├── index.js
│ │ └── package-lock.json
│ ├── module-scope
│ │ ├── dir
│ │ │ └── sub.js
│ │ └── index.js
│ ├── es6-views
│ │ ├── counter.mjs
│ │ └── index.mjs
│ ├── es6-cycles
│ │ ├── even.mjs
│ │ ├── odd.mjs
│ │ └── index.mjs
│ └── es6-forms
│ │ ├── index.mjs
│ │ └── mod.mjs
├── src
│ ├── tail.js
│ ├── head.js
│ ├── opts.rs
│ ├── test
│ │ ├── mod.rs
│ │ └── itt.js
│ ├── es6.rs
│ └── main.rs
├── Cargo.toml
└── LICENSE.md
├── pax.sublime-project
├── Cargo.toml
├── .gitignore
├── .travis.yml
├── esparse
├── Cargo.toml
├── LICENSE.md
├── src
│ ├── lib.rs
│ ├── ast.rs
│ └── skip.rs
└── examples
│ └── lex-echo.rs
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | esparse/data/**/*.js linguist-vendored
2 |
--------------------------------------------------------------------------------
/pax/examples/es6-import-meta/index.mjs:
--------------------------------------------------------------------------------
1 | console.log(import.meta.url)
2 |
--------------------------------------------------------------------------------
/pax/examples/simple/math.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | exports.square = x => x * x
3 |
--------------------------------------------------------------------------------
/pax/examples/es6-simple/math.mjs:
--------------------------------------------------------------------------------
1 | export const square = x => x * x, cube = x => x * x * x
2 |
--------------------------------------------------------------------------------
/pax.sublime-project:
--------------------------------------------------------------------------------
1 | {
2 | "folders":
3 | [
4 | {
5 | "path": "."
6 | }
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/pax/examples/es6-reexport/inner.mjs:
--------------------------------------------------------------------------------
1 | export default 'something'
2 | export const CONST = 1e9
3 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = ["esparse", "pax"]
3 |
4 | [profile.release]
5 | lto = true
6 |
--------------------------------------------------------------------------------
/pax/examples/es6-everywhere-simple/math.js:
--------------------------------------------------------------------------------
1 | export const square = x => x * x, cube = x => x * x * x
2 |
--------------------------------------------------------------------------------
/pax/examples/module-scope/dir/sub.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = {__filename, __dirname}
4 |
--------------------------------------------------------------------------------
/pax/examples/es6-simple/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "itt": "^0.2.1"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/pax/src/tail.js:
--------------------------------------------------------------------------------
1 | }(typeof global !== "undefined" ? global : typeof window !== "undefined" ? window : this)
2 |
--------------------------------------------------------------------------------
/pax/examples/es6-everywhere-simple/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "itt": "^0.2.1"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/pax/examples/es6-views/counter.mjs:
--------------------------------------------------------------------------------
1 | export let counter = 3
2 |
3 | export function incCounter() {
4 | ++counter
5 | }
6 |
--------------------------------------------------------------------------------
/pax/examples/es6-reexport/counter.mjs:
--------------------------------------------------------------------------------
1 | export let counter = 3
2 |
3 | export function incCounter() {
4 | ++counter
5 | }
6 |
--------------------------------------------------------------------------------
/pax/examples/es6-reexport/counter2.mjs:
--------------------------------------------------------------------------------
1 | export let counter = 1
2 |
3 | export function incCounter() {
4 | ++counter
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | **/*.rs.bk
3 | Cargo.lock
4 | private
5 | node_modules
6 | *.sublime-workspace
7 | .DS_Store
8 | .cargo
9 |
--------------------------------------------------------------------------------
/pax/examples/es6-cycles/even.mjs:
--------------------------------------------------------------------------------
1 | import odd from './odd'
2 |
3 | export default function even(n) {
4 | return n == 0 || odd(n - 1)
5 | }
6 |
--------------------------------------------------------------------------------
/pax/examples/es6-cycles/odd.mjs:
--------------------------------------------------------------------------------
1 | import even from './even'
2 | export default function odd(n) {
3 | return n != 0 && even(n - 1)
4 | }
5 |
--------------------------------------------------------------------------------
/pax/examples/simple/index.js:
--------------------------------------------------------------------------------
1 | const itt = require('itt')
2 | const math = require('./math')
3 |
4 | console.log(itt.range(10).map(math.square).join(' '))
5 |
--------------------------------------------------------------------------------
/pax/examples/module-scope/index.js:
--------------------------------------------------------------------------------
1 | const sub = require('./dir/sub')
2 |
3 | console.log(__filename, __dirname)
4 | console.log(sub.__filename, sub.__dirname)
5 |
--------------------------------------------------------------------------------
/pax/examples/simple/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple",
3 | "private": true,
4 | "main": "index.js",
5 | "dependencies": {
6 | "itt": "^0.2.1"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: rust
2 | rust:
3 | - stable
4 | - beta
5 | - nightly
6 | matrix:
7 | allow_failures:
8 | - rust: nightly
9 | fast_finish: true
10 |
--------------------------------------------------------------------------------
/pax/examples/es6-reexport/outer.mjs:
--------------------------------------------------------------------------------
1 | export * from './counter'
2 | export {counter as c1, incCounter as i1} from './counter2'
3 | export {default as SOMETHING, CONST as default} from './inner'
4 |
--------------------------------------------------------------------------------
/pax/examples/es6-views/index.mjs:
--------------------------------------------------------------------------------
1 | import { counter, incCounter } from './counter'
2 |
3 | console.log(counter) // 3
4 | incCounter()
5 | console.log(counter) // 4
6 |
7 | ++counter // TypeError
8 |
--------------------------------------------------------------------------------
/pax/examples/es6-cycles/index.mjs:
--------------------------------------------------------------------------------
1 | // export {default as even} from './even'
2 | // export {default as odd} from './odd'
3 |
4 | import isEven from './even'
5 |
6 | console.log(isEven(5), isEven(6))
7 |
--------------------------------------------------------------------------------
/pax/examples/es6-simple/index.mjs:
--------------------------------------------------------------------------------
1 | import itt from 'itt'
2 | import { square, cube } from './math'
3 |
4 | console.log(itt.range(10).map(square).join(' '))
5 | console.log(itt.range(10).map(cube).join(' '))
6 |
--------------------------------------------------------------------------------
/pax/examples/es6-everywhere-simple/index.js:
--------------------------------------------------------------------------------
1 | import itt from 'itt'
2 | import { square, cube } from './math'
3 |
4 | console.log(itt.range(10).map(square).join(' '))
5 | console.log(itt.range(10).map(cube).join(' '))
6 |
--------------------------------------------------------------------------------
/pax/examples/es6-reexport/index.mjs:
--------------------------------------------------------------------------------
1 | import CONST, {c1, i1, counter, incCounter, SOMETHING} from './outer'
2 |
3 | console.log(CONST)
4 | console.log(SOMETHING)
5 |
6 | console.log(counter) // 3
7 | incCounter()
8 | console.log(counter) // 4
9 |
10 | console.log(c1) // 1
11 | i1()
12 | console.log(c1) // 2
13 |
14 | ++counter // TypeError
15 |
--------------------------------------------------------------------------------
/pax/examples/es6-simple/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "requires": true,
3 | "lockfileVersion": 1,
4 | "dependencies": {
5 | "itt": {
6 | "version": "0.2.6",
7 | "resolved": "https://registry.npmjs.org/itt/-/itt-0.2.6.tgz",
8 | "integrity": "sha512-AbQtJhPL/Am3Ie08oTWz4DHLtZo6q0PQ2iuBGO3Y/Ra8XLrRDSp/WupvRptDCTyRMNFrrj9DIJmoKtXBds339Q=="
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pax/examples/es6-everywhere-simple/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "requires": true,
3 | "lockfileVersion": 1,
4 | "dependencies": {
5 | "itt": {
6 | "version": "0.2.6",
7 | "resolved": "https://registry.npmjs.org/itt/-/itt-0.2.6.tgz",
8 | "integrity": "sha512-AbQtJhPL/Am3Ie08oTWz4DHLtZo6q0PQ2iuBGO3Y/Ra8XLrRDSp/WupvRptDCTyRMNFrrj9DIJmoKtXBds339Q=="
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pax/examples/simple/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple",
3 | "requires": true,
4 | "lockfileVersion": 1,
5 | "dependencies": {
6 | "itt": {
7 | "version": "0.2.6",
8 | "resolved": "https://registry.npmjs.org/itt/-/itt-0.2.6.tgz",
9 | "integrity": "sha512-AbQtJhPL/Am3Ie08oTWz4DHLtZo6q0PQ2iuBGO3Y/Ra8XLrRDSp/WupvRptDCTyRMNFrrj9DIJmoKtXBds339Q=="
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/pax/examples/es6-forms/index.mjs:
--------------------------------------------------------------------------------
1 | import def from './mod'
2 | import * as ns from './mod'
3 | import {} from './mod'
4 | import { a } from './mod'
5 | import { a, } from './mod'
6 | import { a as b, } from './mod'
7 | import { a, b } from './mod'
8 | import { a, b, } from './mod'
9 | import { a as b, c as d } from './mod'
10 | import def, { a } from './mod'
11 | import def, * as ns from './mod'
12 |
13 | console.log(a)
14 |
--------------------------------------------------------------------------------
/pax/examples/es6-forms/mod.mjs:
--------------------------------------------------------------------------------
1 | export default 0
2 | export default class Test {}
3 | export default function test() {}
4 | export default function* testGen() {}
5 |
6 | export var asdf
7 | export let a = 1, b = (1, 2), c = 3, d = (za, zb) => b, e
8 | export const j = class A extends B(c, d) {}, k = 1
9 | export class Test2 {}
10 | export function test2() {}
11 | export function* testGen2() {}
12 |
13 | function B() {return null}
14 |
15 | var va, vb
16 | export {va as vaz, vb}
17 |
--------------------------------------------------------------------------------
/esparse/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "esparse"
3 | version = "0.1.0" # should match html_root_url
4 | authors = ["Nathan"]
5 |
6 | description = "A fast JavaScript parser. Currently only a lexical analyzer."
7 | documentation = "https://docs.rs/esparse"
8 | repository = "https://github.com/nathan/pax"
9 | license = "MIT"
10 |
11 | keywords = ["js", "javascript", "es", "ecmascript", "parser"]
12 | categories = ["parser-implementations"]
13 |
14 | exclude = [
15 | "private/*",
16 | "data/*",
17 | ]
18 |
19 | [features]
20 | default = []
21 | bench = []
22 |
23 | [dependencies]
24 | memchr = "2.0.1"
25 | matches = "0.1"
26 | cfg-if = "0.1.4"
27 | unicode-xid = "0.1.0"
28 |
--------------------------------------------------------------------------------
/pax/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "pax"
3 | version = "0.4.0"
4 | authors = ["Nathan"]
5 |
6 | description = "The fastest JavaScript bundler in the galaxy."
7 | repository = "https://github.com/nathan/pax"
8 | license = "MIT"
9 | readme = "../README.md"
10 |
11 | keywords = ["js", "javascript", "es", "ecmascript", "bundler"]
12 | categories = ["development-tools", "command-line-utilities", "web-programming"]
13 |
14 | autobins = true
15 | exclude = [
16 | "examples/*",
17 | "private/*",
18 | "src/test/*",
19 | ]
20 |
21 | [[bin]]
22 | name = "px"
23 | path = "src/main.rs"
24 |
25 | [features]
26 | default = []
27 | bench = []
28 |
29 | [dependencies]
30 | esparse = { version = "0.1.0", path = "../esparse" }
31 | regex = "1"
32 | fnv = "1.0.3"
33 | lazy_static = "1.1.0"
34 | cfg-if = "0.1.4"
35 | crossbeam = "0.2"
36 | num_cpus = "1.6"
37 | matches = "0.1"
38 | serde = "1.0"
39 | serde_derive = "1.0"
40 | serde_json = "1.0"
41 | notify = "4.0"
42 | memchr = "2.0.1"
43 | base64 = "0.6.0"
44 |
--------------------------------------------------------------------------------
/pax/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2018 Nathan
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/esparse/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2017 Nathan
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/esparse/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! A fast JavaScript parser. Currently only a [lexical analyzer](lex/index.html) and a [skipper](skip/index.html).
2 |
3 | #![cfg_attr(all(test, feature = "bench"), feature(test))]
4 |
5 | #![warn(missing_docs)]
6 | #![doc(html_root_url = "https://docs.rs/esparse/0.1.0")]
7 |
8 | #[macro_use]
9 | extern crate matches;
10 | extern crate memchr;
11 | extern crate unicode_xid;
12 |
13 | #[cfg(test)]
14 | #[macro_use]
15 | extern crate cfg_if;
16 |
17 | #[macro_use]
18 | pub mod lex;
19 | pub mod skip;
20 | pub mod ast;
21 |
22 | pub use ast::{Loc, Span};
23 |
24 | #[doc(hidden)]
25 | #[derive(Debug, Clone, PartialEq, Eq, Hash)]
26 | pub struct ParseOptions<'a> {
27 | pub first_line: usize,
28 | pub file_name: &'a str,
29 | }
30 |
31 | impl<'a> Default for ParseOptions<'a> {
32 | fn default() -> Self {
33 | ParseOptions {
34 | first_line: 0,
35 | file_name: "",
36 | }
37 | }
38 | }
39 |
40 | #[doc(hidden)]
41 | pub fn parse_script(_input: &str, _options: ParseOptions) -> ! {
42 | unimplemented!()
43 | }
44 | #[doc(hidden)]
45 | pub fn parse_module(_input: &str, _options: ParseOptions) -> ! {
46 | unimplemented!()
47 | }
48 | #[doc(hidden)]
49 | pub fn parse_expr(_input: &str, _options: ParseOptions) -> ! {
50 | unimplemented!()
51 | }
52 |
53 | #[cfg(test)]
54 | mod test {
55 | }
56 |
--------------------------------------------------------------------------------
/esparse/examples/lex-echo.rs:
--------------------------------------------------------------------------------
1 | extern crate esparse;
2 |
3 | use std::{env, process, io, fs};
4 | use std::io::prelude::*;
5 | use esparse::lex;
6 |
7 | fn run() -> Result<(), CliError> {
8 | let file_name = env::args().nth(1).ok_or(CliError::MissingFileName)?;
9 | let file = fs::File::open(&file_name)?;
10 | let mut buf_reader = io::BufReader::new(file);
11 | let mut contents = String::new();
12 | buf_reader.read_to_string(&mut contents)?;
13 |
14 | let stdout = io::stdout();
15 | let mut stdout = stdout.lock();
16 | let mut lexer = lex::Lexer::new(&file_name, &contents);
17 | loop {
18 | let tok = lexer.advance();
19 | write!(stdout, "{}{}", tok.ws_before, tok.tt).unwrap();
20 | if tok.tt == lex::Tt::Eof {
21 | break
22 | }
23 | }
24 | // writeln!(stdout).unwrap();
25 | Ok(())
26 | }
27 |
28 | const APP_NAME: &'static str = env!("CARGO_PKG_NAME");
29 |
30 | fn print_usage() {
31 | println!("usage: {} ", APP_NAME);
32 | }
33 |
34 | enum CliError {
35 | MissingFileName,
36 | Io(io::Error),
37 | }
38 | impl From for CliError {
39 | fn from(inner: io::Error) -> CliError {
40 | CliError::Io(inner)
41 | }
42 | }
43 |
44 | fn main() {
45 | process::exit(match run() {
46 | Ok(_) => 0,
47 | Err(kind) => {
48 | match kind {
49 | CliError::MissingFileName => print_usage(),
50 | CliError::Io(inner) => println!("{}: {}", APP_NAME, inner),
51 | }
52 | 1
53 | }
54 | })
55 | }
56 |
--------------------------------------------------------------------------------
/pax/src/head.js:
--------------------------------------------------------------------------------
1 | ~function(global) {
2 | const Pax = {}
3 | Pax.baseRequire = typeof require !== "undefined" ? require : n => {
4 | throw new Error(`Could not resolve module name: ${n}`)
5 | }
6 | Pax.modules = {}
7 | Pax.files = {}
8 | Pax.mains = {}
9 | Pax.resolve = (base, then) => {
10 | base = base.split('/')
11 | base.shift()
12 | for (const p of then.split('/')) {
13 | if (p === '..') base.pop()
14 | else if (p !== '.') base.push(p)
15 | }
16 | return '/' + base.join('/')
17 | }
18 | Pax.Module = function Module(filename, parent) {
19 | this.filename = filename
20 | this.id = filename
21 | this.loaded = false
22 | this.parent = parent
23 | this.children = []
24 | this.exports = {}
25 | }
26 | Pax.makeRequire = self => {
27 | const require = m => require._module(m).exports
28 | require._deps = {}
29 | require.main = self
30 |
31 | require._esModule = m => {
32 | const mod = require._module(m)
33 | return mod.exports.__esModule ? mod.exports : {
34 | get default() {return mod.exports},
35 | }
36 | }
37 | require._module = m => {
38 | let fn = self ? require._deps[m] : Pax.main
39 | if (fn == null) {
40 | const module = {exports: Pax.baseRequire(m)}
41 | require._deps[m] = {module: module}
42 | return module
43 | }
44 | if (fn.module) return fn.module
45 | const module = new Pax.Module(fn.filename, self)
46 | fn.module = module
47 | module.require = Pax.makeRequire(module)
48 | module.require._deps = fn.deps
49 | module.require.main = self ? self.require.main : module
50 | if (self) self.children.push(module)
51 | fn(module, module.exports, module.require, fn.filename, fn.filename.split('/').slice(0, -1).join('/'), {url: 'file://' + (fn.filename.charAt(0) === '/' ? '' : '/') + fn.filename})
52 | module.loaded = true
53 | return module
54 | }
55 | return require
56 | }
57 |
--------------------------------------------------------------------------------
/esparse/src/ast.rs:
--------------------------------------------------------------------------------
1 | //! Syntactic constructs and related data structures.
2 | use std::fmt;
3 | use std::rc::Rc;
4 |
5 | /// A location in source code.
6 | ///
7 | /// Stores both the bytewise [position](#structfield.pos) and the logical [line](#structfield.row) and [character](#structfield.col) numbers.
8 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
9 | pub struct Loc {
10 | /// 0-based byte index.
11 | pub pos: usize,
12 | /// 0-based line number.
13 | pub row: usize,
14 | /// 0-based character number on the line.
15 | pub col: usize,
16 | }
17 |
18 | impl Loc {
19 | /// Creates a new `Loc` with the given positions.
20 | #[inline]
21 | pub fn new(pos: usize, row: usize, col: usize) -> Self {
22 | Loc {
23 | pos,
24 | row,
25 | col,
26 | }
27 | }
28 |
29 | /// Creates a new `Loc` pointing to the first byte of the source code (`pos`, `row`, and `col` all zero).
30 | #[inline]
31 | pub fn zero() -> Self {
32 | Default::default()
33 | }
34 | }
35 |
36 | /// A region of source code.
37 | ///
38 | /// A pair of locations, representing a half-open range, and a file name, identifying the source code in which this region appears.
39 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40 | pub struct SpanT {
41 | /// The name of the source code.
42 | ///
43 | /// Often a file name, but can be an arbitrary string like `` or even any other type.
44 | pub file_name: F,
45 | /// The (inclusive) starting location.
46 | pub start: L,
47 | /// The (exclusive) ending location.
48 | pub end: L,
49 | }
50 |
51 | // TODO link to parser
52 | /// A `SpanT` with a borrowed string file name.
53 | ///
54 | /// Used widely by the [lexer](../lex/index.html) and parser because it appears in every syntactic construct and is cheap to copy.
55 | ///
56 | /// If the `SpanT` must own its filename, use [`SpanRc`](type.SpanRc.html) instead.
57 | pub type Span<'f, L> = SpanT<&'f str, L>;
58 |
59 | /// A `SpanT` with a reference-counted file name.
60 | ///
61 | /// Useful for creating `SpanT`s which own their file name, but more expensive to clone than a regular [`Span`](type.Span.html).
62 | pub type SpanRc = SpanT, L>;
63 |
64 | impl SpanT {
65 | /// Creates a new `SpanT` with the given file name and locations.
66 | #[inline]
67 | pub fn new(file_name: F, start: L, end: L) -> Self {
68 | SpanT {
69 | file_name,
70 | start,
71 | end,
72 | }
73 | }
74 | }
75 | impl SpanT {
76 | /// Creates an empty `SpanT` at the given location, with the given file name.
77 | #[inline]
78 | pub fn empty(file_name: F, loc: L) -> Self {
79 | SpanT::new(file_name, loc.clone(), loc)
80 | }
81 | }
82 | impl SpanT {
83 | /// Creates an empty `SpanT` with the given file name, pointing to the first position in the file.
84 | #[inline]
85 | pub fn zero(file_name: F) -> Self {
86 | SpanT::new(file_name, Default::default(), Default::default())
87 | }
88 | }
89 |
90 | impl<'f, L: Clone> Span<'f, L> {
91 | /// Converts a `Span` into a [`SpanRc`](type.SpanRc.html) by cloning the borrowed file name.
92 | pub fn with_rc(&self) -> SpanRc {
93 | SpanT::new(Rc::new(self.file_name.to_owned()), self.start.clone(), self.end.clone())
94 | }
95 |
96 | /// Converts a `Span` into a [`SpanT`](struct.SpanT.html) which owns its data by cloning the borrowed file name.
97 | pub fn with_owned(&self) -> SpanT {
98 | SpanT::new(self.file_name.to_owned(), self.start.clone(), self.end.clone())
99 | }
100 | }
101 |
102 | impl fmt::Display for Loc {
103 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104 | write!(f, "{},{}", self.row + 1, self.col + 1)
105 | }
106 | }
107 |
108 | impl fmt::Display for SpanT {
109 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110 | if self.start.row == self.end.row {
111 | if self.start.col == self.end.col {
112 | write!(
113 | f,
114 | "{}:{},{}",
115 | self.file_name,
116 | self.start.row + 1,
117 | self.start.col + 1,
118 | )
119 | } else {
120 | write!(
121 | f,
122 | "{}:{},{}-{}",
123 | self.file_name,
124 | self.start.row + 1,
125 | self.start.col + 1,
126 | self.end.col + 1,
127 | )
128 | }
129 | } else {
130 | write!(
131 | f,
132 | "{}:{},{}-{},{}",
133 | self.file_name,
134 | self.start.row + 1,
135 | self.start.col + 1,
136 | self.end.row + 1,
137 | self.end.col + 1,
138 | )
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/pax/src/opts.rs:
--------------------------------------------------------------------------------
1 | use std::{env, iter};
2 |
3 | pub fn args() -> Expand> {
4 | expand(env::args().skip(1))
5 | }
6 |
7 | pub fn expand>(args: I) -> Expand {
8 | Expand {
9 | arg: None,
10 | args: args.into_iter(),
11 | state: State::Start,
12 | }
13 | }
14 |
15 | #[derive(Debug)]
16 | pub struct Expand {
17 | arg: Option,
18 | args: I,
19 | state: State,
20 | }
21 | #[derive(Debug, Clone, Copy, PartialEq, Eq)]
22 | enum State {
23 | Start,
24 | ShortOption(usize),
25 | Raw,
26 | Done,
27 | }
28 | #[derive(Debug, Clone, PartialEq, Eq)]
29 | pub enum Arg {
30 | Opt(String),
31 | Pos(String),
32 | }
33 | #[derive(Debug)]
34 | enum Type {
35 | Short,
36 | Long,
37 | Raw,
38 | Pos,
39 | }
40 |
41 | impl> Expand {
42 | pub fn next_arg(&mut self) -> Option {
43 | match self.state {
44 | State::Done => None,
45 | State::Start |
46 | State::Raw |
47 | State::ShortOption(_) => self.args.next(),
48 | }
49 | }
50 | }
51 |
52 | impl> Iterator for Expand {
53 | type Item = Arg;
54 |
55 | fn next(&mut self) -> Option {
56 | loop {
57 | match self.state {
58 | State::Start => {
59 | let arg = match self.args.next() {
60 | None => {
61 | self.state = State::Done;
62 | return None
63 | }
64 | Some(arg) => arg,
65 | };
66 |
67 | let ty = {
68 | let mut chars = arg.chars();
69 | match chars.next() {
70 | Some('-') => match chars.next() {
71 | Some('-') => {
72 | if chars.next().is_none() {
73 | Type::Raw
74 | } else {
75 | Type::Long
76 | }
77 | }
78 | Some(_) => Type::Short,
79 | None => Type::Pos,
80 | }
81 | _ => Type::Pos,
82 | }
83 | };
84 | match ty {
85 | Type::Raw => {
86 | self.state = State::Raw;
87 | }
88 | Type::Short => {
89 | self.arg = Some(arg);
90 | self.state = State::ShortOption(1);
91 | }
92 | Type::Long => {
93 | return Some(Arg::Opt(arg))
94 | }
95 | Type::Pos => {
96 | return Some(Arg::Pos(arg))
97 | }
98 | }
99 | }
100 | State::Raw => {
101 | let arg = self.args.next();
102 | if arg.is_none() {
103 | self.state = State::Done;
104 | }
105 | return arg.map(Arg::Pos)
106 | }
107 | State::ShortOption(n) => {
108 | let c = {
109 | let mut indices = self.arg.as_ref().unwrap()[n..].char_indices();
110 | match indices.next() {
111 | Some((_, c)) => {
112 | self.state = match indices.next() {
113 | Some((m, _)) => State::ShortOption(n + m),
114 | None => State::Start,
115 | };
116 | c
117 | }
118 | None => unreachable!(),
119 | }
120 | };
121 | if self.state == State::Start {
122 | self.arg = None;
123 | }
124 | return Some(Arg::Opt(format!("-{}", c)))
125 | }
126 | State::Done => return None,
127 | }
128 | }
129 | }
130 | }
131 | // impl iter::FusedIterator for Expand {}
132 |
133 | #[cfg(test)]
134 | mod test {
135 | use super::*;
136 |
137 | #[test]
138 | fn test_parser() {
139 | let mut args = expand("-f -bz - --a --foo c -- -f -bz - --a --foo c ".split(" ").into_iter().map(ToOwned::to_owned));
140 | assert_eq!(args.next(), Some(Arg::Opt("-f".to_owned())));
141 | assert_eq!(args.next(), Some(Arg::Opt("-b".to_owned())));
142 | assert_eq!(args.next(), Some(Arg::Opt("-z".to_owned())));
143 | assert_eq!(args.next(), Some(Arg::Pos("-".to_owned())));
144 | assert_eq!(args.next(), Some(Arg::Opt("--a".to_owned())));
145 | assert_eq!(args.next(), Some(Arg::Opt("--foo".to_owned())));
146 | assert_eq!(args.next(), Some(Arg::Pos("c".to_owned())));
147 | assert_eq!(args.next(), Some(Arg::Pos("".to_owned())));
148 | assert_eq!(args.next(), Some(Arg::Pos("-f".to_owned())));
149 | assert_eq!(args.next(), Some(Arg::Pos("-bz".to_owned())));
150 | assert_eq!(args.next(), Some(Arg::Pos("-".to_owned())));
151 | assert_eq!(args.next(), Some(Arg::Pos("--a".to_owned())));
152 | assert_eq!(args.next(), Some(Arg::Pos("--foo".to_owned())));
153 | assert_eq!(args.next(), Some(Arg::Pos("c".to_owned())));
154 | assert_eq!(args.next(), Some(Arg::Pos("".to_owned())));
155 | }
156 |
157 | #[test]
158 | fn test_next_arg() {
159 | let mut args = expand("0 -bz 1 2 --something 3 4".split(" ").into_iter().map(ToOwned::to_owned));
160 | assert_eq!(args.next_arg(), Some("0".to_owned()));
161 | assert_eq!(args.next(), Some(Arg::Opt("-b".to_owned())));
162 | assert_eq!(args.next_arg(), Some("1".to_owned()));
163 | assert_eq!(args.next(), Some(Arg::Opt("-z".to_owned())));
164 | assert_eq!(args.next_arg(), Some("2".to_owned()));
165 | assert_eq!(args.next(), Some(Arg::Opt("--something".to_owned())));
166 | assert_eq!(args.next_arg(), Some("3".to_owned()));
167 | assert_eq!(args.next_arg(), Some("4".to_owned()));
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/pax/src/test/mod.rs:
--------------------------------------------------------------------------------
1 | #![allow(unused_imports)]
2 |
3 | #[cfg(feature = "bench")]
4 | extern crate test;
5 |
6 | use std::path::Path;
7 | use std::process;
8 | use super::*;
9 |
10 | #[test]
11 | fn test_count_lines() {
12 | assert_eq!(count_lines(""), 1);
13 | assert_eq!(count_lines("this is a line"), 1);
14 | assert_eq!(count_lines("this is a line\n"), 2);
15 | assert_eq!(count_lines("\nthis is a line"), 2);
16 | assert_eq!(count_lines("\n\n\nthis is a line"), 4);
17 | assert_eq!(count_lines("this is a line\n\n\n"), 4);
18 | assert_eq!(count_lines("these\nare\nlines"), 3);
19 | assert_eq!(count_lines("\r\n"), 2);
20 | assert_eq!(count_lines("this is a line\r\n"), 2);
21 | assert_eq!(count_lines("\r\nthis is a line"), 2);
22 | assert_eq!(count_lines("these\nare\r\nlines"), 3);
23 | }
24 |
25 | #[test]
26 | fn test_vlq() {
27 | // 0000000000000000111111111111111122222222222222223333333333333333
28 | // 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
29 | // ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
30 | let mut vlq = Vlq::new();
31 | assert_eq!(vlq.enc(0), "A");
32 | assert_eq!(vlq.enc(1), "C");
33 | assert_eq!(vlq.enc(-1), "D");
34 | assert_eq!(vlq.enc(5), "K");
35 | assert_eq!(vlq.enc(-5), "L");
36 | assert_eq!(vlq.enc(15), "e");
37 | assert_eq!(vlq.enc(-15), "f");
38 | assert_eq!(vlq.enc(16), "gB");
39 | assert_eq!(vlq.enc(1876), "o1D"); // 11 10101 0100
40 | assert_eq!(vlq.enc(-485223), "v2zd"); // 11101 10011 10110 0111
41 | }
42 |
43 | cfg_if! {
44 | if #[cfg(feature = "bench")] {
45 | fn npm_install(dir: &Path) {
46 | let node_modules = dir.join("node_modules");
47 | if node_modules.is_dir() { return }
48 |
49 | let ok = process::Command::new("npm")
50 | .arg("install")
51 | .arg("--silent")
52 | .current_dir(dir)
53 | .spawn()
54 | .expect("failed to start `npm install`")
55 | .wait()
56 | .unwrap()
57 | .success();
58 | if !ok {
59 | panic!("`npm install` did not exit successfully");
60 | }
61 | }
62 |
63 | #[bench]
64 | fn bench_vlq(b: &mut test::Bencher) {
65 | let mut vlq = Vlq::new();
66 | b.iter(|| {
67 | test::black_box(vlq.enc(-1001));
68 | });
69 | }
70 |
71 | #[bench]
72 | fn bench_cjs_simple(b: &mut test::Bencher) {
73 | let entry_point = Path::new("examples/simple/index.js");
74 | npm_install(entry_point.parent().unwrap());
75 | let input_options = InputOptions::default();
76 | let output = "/dev/null";
77 | let map_output = SourceMapOutput::Inline;
78 |
79 | b.iter(|| {
80 | let _ = bundle(&entry_point, input_options, &output, &map_output).unwrap();
81 | });
82 | }
83 |
84 | #[bench]
85 | fn bench_es6_simple(b: &mut test::Bencher) {
86 | let entry_point = Path::new("examples/es6-simple/index.mjs");
87 | npm_install(entry_point.parent().unwrap());
88 | let input_options = InputOptions {
89 | es6_syntax: true,
90 | ..InputOptions::default()
91 | };
92 | let output = "/dev/null";
93 | let map_output = SourceMapOutput::Inline;
94 |
95 | b.iter(|| {
96 | let _ = bundle(&entry_point, input_options, &output, &map_output).unwrap();
97 | });
98 | }
99 |
100 | #[bench]
101 | fn bench_es6_everywhere_simple(b: &mut test::Bencher) {
102 | let entry_point = Path::new("examples/es6-everywhere-simple/index.js");
103 | npm_install(entry_point.parent().unwrap());
104 | let input_options = InputOptions {
105 | es6_syntax: true,
106 | es6_syntax_everywhere: true,
107 | ..InputOptions::default()
108 | };
109 | let output = "/dev/null";
110 | let map_output = SourceMapOutput::Inline;
111 |
112 | b.iter(|| {
113 | let _ = bundle(&entry_point, input_options, &output, &map_output).unwrap();
114 | });
115 | }
116 |
117 | #[bench]
118 | fn bench_write_map_to(b: &mut test::Bencher) {
119 | let writer = Writer {
120 | modules: {
121 | let mut modules = FnvHashMap::default();
122 | for i in 0..1000 {
123 | let mut path = PathBuf::new();
124 | path.push(i.to_string());
125 | path.push("examples/es6-everywhere-simple/node_modules/itt/index.js");
126 | modules.insert(
127 | path,
128 | Module {
129 | source: Source {
130 | prefix: "~function() {".to_owned(),
131 | body: include_str!("itt.js").to_owned(),
132 | suffix: "}()".to_owned(),
133 | original: None,
134 | },
135 | deps: {
136 | let mut deps = FnvHashMap::new();
137 | deps.insert("./math".to_owned(), Resolved::Normal(
138 | Path::new("examples/es6-everywhere-simple/math.js").to_owned(),
139 | ));
140 | deps.insert("itt".to_owned(), Resolved::Normal(
141 | Path::new("examples/es6-everywhere-simple/node_modules/itt/index.js").to_owned(),
142 | ));
143 | deps
144 | },
145 | },
146 | );
147 | }
148 | modules
149 | },
150 | entry_point: Path::new("examples/es6-everywhere-simple/index.js"),
151 | map_output: &SourceMapOutput::Inline,
152 | };
153 |
154 | let mut out = Vec::new();
155 | b.iter(|| {
156 | out.clear();
157 | writer.write_map_to(&mut out).unwrap();
158 | });
159 | b.bytes = out.len() as u64;
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pax
2 |
3 | The [fastest](#is-it-fast) JavaScript bundler in the galaxy. Fully supports ECMAScript module syntax (`import`/`export`) in addition to CommonJS `require()`.
4 |
5 | - [Why do I need it?](#why-do-i-need-it)
6 | - [How do I get it?](#how-do-i-get-it)
7 | - [How do I use it?](#how-do-i-use-it)
8 | - [Does it do source maps?](#does-it-do-source-maps)
9 | - [Modules?](#modules)
10 | - [What are the options?](#what-are-the-options)
11 | - [Is it fast?](#is-it-fast)
12 |
13 | # Why do I need it?
14 |
15 | Because your bundler is **too slow**.
16 |
17 | You know the feeling. You make that tweak, hit ⌘S ⌘Tab ⌘R, and… **nothing changes**. You get the old version. You beat the bundler. You wait a few seconds, hit ⌘R again, and your changes finally show up. But it’s too late—**you’ve lost momentum.** It’s the wrong shade of pink. You spelled “menu” with a z. The bug still happens sometimes.
18 |
19 | Rinse. Repeat. Ten cycles later, things are looking good. It’s time to `git commit`. But you spent **more time waiting than working**. And it’s your bundler’s fault.
20 |
21 | Pax is a bundler. But you’ll never beat it. Why?
22 |
23 | - It’s parallelized. It makes the most of your cores.
24 | - It’s minimal. It isn’t bogged down by features you don’t need.
25 | - It knows exactly enough about JavaScript to handle dependency resolution. It doesn’t even bother parsing most of your source code.
26 |
27 | Don’t waste time waiting for your bundler to do its thing. Use Pax while you’re developing, and **iterate to your heart’s content**. Use your super-cool, magical, slow-as-molasses bundler for releases, when you don’t care how long it takes to run.
28 |
29 | # How do I get it?
30 |
31 | ```sh
32 | > cargo install pax
33 | ```
34 |
35 | If you don’t have `cargo`, install it with [https://rustup.rs](https://rustup.rs/).
36 |
37 | # How do I use it?
38 |
39 | ```js
40 | // index.js:
41 | const itt = require('itt')
42 | const math = require('./math')
43 | console.log(itt.range(10).map(math.square).join(' '))
44 |
45 | // math.js:
46 | exports.square = x => x * x
47 | ```
48 |
49 | ```sh
50 | > px index.js bundle.js
51 | ```
52 |
53 | Slap on a `