Compare commits

...

5 Commits

Author SHA1 Message Date
Rich Harris
cb45bb0fbe -> v0.23.2 2018-10-16 08:58:02 -04:00
Rich Harris
f39455014a update deps 2018-10-16 08:44:22 -04:00
Rich Harris
4fe8df3696 Merge pull request #471 from sveltejs/missing-css
include css depended upon by entry point, even if also depended on by a lazily-loaded component
2018-10-16 08:38:20 -04:00
Rich Harris
44736754ad fix file extension 2018-10-15 21:27:52 -04:00
Rich Harris
1b9b559d82 include css depended upon by entry point, even if also depended on by a lazily-loaded component 2018-10-11 23:18:51 -04:00
4 changed files with 593 additions and 772 deletions

View File

@@ -1,5 +1,9 @@
# sapper changelog # sapper changelog
## 0.23.2
* Fix entry point CSS ([#471](https://github.com/sveltejs/sapper/pull/471))
## 0.23.1 ## 0.23.1
* Scroll to deeplink that matches current URL ([#472](https://github.com/sveltejs/sapper/pull/472)) * Scroll to deeplink that matches current URL ([#472](https://github.com/sveltejs/sapper/pull/472))

1059
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "sapper", "name": "sapper",
"version": "0.23.1", "version": "0.23.2",
"description": "Military-grade apps, engineered by Svelte", "description": "Military-grade apps, engineered by Svelte",
"bin": { "bin": {
"sapper": "./sapper" "sapper": "./sapper"
@@ -17,53 +17,53 @@
"test": "test" "test": "test"
}, },
"dependencies": { "dependencies": {
"html-minifier": "^3.5.16", "html-minifier": "^3.5.20",
"shimport": "0.0.11", "shimport": "0.0.11",
"source-map-support": "^0.5.6", "source-map-support": "^0.5.9",
"sourcemap-codec": "^1.4.1", "sourcemap-codec": "^1.4.3",
"string-hash": "^1.1.3", "string-hash": "^1.1.3",
"tslib": "^1.9.1" "tslib": "^1.9.3"
}, },
"devDependencies": { "devDependencies": {
"@types/mkdirp": "^0.5.2", "@types/mkdirp": "^0.5.2",
"@types/mocha": "^5.2.5", "@types/mocha": "^5.2.5",
"@types/node": "^10.7.1", "@types/node": "^10.12.0",
"@types/puppeteer": "^1.9.0", "@types/puppeteer": "^1.9.0",
"@types/rimraf": "^2.0.2", "@types/rimraf": "^2.0.2",
"agadoo": "^1.0.1", "agadoo": "^1.0.1",
"cheap-watch": "^0.3.0", "cheap-watch": "^1.0.0",
"cookie": "^0.3.1", "cookie": "^0.3.1",
"devalue": "^1.0.4", "devalue": "^1.0.4",
"eslint": "^4.13.1", "eslint": "^5.7.0",
"eslint-plugin-import": "^2.12.0", "eslint-plugin-import": "^2.14.0",
"kleur": "^2.0.1", "kleur": "^2.0.2",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"node-fetch": "^2.1.1", "node-fetch": "^2.2.0",
"npm-run-all": "^4.1.3", "npm-run-all": "^4.1.3",
"polka": "^0.4.0", "polka": "^0.5.1",
"port-authority": "^1.0.5", "port-authority": "^1.0.5",
"pretty-bytes": "^5.0.0", "pretty-bytes": "^5.1.0",
"puppeteer": "^1.9.0", "puppeteer": "^1.9.0",
"require-relative": "^0.8.7", "require-relative": "^0.8.7",
"rimraf": "^2.6.2", "rimraf": "^2.6.2",
"rollup": "^0.65.0", "rollup": "^0.66.6",
"rollup-plugin-commonjs": "^9.1.3", "rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-json": "^3.0.0", "rollup-plugin-json": "^3.1.0",
"rollup-plugin-node-resolve": "^3.4.0", "rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-replace": "^2.0.0", "rollup-plugin-replace": "^2.1.0",
"rollup-plugin-string": "^2.0.2", "rollup-plugin-string": "^2.0.2",
"rollup-plugin-svelte": "^4.3.2", "rollup-plugin-svelte": "^4.3.2",
"rollup-plugin-typescript": "^0.8.1", "rollup-plugin-typescript": "^1.0.0",
"sade": "^1.4.1", "sade": "^1.4.1",
"sander": "^0.6.0", "sander": "^0.6.0",
"sirv": "^0.2.2", "sirv": "^0.2.2",
"svelte": "^2.6.3", "svelte": "^2.13.5",
"svelte-loader": "^2.9.0", "svelte-loader": "^2.11.0",
"ts-node": "^7.0.1", "ts-node": "^7.0.1",
"typescript": "^2.8.3", "typescript": "^3.1.3",
"webpack": "^4.8.3", "webpack": "^4.20.2",
"webpack-format-messages": "^2.0.1" "webpack-format-messages": "^2.0.3"
}, },
"scripts": { "scripts": {
"test": "mocha --opts mocha.opts", "test": "mocha --opts mocha.opts",

View File

@@ -3,7 +3,7 @@ import * as path from 'path';
import hash from 'string-hash'; import hash from 'string-hash';
import * as codec from 'sourcemap-codec'; import * as codec from 'sourcemap-codec';
import { PageComponent, Dirs } from '../../interfaces'; import { PageComponent, Dirs } from '../../interfaces';
import { CompileResult } from './interfaces'; import { CompileResult, Chunk } from './interfaces';
import { posixify } from '../../utils' import { posixify } from '../../utils'
const inline_sourcemap_header = 'data:application/json;charset=utf-8;base64,'; const inline_sourcemap_header = 'data:application/json;charset=utf-8;base64,';
@@ -46,72 +46,7 @@ type SourceMap = {
mappings: string; mappings: string;
}; };
export default function extract_css(client_result: CompileResult, components: PageComponent[], dirs: Dirs) { function get_css_from_modules(modules: string[], css_map: Map<string, string>, dirs: Dirs) {
const result: {
main: string | null;
chunks: Record<string, string[]>
} = {
main: null,
chunks: {}
};
if (!client_result.css_files) return; // Rollup-only for now
const unaccounted_for = new Set();
const css_map = new Map();
client_result.css_files.forEach(css => {
unaccounted_for.add(css.id);
css_map.set(css.id, css.code);
});
const chunk_map = new Map();
client_result.chunks.forEach(chunk => {
chunk_map.set(chunk.file, chunk);
});
const chunks_with_css = new Set();
// figure out which chunks belong to which components...
const component_owners = new Map();
client_result.chunks.forEach(chunk => {
chunk.modules.forEach(module => {
const component = posixify(path.relative(dirs.routes, module));
component_owners.set(component, chunk);
});
});
const chunks_depended_upon_by_component = new Map();
// ...so we can figure out which chunks don't belong
components.forEach(component => {
const chunk = component_owners.get(component.file);
if (!chunk) {
// this should never happen!
throw new Error(`Could not find chunk that owns ${component.file}`);
}
const chunks = new Set([chunk]);
chunks.forEach(chunk => {
chunk.imports.forEach((file: string) => {
const chunk = chunk_map.get(file);
if (chunk) chunks.add(chunk);
});
});
chunks.forEach(chunk => {
chunk.modules.forEach((module: string) => {
unaccounted_for.delete(module);
});
});
chunks_depended_upon_by_component.set(
component,
chunks
);
});
function get_css_from_modules(modules: string[]) {
const parts: string[] = []; const parts: string[] = [];
const mappings: number[][][] = []; const mappings: number[][][] = [];
@@ -168,18 +103,43 @@ export default function extract_css(client_result: CompileResult, components: Pa
} }
return null; return null;
} }
export default function extract_css(client_result: CompileResult, components: PageComponent[], dirs: Dirs) {
const result: {
main: string | null;
chunks: Record<string, string[]>
} = {
main: null,
chunks: {}
};
if (!client_result.css_files) return; // Rollup-only for now
let asset_dir = `${dirs.dest}/client`; let asset_dir = `${dirs.dest}/client`;
if (process.env.SAPPER_LEGACY_BUILD) asset_dir += '/legacy'; if (process.env.SAPPER_LEGACY_BUILD) asset_dir += '/legacy';
const replacements = new Map(); const unclaimed = new Set(client_result.css_files.map(x => x.id));
chunks_depended_upon_by_component.forEach((chunks, component) => { const lookup = new Map();
const chunks_with_css = Array.from(chunks).filter(chunk => { client_result.chunks.forEach(chunk => {
const css = get_css_from_modules(chunk.modules); lookup.set(chunk.file, chunk);
});
const css_map = new Map();
client_result.css_files.forEach(css_module => {
css_map.set(css_module.id, css_module.code);
});
const chunks_with_css = new Set();
// concatenate and emit CSS
client_result.chunks.forEach(chunk => {
const css_modules = chunk.modules.filter(m => css_map.has(m));
if (!css_modules.length) return;
const css = get_css_from_modules(css_modules, css_map, dirs);
if (css) {
const { code, map } = css; const { code, map } = css;
const output_file_name = chunk.file.replace(/\.js$/, '.css'); const output_file_name = chunk.file.replace(/\.js$/, '.css');
@@ -190,18 +150,60 @@ export default function extract_css(client_result: CompileResult, components: Pa
fs.writeFileSync(`${asset_dir}/${output_file_name}`, `${code}\n/* sourceMappingURL=./${output_file_name}.map */`); fs.writeFileSync(`${asset_dir}/${output_file_name}`, `${code}\n/* sourceMappingURL=./${output_file_name}.map */`);
fs.writeFileSync(`${asset_dir}/${output_file_name}.map`, JSON.stringify(map, null, ' ')); fs.writeFileSync(`${asset_dir}/${output_file_name}.map`, JSON.stringify(map, null, ' '));
return true; chunks_with_css.add(chunk);
});
const entry = path.resolve(dirs.src, 'client.js');
const entry_chunk = client_result.chunks.find(chunk => chunk.modules.indexOf(entry) !== -1);
const entry_chunk_dependencies: Set<Chunk> = new Set([entry_chunk]);
const entry_css_modules: string[] = [];
// recursively find the chunks this component depends on
entry_chunk_dependencies.forEach(chunk => {
chunk.imports.forEach(file => {
entry_chunk_dependencies.add(lookup.get(file));
});
if (chunks_with_css.has(chunk)) {
chunk.modules.forEach(file => {
unclaimed.delete(file);
if (css_map.has(file)) {
entry_css_modules.push(file);
}
});
} }
}); });
const files = chunks_with_css.map(chunk => chunk.file.replace(/\.js$/, '.css')); // figure out which (css-having) chunks each component depends on
components.forEach(component => {
const resolved = path.resolve(dirs.routes, component.file);
const chunk: Chunk = client_result.chunks.find(chunk => chunk.modules.indexOf(resolved) !== -1);
replacements.set( if (!chunk) {
component.file, // this should never happen!
files throw new Error(`Could not find chunk that owns ${component.file}`);
); }
result.chunks[component.file] = files; const chunk_dependencies: Set<Chunk> = new Set([chunk]);
const css_dependencies: string[] = [];
// recursively find the chunks this component depends on
chunk_dependencies.forEach(chunk => {
chunk.imports.forEach(file => {
chunk_dependencies.add(lookup.get(file));
});
if (chunks_with_css.has(chunk)) {
css_dependencies.push(chunk.file.replace(/\.js$/, '.css'));
chunk.modules.forEach(file => {
unclaimed.delete(file);
});
}
});
result.chunks[component.file] = css_dependencies;
}); });
fs.readdirSync(asset_dir).forEach(file => { fs.readdirSync(asset_dir).forEach(file => {
@@ -210,13 +212,17 @@ export default function extract_css(client_result: CompileResult, components: Pa
const source = fs.readFileSync(`${asset_dir}/${file}`, 'utf-8'); const source = fs.readFileSync(`${asset_dir}/${file}`, 'utf-8');
const replaced = source.replace(/["']__SAPPER_CSS_PLACEHOLDER:(.+?)__["']/g, (m, route) => { const replaced = source.replace(/["']__SAPPER_CSS_PLACEHOLDER:(.+?)__["']/g, (m, route) => {
return JSON.stringify(replacements.get(route)); return JSON.stringify(result.chunks[route]);
}); });
fs.writeFileSync(`${asset_dir}/${file}`, replaced); fs.writeFileSync(`${asset_dir}/${file}`, replaced);
}); });
const leftover = get_css_from_modules(Array.from(unaccounted_for)); unclaimed.forEach(file => {
entry_css_modules.push(css_map.get(file));
});
const leftover = get_css_from_modules(entry_css_modules, css_map, dirs);
if (leftover) { if (leftover) {
const { code, map } = leftover; const { code, map } = leftover;