Themes - how to approach client-side js assets with yarn

Hi, I’m trying to customize a theme for a ghost site, and know by default ghost tends to use yarn for at least back-end/build-time js packages, then there’s typically an assets/js and assets/js/lib directory for manually added js files, and of course the gulpfile to direct what gets used as a site resource.

I want to install front-end js packages using a package manager tool - in the past I’ve used bower but they recommend migrating to yarn.

But the confusion comes in distinguishing using yarn for client-side js packages. The options I’ve considered are:

  • Use the gulp file to specify selected packages within node_modules/ to be part of the built assets.

  • Having two yarn.lock files. Seems like a bad idea and doesn’t seem to actually work, as yarn seems to look for the parent yarn.lock file even if you tried to set one up in the assets/ directory.

  • Keep using bower for client-side stuff. It’s technically still an option but don’t feel happy thinking about two different js package managers.

I ended up doing the following:

  1. Installed whatever front-end things I want via yarn (e.g. yarn add jquery) which ends up as a ‘dependency’ in package.json . This’ll put things in node_modules with everything else, but we’ll deal with this in a second.

  2. In the node_modules directory, notice how ‘dependency’ installed modules have a ‘dist’ directory compared to the ‘devDependency’ modules.

  3. Installed as a ‘devDependency’ gulp-npm-dist - i.e. yarn add gulp-npm-dist --dev which provides some functionality to make getting the client-side js files from node_modules. You may also want to do yarn add gulp-rename --dev while you’re at it.

  4. In your theme’s gulpfile, reference the packages

// gulp distribute node_modules files 
const gulp = require('gulp');
const npmDist = require('gulp-npm-dist');
const rename = require('gulp-rename');

and add in a function to copy in the files.

// Copy node client-side 
// dependencies to ./assets/js/node_dist
function copyNodeDependencies() {
    gulp.src(npmDist(), {base:'./node_modules/'})
        .pipe(rename(function(path) {
            path.dirname = path.dirname.replace(/\/dist/, '').replace(/\\dist/, '');

and reference it in your js gulp function to run and also to source them:

function js(done) {
[ of your code...]

full example gist here: Gulpfile example for front-end packages (both build & client-side) with just yarn (i.e. depends on npm, but no Bower needed!) · GitHub

Can’t edit the previous post, but there’s a more updated function now called refreshNodeDependencies:

// Clean/Copy node client-side 
// dependencies to ./assets/js/node_dist
function refreshNodeDependencies(done) {
    const deletedDirectoryPaths =   del.sync(['assets/js/node_dist/**']);
    // console.log('Deleted files:\n', deletedDirectoryPaths.join('\n'));
    gulp.src(npmDist(), {base:'./node_modules/'})
        .pipe(rename(function(path) {
            path.dirname = path.dirname.replace(/\/dist/, '').replace(/\\dist/, '');
    return done();