Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Process inline styles and scripts #1456

Merged
merged 15 commits into from Jul 21, 2018
628 changes: 314 additions & 314 deletions CHANGELOG.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions README.md
Expand Up @@ -25,7 +25,7 @@

## Getting started

1. Install with yarn:
1. Install with yarn:

```shell
yarn global add parcel-bundler
Expand All @@ -37,7 +37,7 @@ or with npm:
npm install -g parcel-bundler
```

2. Parcel can take any type of file as an entry point, but an HTML or JavaScript file is a good place to start. If you link your main JavaScript file in the HTML using a relative path, Parcel will also process it for you, and replace the reference with a URL to the output file.
2. Parcel can take any type of file as an entry point, but an HTML or JavaScript file is a good place to start. If you link your main JavaScript file in the HTML using a relative path, Parcel will also process it for you, and replace the reference with a URL to the output file.

```html
<html>
Expand All @@ -47,13 +47,13 @@ npm install -g parcel-bundler
</html>
```

3. Parcel has a development server built in, which will automatically rebuild your app as you change files and supports hot module replacement for fast development. Just point it at your entry file:
3. Parcel has a development server built in, which will automatically rebuild your app as you change files and supports hot module replacement for fast development. Just point it at your entry file:

```shell
parcel index.html
```

4. Now open http://localhost:1234/ in your browser. If needed, you can also override the default port with the `-p` option. Add `--open` to automatically open a browser.
4. Now open http://localhost:1234/ in your browser. If needed, you can also override the default port with the `-p` option. Add `--open` to automatically open a browser.

See [parceljs.org](https://parceljs.org) for more documentation!

Expand Down
21 changes: 8 additions & 13 deletions package.json
Expand Up @@ -8,12 +8,7 @@
"type": "git",
"url": "https://github.com/parcel-bundler/parcel.git"
},
"files": [
"bin/",
"lib/",
"src/",
"index.js"
],
"files": ["bin/", "lib/", "src/", "index.js"],
"dependencies": {
"ansi-to-html": "^0.6.4",
"babel-code-frame": "^6.26.0",
Expand Down Expand Up @@ -114,10 +109,13 @@
"format": "prettier --write \"./{src,bin,test}/**/*.{js,json,md}\"",
"build": "yarn minify && babel src -d lib && ncp src/builtins lib/builtins",
"prepublish": "yarn build",
"minify": "terser -c -m -o src/builtins/prelude.min.js src/builtins/prelude.js && terser -c -m -o src/builtins/prelude2.min.js src/builtins/prelude2.js",
"minify":
"terser -c -m -o src/builtins/prelude.min.js src/builtins/prelude.js && terser -c -m -o src/builtins/prelude2.min.js src/builtins/prelude2.js",
"precommit": "npm run lint && lint-staged",
"lint": "eslint . && prettier \"./{src,bin,test}/**/*.{js,json,md}\" --list-different",
"postinstall": "node -e \"console.log('\\u001b[35m\\u001b[1mLove Parcel? You can now donate to our open collective:\\u001b[22m\\u001b[39m\\n > \\u001b[34mhttps://opencollective.com/parcel/donate\\u001b[0m')\""
"lint":
"eslint . && prettier \"./{src,bin,test}/**/*.{js,json,md}\" --list-different",
"postinstall":
"node -e \"console.log('\\u001b[35m\\u001b[1mLove Parcel? You can now donate to our open collective:\\u001b[22m\\u001b[39m\\n > \\u001b[34mhttps://opencollective.com/parcel/donate\\u001b[0m')\""
},
"bin": {
"parcel": "bin/cli.js"
Expand All @@ -126,10 +124,7 @@
"node": ">= 6.0.0"
},
"lint-staged": {
"*.{js,json,md}": [
"prettier --write",
"git add"
]
"*.{js,json,md}": ["prettier --write", "git add"]
},
"collective": {
"type": "opencollective",
Expand Down
98 changes: 96 additions & 2 deletions src/assets/HTMLAsset.js
Expand Up @@ -61,6 +61,11 @@ const META = {
]
};

const SCRIPT_TYPES = {
'application/javascript': 'js',
'application/json': 'json'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parcel compiles json to javascript currently, so this might actually break things. haven't tested.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, and it does mess up the inlining, might have to look into that

};

