Skip to content

Commit

Permalink
Merge branch 'master' into 5.0
Browse files Browse the repository at this point in the history
* master:
  revert fix Kunstmaan#1562 with new solution
  • Loading branch information
janb87 committed Aug 2, 2017
1 parent f9bf066 commit 30224d0
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 97 deletions.
93 changes: 0 additions & 93 deletions groundcontrol/admin-bundle.tasks.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
/* eslint-env node */

import gulp from 'gulp';
import critical from 'critical';
import http from 'http';
import fs from 'fs';
import url from 'url';
import path from 'path';

import consoleArguments from './console-arguments';

Expand Down Expand Up @@ -61,91 +56,3 @@ adminBundle.tasks.scripts = createScriptsTask({
dest: adminBundle.config.distPath + 'js',
filename: 'admin-bundle.min.js'
});

const extractCriticalCss = (item, done) => {
const basePath = item.basePath;
const distPath = item.distPath;
const urlToCheck = item.url;
const CSS_REGEX = /(<link\s+rel=\"stylesheet\"\s+href=\").*(css\/.*\.css.*\"\s?(>|\/>))/gmi;

http.get(urlToCheck, res => {
const data = [];
res.on('data', function (chunk) {
const originalData = chunk.toString();
const data = originalData.replace(CSS_REGEX, `$1${distPath}$2`);

let cssFiles = [];
let originalCssSize = 0;
// Rewrite the url to the css bundles dist path
const cssTags = data.match(CSS_REGEX);
if (cssTags && cssTags.length > 0) {
cssFiles = cssTags.map(tag => {
const cssPath = tag.match(/.*href=\"(.*)\".*/i)[1];
return url.parse(cssPath).pathname;
});
for (const cssFile of cssFiles) {
const fullPath = basePath + cssFile;
originalCssSize = originalCssSize + fs.statSync(fullPath).size;
// Create a copy so we have the original
const oldFilePath = path.parse(fullPath);
oldFilePath.base = oldFilePath.name + ".original" + oldFilePath.ext;
const newPath = path.format(oldFilePath);
fs.createReadStream(fullPath).pipe(fs.createWriteStream(newPath));
}
}

critical.generate({
inline: false,
base: basePath,
html: data,
dest: 'critical-only.css',
minify: true,
width: 1024,
height: 800
}, (err, output) => {
if (err) {
console.log(err);
done(err);
return;
}

// Write the output to the original css so we can verify the result
fs.writeFileSync(basePath + cssFiles[0], output);

// Verified result, css load time takes 230ms after processing with critical before 4.22s (on 3G good)
// Some issues found:
// 1. Hard to do for secured webpages as we are using a proxy to a php backend (altough if we load the main css in the background on the login page this should be more or less ok as on the second visit the bundle is there)
// 2. Inline option for cricitical css is preferred as it eliminates another http request, this is hard as we need to inject this into the template (twig) somehow

console.log(`Reduced css size for url ${urlToCheck} from ${originalCssSize} to ${output.length}`);
done();
});
});
});

}

adminBundle.tasks.splitCriticalCss = function splitCriticalCss(done) {
// Some issues found:
// 1. Hard to do for secured webpages as we are using a proxy to a php backend (altough if we load the main css in the background on the login page this should be more or less ok as on the second visit the bundle is there)
// 2. Inline option for cricitical css is preferred as it eliminates another http request, this is hard as we need to inject this into the template (twig) somehow
// 3. Loads css files from the file system, doesn't seem to work for dynamic css files

// Verified result on 3G good
const itemsToCheck = [
// css load time takes 230ms after processing with critical before 4.22s (size down to 5KB, from 200kb)
{ url: `${consoleArguments.backendProxy}en/admin/login`, basePath: '.', distPath: adminBundle.config.distPath.substring(1) },
// css load time takes 230ms after processing with critical before 550ms (size down to 3,5KB, from 19kb)
{ url: `${consoleArguments.backendProxy}en`, basePath: '../../', distPath: 'frontendpoc/data/frontendpoc/web/frontend/' }
];

let itemsProcessed = 0;
for (const itemToCheck of itemsToCheck) {
extractCriticalCss(itemToCheck, () => {
itemsProcessed++;
if (itemsProcessed === itemsToCheck.length) {
done();
}
});
}
};
158 changes: 158 additions & 0 deletions groundcontrol/critical-css.task.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { adminBundle } from './admin-bundle.tasks';
import http from 'http';
import critical from 'critical';
import fs from 'fs';
import url from 'url';
import path from 'path';
import consoleArguments from './console-arguments';
import chalk from 'chalk';

// TODO
// 1. Remove example of custom home page, focus on the kunstmaan bundles
// 2. Auto inject critical css into the template (src and proxy application), delay loading of main css (in background)
// 3. Add more urls
// 4. Find a way to do this on "secured" pages

// Some issues found:
// 1. Hard to do for secured webpages as we are using a proxy to a php backend (altough if we load the main css in the background on the login page this should be more or less ok as on the second visit the bundle is there)
// 2. Inline option for cricitical css is preferred as it eliminates another http request, this is hard as we need to inject this into the template (twig) and it kinda pollutes the template somewhat
// 3. Loads css files from the file system, doesn't seem to work for dynamic css files
// 4. We cannot rely on the proxy at build time, should create static html for the build to run on which requires manual updates by the developer

// Verified result on 3G good
const PAGES_TO_OPTIMIZE = [
// css load time takes 230ms after processing with critical before 4.22s (size down to 5KB, from 200kb)
{
url: `${consoleArguments.backendProxy}en/admin/login`,
targetFileName: 'login.html',
basePath: '.',
distPath: adminBundle.config.distPath.substring(1)
},
// css load time takes 230ms after processing with critical before 550ms (size down to 3,5KB, from 19kb)
{
url: `${consoleArguments.backendProxy}en`,
targetFileName: 'home.html',
basePath: '../../',
distPath: 'frontendpoc/data/frontendpoc/web/frontend/'
}
];
const CSS_REGEX = /(<link\s+rel=\"stylesheet\"\s+href=\").*(css\/.*\.css.*\"\s?(>|\/>))/gmi;

const getTargetPath = item => {
const { url: itemUrl, distPath, basePath } = item;
return basePath + distPath + 'generated-static-html/' + item.targetFileName;
};

// Create target dir if it doesn't exist
const createTargetDir = (targetDir, done) => {
fs.stat(targetDir, (err, stats) => {
if (err || !stats.isDirectory()) {
fs.mkdir(targetDir, done);
return;
}
done();
});
};

const writeStaticHtml = (item, done) => {
const { url: itemUrl, distPath } = item;
http.get(itemUrl, res => {
res.on('data', chunk => {
const originalData = chunk.toString();
const data = originalData.replace(CSS_REGEX, `$1${distPath}$2`);
const targetPath = getTargetPath(item);
const targetDir = path.dirname(targetPath);
createTargetDir(targetDir, err => {
if (err) {
done(err);
return;
}
fs.writeFile(getTargetPath(item), data, done);
});
});
});
};

const extractCriticalCss = (item, done) => {
const basePath = item.basePath;
const distPath = item.distPath;
const urlToCheck = item.url;

const staticHtmlPath = getTargetPath(item);
fs.readFile(staticHtmlPath, (err, data) => {
if (err) {
done(err);
return;
}

let cssFiles = [];
let originalCssSize = 0;
// Rewrite the url to the css bundles dist path
const cssTags = data.toString().match(CSS_REGEX);
if (cssTags && cssTags.length > 0) {
cssFiles = cssTags.map(tag => {
const cssPath = tag.match(/.*href=\"(.*)\".*/i)[1];
return url.parse(cssPath).pathname;
});
for (const cssFile of cssFiles) {
const fullPath = basePath + cssFile;
originalCssSize = originalCssSize + fs.statSync(fullPath).size;
}
}

critical.generate({
inline: false,
base: basePath,
dest: 'styles-critical.css',
html: data,
minify: true,
width: 1024,
height: 800
}, (err, output) => {
if (err) {
console.log(err);
done(err);
return;
}

console.log(`Reduced css size for url ${urlToCheck} from ${originalCssSize} to ${output.length}`);
done();
});
});
};

export function generateStaticHtml(done) {
let itemsProcessed = 0;
let hasError = false;
for (const itemToCheck of PAGES_TO_OPTIMIZE) {
console.log(chalk.blue('Generating static html for '), chalk.yellow(itemToCheck.url));
writeStaticHtml(itemToCheck, err => {
itemsProcessed++;
if (err) {
console.log(chalk.red(err.message));
hasError = true;
}
if (itemsProcessed === PAGES_TO_OPTIMIZE.length) {
done(hasError ? new Error('Generating static html failed') : undefined);
}
});
}
};

export function splitCriticalCss(done) {
let itemsProcessed = 0;
let hasError = false;
for (const itemToCheck of PAGES_TO_OPTIMIZE) {
console.log(chalk.blue('Generating critical css for '), chalk.yellow(itemToCheck.url));
extractCriticalCss(itemToCheck, err => {
itemsProcessed++;
if (err) {
console.log(chalk.red(err.message));
hasError = true;
}
if (itemsProcessed === PAGES_TO_OPTIMIZE.length) {
done(hasError ? new Error('Generating critical css failed') : undefined);
}
});
}
};
9 changes: 5 additions & 4 deletions gulpfile.babel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { mediaBundle } from './groundcontrol/media-bundle.tasks';
import { translatorBundle } from './groundcontrol/translator-bundle.tasks';
import startLocalTask, { buildOnChange } from './groundcontrol/start-local.task';
import createBuildGroundControlSkeletonTask from './groundcontrol/tasks/build-gc-skeleton';

import { generateStaticHtml, splitCriticalCss } from './groundcontrol/critical-css.task';

// AdminBundle Tasks
const analyzeAdminBundle = gulp.series(
Expand Down Expand Up @@ -115,11 +115,12 @@ const startLocal = gulp.series(
const buildGroundControlSkeleton = gulp.series(createBuildGroundControlSkeletonTask('./src/Kunstmaan/GeneratorBundle/Resources/SensioGeneratorBundle/skeleton/layout/groundcontrol'));

// POC critical css only
const splitCriticalCssMain = gulp.series(
const createCriticalCss = gulp.series(
buildOptimized,
adminBundle.tasks.splitCriticalCss,
generateStaticHtml,
splitCriticalCss,
startLocalTask
);

// Export public tasks
export { test, buildOptimized, testAndBuildOptimized, startLocal, buildGroundControlSkeleton, splitCriticalCssMain };
export { test, buildOptimized, testAndBuildOptimized, startLocal, buildGroundControlSkeleton, createCriticalCss };

0 comments on commit 30224d0

Please sign in to comment.