Skip to content

Commit

Permalink
fix(node): fix icons generator. Improve sw. Improve htmlToHyperscript…
Browse files Browse the repository at this point in the history
… parser

Icons generation was broken a few commits ago, now it's fixed. Improve service worker and
htmlToHyperscript method.
  • Loading branch information
Masquerade-Circus committed Sep 27, 2020
1 parent e1f10eb commit b38627b
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 90 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"warn",
{ "max": 300, "skipBlankLines": true, "skipComments": true }
],
"max-len": ["error", 145],
"max-len": ["error", 165],
"max-statements-per-line": ["error", { "max": 3 }]
},
"env": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"test": "cross-env NODE_ENV=development nyc mocha --timeout 10000 --require @babel/register --require esm \"test/**/*_test.js\"",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"remark": "remark . -o",
"commit": "git-cz",
"commit": "git add . && git-cz",
"release": "release-it --verbose",
"release-test": "release-it --dry-run --verbose"
},
Expand Down
15 changes: 11 additions & 4 deletions plugins/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ function parseDom(childNodes, depth = 1) {

return childNodes
.map((item) => {
if (item.nodeType === 3) {
if (item.nodeType === 10) {
return `\n${spaces}"<!DOCTYPE html>"`;
} else if (item.nodeType === 3) {
return `\n${spaces}"${item.nodeValue}"`;
} else {
let str = `\n${spaces}v("${item.nodeName}", `;
Expand Down Expand Up @@ -228,9 +230,14 @@ function icons(source, configuration = {}) {
}

if (options.linksViewPath) {
let html = 'export default function(){ \n return ';
html += htmlToHyperscript(response.html.join(''));
html += ';\n \n};';
let html = `
function Links(){
return ${htmlToHyperscript(response.html.join(''))};
}
Links.default = Links;
module.exports = Links;
`;

promises.push(
new Promise((resolve, reject) => {
Expand Down
100 changes: 48 additions & 52 deletions plugins/node.sw.tpl.js
Original file line number Diff line number Diff line change
@@ -1,86 +1,82 @@
let Log = console.log;

let config = {
version: 'v1::',
name: 'Valyrian.js',
urls: ['/']
version: "v1::",
name: "Valyrian.js",
urls: ["/"]
};

// Function to add the network response to the cache
let fetchedFromNetwork = (event) => (response) => {
Log('WORKER: fetch response from network.', event.request.url);
if (!response || response.status !== 200 || response.type !== 'basic') {
let cacheName = config.version + config.name;

self.addEventListener("fetch", async (event) => {
// DevTools opening will trigger these o-i-c requests, which this SW can't handle.
// https://github.com/paulirish/caltrainschedule.io/issues/49
if (
event.request.cache === "only-if-cached" &&
event.request.mode !== "same-origin"
) {
return;
}

let cacheCopy = response.clone();
caches
.open(config.version + config.name)
.then((cache) => cache.put(event.request, cacheCopy))
.then(() => Log('WORKER: fetch response stored in cache.', event.request.url))
.catch((err) => Log('WORKER: fetch response could not be stored in cache.', err));
return response;
};

// If the network or the cache response fail, response with Service Unavailable
let unableToResolve = () => {
Log('WORKER: fetch request failed in both cache and network.');
return new Response('<h1>Service Unavailable</h1>', {
status: 503,
statusText: 'Service Unavailable',
headers: new Headers({
'Content-Type': 'text/html'
})
});
};

// Fetch listener
self.addEventListener('fetch', (event) => {
Log('WORKER: fetch event in progress.', event.request.url);
Log("WORKER: fetch event in progress.", event.request.url);

// We only handle Get requests all others let them pass
if (event.request.method !== 'GET') {
if (event.request.method !== "GET") {
return;
}

// TODO: Make a callback available here to filter if this request must be cached or let it pass directly
// This callback must return true or false
Log("WORKER: fetchevent for " + event.request.url);

Log('WORKER: fetchevent for ' + event.request.url);
let response = await fetch(event.request);
if (response && response.status < 300 && response.type === "basic") {
try {
let cache = await caches.open(cacheName);
cache.put(event.request, response);

event.respondWith(
caches.match(event.request).then((cached) => {
Log('WORKER: fetch event', cached ? '(cached)' : '(network)', event.request.url);
Log("WORKER: fetch response stored in cache.", event.request.url);
} catch (err) {
Log("WORKER: fetch response could not be stored in cache.", err);
}

let network = fetch(event.request)
.then(fetchedFromNetwork(event), unableToResolve)
.catch((error) => {
console.log(error);
return caches.match('/');
});
return event.respondWith(response);
}

let cachedResponse = await caches.match(event.request);
if (cachedResponse) {
Log("WORKER: fetch request failed, responding with cache.");
return event.respondWith(cachedResponse);
}

return network || cached;
})
Log(
"WORKER: fetch request failed in both cache and network, responding with service unavailable."
);
return event.respondWith(
response ||
new Response("<h1>Service Unavailable</h1>", {
status: 503,
statusText: "Service Unavailable",
headers: new Headers({
"Content-Type": "text/html"
})
})
);
});

self.addEventListener('install', (event) => {
self.addEventListener("install", (event) => {
event.waitUntil(
caches
.open(config.version + config.name)
.then((cache) => cache.addAll(config.urls))
caches.open(cacheName).then((cache) => cache.addAll(config.urls))
);
});

self.addEventListener('activate', (event) => {
self.addEventListener("activate", (event) => {
event.waitUntil(
caches
.keys()
.then((keys) =>
Promise.all(
keys
// Filter by keys that don't start with the latest version prefix.
.filter((key) => !key.startsWith(config.version))
.filter((key) => !key.startsWith(cacheName))
// Return a promise that's fulfilled when each outdated cache is deleted.
.map((key) => caches.delete(key))
)
Expand Down
36 changes: 20 additions & 16 deletions plugins/utils/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,27 @@ export function parseAttributes(attributes) {
}

export function parseHtml(html, options = {}) {
let result = parse5.parse(html, options);
let childNodes = result.childNodes;

if (/^<!DOCTYPE html>/gm.test(html)) {
childNodes = result.childNodes;
} else if (/^<html(\s|>)/gm.test(html)) {
childNodes = result.childNodes;
} else if (/^<body(\s|>)/gm.test(html)) {
childNodes = result.childNodes[0].childNodes;
childNodes.shift();
} else if (/^<head(\s|>)/gm.test(html)) {
childNodes = result.childNodes[0].childNodes;
childNodes.pop();
} else {
childNodes = result.childNodes[0].childNodes[1].childNodes;
let returnHtml = /^<!DOCTYPE/i.test(html) || /^<html(\s|>)/i.test(html);
let returnBody = /^<body(\s|>)/i.test(html);
let returnHead = /^<head(\s|>)/i.test(html);

if (returnHtml || returnBody || returnHead) {
let result = parse5.parse(html, options);

if (returnHtml) {
return result.childNodes;
}

if (returnHead) {
return [result.childNodes[0].childNodes[0]];
}

if (returnBody) {
return [result.childNodes[0].childNodes[1]];
}
}
return childNodes;

return parse5.parseFragment(html, options).childNodes;
}

function generateDom(tree) {
Expand Down
81 changes: 71 additions & 10 deletions test/node_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,52 @@ describe('Node test', () => {
])
])
]`);

html = '<!DOCTYPE html><html><link rel="shortcult icon" href="/icons/favicon.ico"/>Hello world</html>';

dom = v.htmlToHyperscript(html);

expect(dom).toEqual(`[
"<!DOCTYPE html>",
v("html", {}, [
v("head", {}, [
v("link", {"rel":"shortcult icon","href":"/icons/favicon.ico"}, [])
]),
v("body", {}, [
"Hello world"
])
])
]`);

html = '<head><link rel="shortcult icon" href="/icons/favicon.ico"/>Hello world</head>';

dom = v.htmlToHyperscript(html);

expect(dom).toEqual(`[
v("head", {}, [
v("link", {"rel":"shortcult icon","href":"/icons/favicon.ico"}, [])
])
]`);

html = '<div><link rel="shortcult icon" href="/icons/favicon.ico"/>Hello world</div>';

dom = v.htmlToHyperscript(html);

expect(dom).toEqual(`[
v("div", {}, [
v("link", {"rel":"shortcult icon","href":"/icons/favicon.ico"}, []),
"Hello world"
])
]`);

html = '<link rel="shortcult icon" href="/icons/favicon.ico"/>Hello world';

dom = v.htmlToHyperscript(html);

expect(dom).toEqual(`[
v("link", {"rel":"shortcult icon","href":"/icons/favicon.ico"}, []),
"Hello world"
]`);
});

it('Should create a service worker file', async () => {
Expand All @@ -55,27 +101,42 @@ describe('Node test', () => {
// favicons options
path: '/icons/', // Path for overriding default icons path. `string`
appName: packageJson.name, // Your application's name. `string`
appShortName: packageJson.name, // Your application's short_name. `string`. Optional. If not set, appName will be used. `string`
appDescription: packageJson.description, // Your application's description. `string`
developerName: 'Christian César Robledo López (Masquerade Circus)', // Your (or your developer's) name. `string`
developerURL: 'http://masquerade-circus.net',
dir: 'auto',
lang: 'en-US',
background: '#fff', // Background colour for flattened icons. `string`
theme_color: '#fff',
theme_color: '#fff', // Theme color user for example in Android's task switcher. `string`
appleStatusBarStyle: "black-translucent", // Style for Apple status bar: "black-translucent", "default", "black". `string`
display: 'standalone', // Android display: "browser" or "standalone". `string`
orientation: 'any', // Android orientation: "any" "portrait" or "landscape". `string`
start_url: '/', // Android start application's URL. `string`
scope: '/', // set of URLs that the browser considers within your app
start_url: '/', // Start URL when launching the application from a device. `string`
version: packageJson.version, // Your application's version number. `number`
logging: false, // Print logs to console? `boolean`
pixel_art: false, // Keeps pixels "sharp" when scaling up, for pixel art. Only supported in offline mode.
loadManifestWithCredentials: false, // Browsers don't send cookies when fetching a manifest, enable this to fix that. `boolean`
icons: {
android: true, // Create Android homescreen icon. `boolean`
appleIcon: false, // Create Apple touch icons. `boolean` or `{ offset: offsetInPercentage }`
appleStartup: false, // Create Apple startup images. `boolean`
coast: false, // Create Opera Coast icon with offset 25%. `boolean` or `{ offset: offsetInPercentage }`
favicons: true, // Create regular favicons. `boolean`
firefox: false, // Create Firefox OS icons. `boolean` or `{ offset: offsetInPercentage }`
windows: false, // Create Windows 8 tile icons. `boolean`
yandex: false // Create Yandex browser icon. `boolean`
// Platform Options:
// - offset - offset in percentage
// - background:
// * false - use default
// * true - force use default, e.g. set background for Android icons
// * color - set background for the specified icons
// * mask - apply mask in order to create circle icon (applied by default for firefox). `boolean`
// * overlayGlow - apply glow effect after mask has been applied (applied by default for firefox). `boolean`
// * overlayShadow - apply drop shadow after mask has been applied .`boolean`
//
android: true, // Create Android homescreen icon. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }` or an array of sources
appleIcon: false, // Create Apple touch icons. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }` or an array of sources
appleStartup: false, // Create Apple startup images. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }` or an array of sources
coast: false, // Create Opera Coast icon. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }` or an array of sources
favicons: true, // Create regular favicons. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }` or an array of sources
firefox: false, // Create Firefox OS icons. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }` or an array of sources
windows: false, // Create Windows 8 tile icons. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }` or an array of sources
yandex: false // Create Yandex browser icon. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }` or an array of sources
}
};

Expand Down
7 changes: 1 addition & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6688,12 +6688,7 @@ minimist-options@^3.0.1:
arrify "^1.0.1"
is-plain-obj "^1.1.0"

minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=

minimist@1.2.5, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5:
minimist@0.0.8, minimist@1.2.5, minimist@>=1.2.5, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
Expand Down

0 comments on commit b38627b

Please sign in to comment.