// Options to be passed to `addURLDependency` for certain tags + attributes
const OPTIONS = {
a: {
Expand Down Expand Up @@ -172,8 +177,97 @@ class HTMLAsset extends Asset {
}
}

generate() {
return this.isAstDirty ? render(this.ast) : this.contents;
async generate() {
let parts = [];
this.ast.walk(node => {
if (node.tag === 'script' || node.tag === 'style') {
if (node.content) {
let value = node.content.join('').trim();
if (value) {
let type;

if (node.tag === 'style') {
if (node.attrs && node.attrs.type) {
type = node.attrs.type.replace('text/', '');
} else {
type = 'css';
}
} else {
if (node.attrs && node.attrs.type) {
if (SCRIPT_TYPES[node.attrs.type]) {
type = SCRIPT_TYPES[node.attrs.type];
} else {
type = node.attrs.type.replace('application/', '');
}
} else {
type = 'js';
}
}

parts.push({type, value});

if (!node.attrs) {
node.attrs = {};
}

if (node.tag === 'style') {
node.attrs.type = 'text/css';
} else if (node.attrs && !SCRIPT_TYPES[node.attrs.type]) {
node.attrs.type = 'application/javascript';
}
}
}
}

if (node.attrs && node.attrs.style) {
parts.push({
type: 'css',
value: node.attrs.style
});
}

return node;
});

return parts;
}

async postProcess(generated) {
// Hacky way of filtering out CSS HMR & sourcemaps
// Related: https://github.com/parcel-bundler/parcel/issues/1389
generated = generated.filter(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line might cause issues, related to this issue: #1389
There is currently no way to tell in generated you don't want the hmr code for css assets.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this won't work if HMR is actually enabled - the generated code won't be empty in that case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also probably need to filter out source maps or they'll mess up the indexing below

generatedAsset =>
!(
(generatedAsset.type === 'js' && generatedAsset.value === '') ||
generatedAsset.type === 'map' ||
generatedAsset.value.includes('_css_loader')
)
);

// Update processed inlined assets
let index = 0;
this.ast.walk(node => {
if (node.tag === 'script' || node.tag === 'style') {
if (node.content && node.content.join('').trim()) {
node.content = generated[index].value;
index++;
}
}

if (node.attrs && node.attrs.style) {
node.attrs.style = generated[index].value;
index++;
}

return node;
});

return [
{
type: 'html',
value: render(this.ast)
}
];
}
}

Expand Down
13 changes: 10 additions & 3 deletions src/transforms/htmlnano.js
Expand Up @@ -4,10 +4,17 @@ const htmlnano = require('htmlnano');
module.exports = async function(asset) {
await asset.parseIfNeeded();

let htmlNanoConfig = await asset.getConfig(
['.htmlnanorc', '.htmlnanorc.js'],
{packageKey: 'htmlnano'}
let htmlNanoConfig = Object.assign(
{},
await asset.getConfig(['.htmlnanorc', '.htmlnanorc.js'], {
packageKey: 'htmlnano'
}),
{
minifyCss: false,
minifyJs: false
}
);

let res = await posthtml([htmlnano(htmlNanoConfig)]).process(asset.ast, {
skipParse: true
});
Expand Down
77 changes: 71 additions & 6 deletions test/html.js
Expand Up @@ -283,16 +283,16 @@ describe('html', function() {

let html = await fs.readFile(__dirname + '/dist/index.html', 'utf8');

// mergeStyles
// minifyJson
assert(
html.includes(
'<style>h1{color:red}div{font-size:20px}</style><style media="print">div{color:blue}</style>'
)
html.includes('<script type="application/json">{"user":"me"}</script>')
);

// minifyJson
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because htmlnano is no longer responsible for JSON minifying, although this caused the tests to not show that it actually changed json into JS so I've put it back

// mergeStyles
assert(
html.includes('<script type="application/json">{"user":"me"}</script>')
html.includes(
'<style>h1{color:red}div{font-size:20px}</style><style media="print">div{color:#00f}</style>'
)
);

// minifySvg is false
Expand Down Expand Up @@ -589,4 +589,69 @@ describe('html', function() {
]
});
});

it('should process inline JS', async function() {
let b = await bundle(__dirname + '/integration/html-inline-js/index.html', {
production: true
});

const bundleContent = (await fs.readFile(b.name)).toString();
assert(!bundleContent.includes('someArgument'));
});

it('should process inline styles', async function() {
let b = await bundle(
__dirname + '/integration/html-inline-styles/index.html',
{production: true}
);

await assertBundleTree(b, {
name: 'index.html',
assets: ['index.html'],
childBundles: [
{
type: 'jpg',
assets: ['bg.jpg'],
childBundles: []
},
{
type: 'jpg',
assets: ['img.jpg'],
childBundles: []
}
]
});
});

it('should process inline styles using lang', async function() {
let b = await bundle(
__dirname + '/integration/html-inline-sass/index.html',
{production: true}
);

await assertBundleTree(b, {
name: 'index.html',
assets: ['index.html']
});

let html = await fs.readFile(__dirname + '/dist/index.html', 'utf8');

assert(html.includes('<style type="text/css">.index{color:#00f}</style>'));
});

it.only('should process inline non-js scripts', async function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a heads up, you've left an only call here

let b = await bundle(
__dirname + '/integration/html-inline-coffeescript/index.html',
{production: true}
);

await assertBundleTree(b, {
name: 'index.html',
assets: ['index.html']
});

let html = await fs.readFile(__dirname + '/dist/index.html', 'utf8');

assert(html.includes('alert("Hello, World!")'));
});
});
11 changes: 11 additions & 0 deletions test/integration/html-inline-coffeescript/index.html
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Inline JavaScript Parcel</title>
</head>
<body>
<script type="application/coffee">
alert "Hello, World!"
</script>
</body>
</html>
23 changes: 23 additions & 0 deletions test/integration/html-inline-js/index.html
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Inline JavaScript Parcel</title>
</head>
<body>
<script>
var hello = "Hello";
let getHello = (someArgument) => {
return someArgument;
}
</script>
<script>
var world = "world";
</script>
<script>
var end = "!";
</script>
<script>
console.log(`${hello} ${world}${end}`);
</script>
</body>
</html>
12 changes: 12 additions & 0 deletions test/integration/html-inline-sass/index.html
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Inline JavaScript Parcel</title>
</head>
<body>
<style type="text/sass">
.index
color: blue
</style>
</body>
</html>
1 change: 1 addition & 0 deletions test/integration/html-inline-styles/bg.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/integration/html-inline-styles/img.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions test/integration/html-inline-styles/index.html
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Inline JavaScript Parcel</title>
</head>
<body>
<style>
.someClass {
background: blue;
color: white;
}
</style>

<div class="someClass">
<h1 style="color: green;"></h1>
<p style="background: url(./img.jpg);"></p>
<p style="background-image: url(./bg.jpg);"></p>
</div>
</body>
</html>