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,6 +46,65 @@ type SourceMap = {
mappings: string; mappings: string;
}; };
function get_css_from_modules(modules: string[], css_map: Map<string, string>, dirs: Dirs) {
const parts: string[] = [];
const mappings: number[][][] = [];
const combined_map: SourceMap = {
version: 3,
file: null,
sources: [],
sourcesContent: [],
names: [],
mappings: null
};
modules.forEach(module => {
if (!/\.css$/.test(module)) return;
const css = css_map.get(module);
const { code, map } = extract_sourcemap(css, module);
parts.push(code);
if (map) {
const lines = codec.decode(map.mappings);
if (combined_map.sources.length > 0 || combined_map.names.length > 0) {
lines.forEach(line => {
line.forEach(segment => {
// adjust source index
segment[1] += combined_map.sources.length;
// adjust name index
if (segment[4]) segment[4] += combined_map.names.length;
});
});
}
combined_map.sources.push(...map.sources);
combined_map.sourcesContent.push(...map.sourcesContent);
combined_map.names.push(...map.names);
mappings.push(...lines);
}
});
if (parts.length > 0) {
combined_map.mappings = codec.encode(mappings);
combined_map.sources = combined_map.sources.map(source => path.relative(`${dirs.dest}/client`, source));
return {
code: parts.join('\n'),
map: combined_map
};
}
return null;
}
export default function extract_css(client_result: CompileResult, components: PageComponent[], dirs: Dirs) { export default function extract_css(client_result: CompileResult, components: PageComponent[], dirs: Dirs) {
const result: { const result: {
main: string | null; main: string | null;
@@ -57,151 +116,94 @@ export default function extract_css(client_result: CompileResult, components: Pa
if (!client_result.css_files) return; // Rollup-only for now if (!client_result.css_files) return; // Rollup-only for now
const unaccounted_for = new Set(); let asset_dir = `${dirs.dest}/client`;
if (process.env.SAPPER_LEGACY_BUILD) asset_dir += '/legacy';
const css_map = new Map(); const unclaimed = new Set(client_result.css_files.map(x => x.id));
client_result.css_files.forEach(css => {
unaccounted_for.add(css.id); const lookup = new Map();
css_map.set(css.id, css.code); client_result.chunks.forEach(chunk => {
lookup.set(chunk.file, chunk);
}); });
const chunk_map = new Map(); const css_map = new Map();
client_result.chunks.forEach(chunk => { client_result.css_files.forEach(css_module => {
chunk_map.set(chunk.file, chunk); css_map.set(css_module.id, css_module.code);
}); });
const chunks_with_css = new Set(); const chunks_with_css = new Set();
// figure out which chunks belong to which components... // concatenate and emit CSS
const component_owners = new Map();
client_result.chunks.forEach(chunk => { client_result.chunks.forEach(chunk => {
chunk.modules.forEach(module => { const css_modules = chunk.modules.filter(m => css_map.has(m));
const component = posixify(path.relative(dirs.routes, module)); if (!css_modules.length) return;
component_owners.set(component, chunk);
}); const css = get_css_from_modules(css_modules, css_map, dirs);
const { code, map } = css;
const output_file_name = chunk.file.replace(/\.js$/, '.css');
map.file = output_file_name;
map.sources = map.sources.map(source => path.relative(`${asset_dir}`, source));
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, ' '));
chunks_with_css.add(chunk);
}); });
const chunks_depended_upon_by_component = new Map(); const entry = path.resolve(dirs.src, 'client.js');
const entry_chunk = client_result.chunks.find(chunk => chunk.modules.indexOf(entry) !== -1);
// ...so we can figure out which chunks don't belong 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);
}
});
}
});
// figure out which (css-having) chunks each component depends on
components.forEach(component => { components.forEach(component => {
const chunk = component_owners.get(component.file); const resolved = path.resolve(dirs.routes, component.file);
const chunk: Chunk = client_result.chunks.find(chunk => chunk.modules.indexOf(resolved) !== -1);
if (!chunk) { if (!chunk) {
// this should never happen! // this should never happen!
throw new Error(`Could not find chunk that owns ${component.file}`); throw new Error(`Could not find chunk that owns ${component.file}`);
} }
const chunks = new Set([chunk]); const chunk_dependencies: Set<Chunk> = new Set([chunk]);
chunks.forEach(chunk => { const css_dependencies: string[] = [];
chunk.imports.forEach((file: string) => {
const chunk = chunk_map.get(file); // recursively find the chunks this component depends on
if (chunk) chunks.add(chunk); chunk_dependencies.forEach(chunk => {
chunk.imports.forEach(file => {
chunk_dependencies.add(lookup.get(file));
}); });
});
chunks.forEach(chunk => { if (chunks_with_css.has(chunk)) {
chunk.modules.forEach((module: string) => { css_dependencies.push(chunk.file.replace(/\.js$/, '.css'));
unaccounted_for.delete(module);
});
});
chunks_depended_upon_by_component.set( chunk.modules.forEach(file => {
component, unclaimed.delete(file);
chunks });
);
});
function get_css_from_modules(modules: string[]) {
const parts: string[] = [];
const mappings: number[][][] = [];
const combined_map: SourceMap = {
version: 3,
file: null,
sources: [],
sourcesContent: [],
names: [],
mappings: null
};
modules.forEach(module => {
if (!/\.css$/.test(module)) return;
const css = css_map.get(module);
const { code, map } = extract_sourcemap(css, module);
parts.push(code);
if (map) {
const lines = codec.decode(map.mappings);
if (combined_map.sources.length > 0 || combined_map.names.length > 0) {
lines.forEach(line => {
line.forEach(segment => {
// adjust source index
segment[1] += combined_map.sources.length;
// adjust name index
if (segment[4]) segment[4] += combined_map.names.length;
});
});
}
combined_map.sources.push(...map.sources);
combined_map.sourcesContent.push(...map.sourcesContent);
combined_map.names.push(...map.names);
mappings.push(...lines);
} }
}); });
if (parts.length > 0) { result.chunks[component.file] = css_dependencies;
combined_map.mappings = codec.encode(mappings);
combined_map.sources = combined_map.sources.map(source => path.relative(`${dirs.dest}/client`, source));
return {
code: parts.join('\n'),
map: combined_map
};
}
return null;
}
let asset_dir = `${dirs.dest}/client`;
if (process.env.SAPPER_LEGACY_BUILD) asset_dir += '/legacy';
const replacements = new Map();
chunks_depended_upon_by_component.forEach((chunks, component) => {
const chunks_with_css = Array.from(chunks).filter(chunk => {
const css = get_css_from_modules(chunk.modules);
if (css) {
const { code, map } = css;
const output_file_name = chunk.file.replace(/\.js$/, '.css');
map.file = output_file_name;
map.sources = map.sources.map(source => path.relative(`${asset_dir}`, source));
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, ' '));
return true;
}
});
const files = chunks_with_css.map(chunk => chunk.file.replace(/\.js$/, '.css'));
replacements.set(
component.file,
files
);
result.chunks[component.file] = files;
}); });
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;