Programmatically Edit Any File in NodeJS

NodeJS is useful for way more than just installing packages or running servers. For instance, using the npx package and Node’s filesystem methods, you can create complex logic for interacting with files as if they were plain-ol’ Javascript objects. No BASH required.

This little piece documents a way to programmatically modify package.json. In this example I append JSON output to my existing test script and add a new script into package.json Using this pattern and these methods, you can do basically anything you want inside your repo.

Install NPX

npm i -g npx

Create a File for your NPX Script

touch fs-demo.js

Load package.json as an object

const fs = require('fs');
const pkgJson = require('./package.json’);

CRUD your key/value pairs like any object

const { test: testScript } = packageJson.scripts;

if(testScript.includes('--outputFile')){
  console.log('No changes needed in test script')
} else{
  packageJson.scripts.test = testScript + ' --json --outputFile=jest-test-results.json';
  console.log('Updating test script');
}

packageJson.scripts.foo = 'bar';

Write the updated ‘package.json’ to fs

fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
console.log('Package saved');

NOTE: the additional arguments to JSON.stringify() are there to pretty-print the result. Otherwise you’ll wind up with an unreadable JSON string for the next engineer to prettify. Please practice good coding etiquette 😉

Finally: Run the NPX Script

npx ./fs-demo.js

Here’s the Whole Shebang

const fs = require('fs');
const packageJson = require('./package.json');

const { test: testScript } = packageJson.scripts;

if(testScript.includes('--outputFile')){
  console.log('No changes needed in test script')
} else{
  packageJson.scripts.test = testScript + ' --json --outputFile=jest-test-results.json';
  console.log('Updating test script');
}

packageJson.scripts.foo = 'bar';

fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
console.log('Package saved');

Gotcha: Working with Arbitrary File Types

Working with other file types (say, .gitignore) can be a problem because require doesn’t know what to do with them. It only thinks in terms of Javascript.

To get around this, you can fs.read files into memory and interact with them like strings:

const gitIgnore = fs.readFileSync('./.gitignore', 'utf-8');

You can then chop up your string however you want. For instance in the example below, you can split it on the new-line.

let myGitIgnore = gitIgnore.split(/\n/);

At this point all you’re doing is transforming string data. When you’re done, you reassemble it like above and simply fs.write back to your target file.

Theming Semantic UI with create-react-app

Semantic UI’s default styling is great, but it’s not the end-all/be-all of web design. Semantic’s versatility in terms of build options is great for quickly deploying something beautiful, but it’s confusing when you want to customize it with your own fonts/colors/etc.

This blog post contains the recipe I used for going from default Semantic UI settings to a “Hello World” page using Arial instead of Lato as the site-wide font- kind of a “Hello World” for styling. It assumes you already have experience deploying React apps with create-react-app and have some familiarity with Semantic UI.

Create the React App

Run create-react-app to build your basic front-end. Nothing unusual here.

Install Gulp

Gulp is Semantic UI’s task runner. In layman’s terms: when you customize your Semantic Build, Gulp will merge and minify it into semantic.min.css, etc. so your app can refer to a single file for custom styling or other tweaks.

Run npm install gulp to install it.

Install Semantic UI

Semantic has various configurations of how to do this, depending on your level of customization.

Run npm install semantic-ui-react to use Semantic as a library of React components.

Run npm install semantic-ui to deploy a fully-customizable Semantic build. The docs suggest doing a yarn add instead of npm install, but Yarn failed to actually install Semantic (it’s a known issue right now).

Choose Express Setup when Semantic prompts you. The create-react-app build right now requires you to install Semantic UI in your project’s example-app/src/ directory (as opposed to your root example-app/ directory, which is the Automatic install setting).

Semantic UI needs to live inside example-app/src/semantic/ because your create-react-app instance won’t have permission to look outside the example-app/src/ directory.

Build Your Semantic Library

Run gulp build inside example-app/src/semantic/ to create your default Semantic install.

Run gulp watch inside example-app/src/semantic/ to have Gulp automatically update Semantic when you make changes to your theme.

Customize your Site theme

Now we’re operating within example-app/src/semantic/. The docs can get confusing because Semantic UI has its own /src/ directory as well. For brevity at this point, keep in mind when I refer to /semantic/src/ I’m talking about example-app/src/semantic/src.

You can customize the site theme at src/site/. This can be confusing because you’d think your theming should take place in src/themes/ cuz logic.

Without diving down a rabbit hole about theming options, I choose to customize the site theme because it has the final say on how your CSS comes out. The site theme overrides everything that comes before it.

Inside /site/globals/ you’ll find a file called site.variables, which is where we’ll finally change some base styles.

Drop in this snippet to change the theme font to Arial:

@emSize: 13px;
@fontSize: 13px;
@fontName: 'Arial';

Import Semantic UI CSS

Within your app’s src/index.js file, add the following import line:

import './semantic/dist/semantic.min.css';

This is the minified CSS file your Gulp task runner will build whenever you make changes to your site theme.

Errors? Outta Luck.

Don’t expect any indication you’ve done something wrong when editing your Semantic UI theme. If it doesn’t like your CSS for any reason, Semantic will default back to its original settings.

Mark Bello

Mark Bello