Pour tout problème contactez-nous par mail : support@froggit.fr | La FAQ :grey_question: | Rejoignez-nous sur le Chat :speech_balloon:

Skip to content
Snippets Groups Projects
build.js 7.21 KiB
Newer Older
peter_rabbit's avatar
peter_rabbit committed
#!/usr/bin/env node

"use strict";

const fs = require("fs");
const browserify = require("browserify");
peter_rabbit's avatar
peter_rabbit committed
const config = require("./config");
peter_rabbit's avatar
peter_rabbit committed
const curDir = process.cwd();
peter_rabbit's avatar
peter_rabbit committed
const build_conf = config.build;
peter_rabbit's avatar
peter_rabbit committed

// Handle home page
const b = browserify();
peter_rabbit's avatar
peter_rabbit committed
b.add(`${curDir}/src/main.js`)
peter_rabbit's avatar
peter_rabbit committed
    .bundle()
peter_rabbit's avatar
peter_rabbit committed
    .pipe(fs.createWriteStream(`${curDir}/public/main.js`));
peter_rabbit's avatar
peter_rabbit committed

// Handle subpages
peter_rabbit's avatar
peter_rabbit committed
function getPageHtml(pageName, pageMeta) {
    let html = fs.readFileSync(`${curDir}/public/index.html`, "utf-8");
    const setMeta = function (metaName, value) {
peter_rabbit's avatar
peter_rabbit committed
        html = html.replace(
peter_rabbit's avatar
peter_rabbit committed
            html.match(new RegExp(`<meta\\s*name="${metaName}"[^>]+>`, "g"))[0],
peter_rabbit's avatar
peter_rabbit committed
            `<meta name="${metaName}" content="${value}"/>`
peter_rabbit's avatar
peter_rabbit committed
    const setTitle = function () {
        html = html.replace(
            html.match(new RegExp(`<title.+</title>`, "g"))[0],
            `<title>${pageMeta.title}</title>`
peter_rabbit's avatar
peter_rabbit committed
        );
        html = html.replace(
peter_rabbit's avatar
peter_rabbit committed
            html.match(new RegExp(`<h1.+</h1>`, "g")),
            `<h1 style="visibility: hidden">${pageMeta.title}</h1>`
peter_rabbit's avatar
peter_rabbit committed
    };
    const setStyleSheet = function () {
peter_rabbit's avatar
peter_rabbit committed
        html = html.replace(
            html.match(new RegExp(`<link.+/style.css[^>]+>`, "g"))[0],
peter_rabbit's avatar
peter_rabbit committed
            `<link href="/style/style.css" rel="stylesheet" />`
        );
    };
    const setJs = function () {
peter_rabbit's avatar
peter_rabbit committed
        html = html.replace(
            html.match(new RegExp(`<script.+main.js.+</script>`, "g"))[0],
peter_rabbit's avatar
peter_rabbit committed
            `<script type="text/javascript" src="./${pageName}.js"></script>`
        );
    };
peter_rabbit's avatar
peter_rabbit committed
    const setAdditionalMeta = function (metas) {
peter_rabbit's avatar
peter_rabbit committed
        html = html.replace(
peter_rabbit's avatar
peter_rabbit committed
            "</head>",
            `${metas
                .map(kv => {
                    const [name, content] = kv;
                    return `<meta name="${name}" content="${content}"/>`;
                })
                .join("\n")}</head>`
        );
    };
peter_rabbit's avatar
peter_rabbit committed
    const setOgMeta = function () {
        const pageOgMeta = pageMeta.open_graph || {};

        const getOgMetaSearchRegex = function (name) {
            return new RegExp(`<meta\\s*property="og:${name}"[^>]+>`, "g");
        };
        const getDefaultOgMetaContent = function (name) {
            const getRegexMatch = function (source, searchRe) {
                const m = source.match(searchRe);
                return m ? m[0] : "";
            };
            return getRegexMatch(
                getRegexMatch(
                    getRegexMatch(html, getOgMetaSearchRegex(name)),
                    new RegExp(`content=".+"`, "g")
                ),
                new RegExp(`".+"`, "g")
            ).replace(/"/g, "");
        };
        const requiredOgMeta = [
            { key: "title", defaultValue: pageMeta.title },
            { key: "description", defaultValue: pageMeta.description },
            {
                key: "image",
peter_rabbit's avatar
peter_rabbit committed
                defaultValue: pageMeta.image || getDefaultOgMetaContent("image"),
peter_rabbit's avatar
peter_rabbit committed
            },
            {
                key: "url",
                defaultValue: (function () {
                    const urlContent = getDefaultOgMetaContent("url");

                    return `${urlContent}${urlContent.charAt(urlContent.length - 1) !== "/" ? "/" : ""
                        }${pageName}`;
peter_rabbit's avatar
peter_rabbit committed
                })(),
            },
            {
                key: "locale",
                defaultValue: getDefaultOgMetaContent("locale"),
            },
            // TODO : handle locale:alternate meta tags array
        ];
peter_rabbit's avatar
peter_rabbit committed
        requiredOgMeta.forEach(entry => {
            const { key, defaultValue } = entry;
            html = html.replace(
                html.match(getOgMetaSearchRegex(key)),
peter_rabbit's avatar
peter_rabbit committed
                (function () {
                    const customValue = pageOgMeta[key];
                    if (!customValue)
                        return `<meta property="og:${key}" content="${defaultValue}"/>`;
                    else {
                        return Array.isArray(customValue)
                            ? customValue
                                .map(alt => `<meta property="og:${key}" content="${alt}"/>`)
                                .join("\n")
peter_rabbit's avatar
peter_rabbit committed
                            : `<meta property="og:${key}" content="${customValue}"/>`;
                    }
                })()
peter_rabbit's avatar
peter_rabbit committed
            );
        });
        const additionalOgMeta = Object.keys(pageMeta.open_graph).filter(
            k => !requiredOgMeta.map(rom => rom.key).includes(k)
        );

        if (additionalOgMeta.length > 0) {
            html = html.replace(
                "</head>",
                `${additionalOgMeta
peter_rabbit's avatar
peter_rabbit committed
                    .map(k => `<meta property="og:${k}" content="${pageMeta.open_graph[k]}"/>`)
                    .join("\n")}</head>`
            );
        }
peter_rabbit's avatar
peter_rabbit committed
    };

    const setJsonLdScript = function () {
        const jsonLd = pageMeta.json_ld;
        html = html.replace(
            html.match(new RegExp(`<script.+json.+>[^<]+</script>`, "g"))[0],
            `<script type="application/ld+json">${JSON.stringify(jsonLd)}</script>`
        );
    };

peter_rabbit's avatar
peter_rabbit committed
    setMeta("description", pageMeta.description);
peter_rabbit's avatar
peter_rabbit committed
    pageMeta.image && setMeta("image", pageMeta.image);
peter_rabbit's avatar
peter_rabbit committed
    setTitle();
    setStyleSheet();
    setJs();
    setAdditionalMeta(
        Object.entries(pageMeta).filter(kv => !build_conf.default_meta_keys.includes(kv[0]))
peter_rabbit's avatar
peter_rabbit committed
    );
    setOgMeta();
peter_rabbit's avatar
peter_rabbit committed
    // set twitter image
    html = html.replace(
        html.match(new RegExp(`<meta\\s*property="twitter:image"[^>]+>`, "g"))[0],
        `<meta property="twitter:image" content="${pageMeta.image ||
        html
            .match(new RegExp(`<meta\\s*name="image"[^>]+>`, "g"))[0]
            .match(new RegExp(`content=".+"`, "g"))[0]
            .match(new RegExp(`".+"`, "g"))
            .replace(/"/g, "")
peter_rabbit's avatar
peter_rabbit committed
        }"/>`
    );

    setJsonLdScript();
peter_rabbit's avatar
peter_rabbit committed
    return html;
peter_rabbit's avatar
peter_rabbit committed
}

function createPages(rootdir, destdir) {
    const pages = fs.readdirSync(rootdir);
peter_rabbit's avatar
peter_rabbit committed

    for (const p of pages) {
        const fPath = `${rootdir}/${p}`;
        const targetDirPath = `${destdir}/${p}`;
peter_rabbit's avatar
peter_rabbit committed

        if (!fs.existsSync(targetDirPath)) {
            fs.mkdirSync(targetDirPath);
        }
peter_rabbit's avatar
peter_rabbit committed

        const b = browserify();
peter_rabbit's avatar
peter_rabbit committed

        b.add(fPath)
            .bundle()
            .pipe(fs.createWriteStream(`${targetDirPath}/${p}.js`));
peter_rabbit's avatar
peter_rabbit committed

        const page = fs.createWriteStream(`${targetDirPath}/index.html`);
        const pageMeta = JSON.parse(fs.readFileSync(`${fPath}/meta.json`, "utf-8"));
        page.write(getPageHtml(p, pageMeta));
        if (fs.existsSync(`${fPath}/subpages`)) {
            createPages(`${fPath}/subpages`, targetDirPath);
        }
    }

    // If pages have been deleted in source, remove them in output directory too.
    for (const dir of fs.readdirSync(destdir).filter(f => {
        if (build_conf.protected_dirs.includes(f)) return false;
        const stats = fs.statSync(`${destdir}/${f}`);
        return stats.isDirectory();
    })) {
        if (!pages.includes(dir)) {
            const dPath = `${destdir}/${dir}`;
            try {
                const nestedFiles = fs.readdirSync(dPath);
                for (const nf of nestedFiles) {
                    fs.unlinkSync(`${dPath}/${nf}`);
                }
                fs.rmdirSync(dPath);
            } catch (error) {
                console.error(error);
peter_rabbit's avatar
peter_rabbit committed
}

createPages(`${curDir}/src/pages`, `${curDir}/public`);