Is there a way to customize a package.json file using postinstall?

I've developed a package on npm that generates an "scss directory structure" and my goal is to include custom scripts in the package.json file located at the project's root.

MY-PROJECT
├── node_modules
├── scss
└── package.json <--

Currently, all I can do is copy a file named "package.json" to the local directory, potentially overwriting any existing file of the same name.

My intention is not to overwrite existing files, but rather to simply append scripts like "npm run watch," enabling users to jump right into their projects without manually configuring these scripts themselves.

Your assistance is greatly appreciated.

Answer №1

Here is a helpful node.js script for you to use:

post-install.js

const saveFile = require('fs').writeFileSync;

const pkgJsonPath = require.main.paths[0].split('node_modules')[0] + 'package.json';

const json = require(pkgJsonPath);

if (!json.hasOwnProperty('scripts')) {
  json.scripts = {};
}

json.scripts['watch'] = '<some_commands_here>';

saveFile(pkgJsonPath, JSON.stringify(json, null, 2));

package.json

To implement the above script, add the following to the scripts section of your project's package.json file under the postinstall script:

{
  "scripts": {
    "postinstall": "node post-install"
  }
}

Note: Make sure that the `post-install.js` script is in the same directory as your `package.json` file.


Explanation:

  1. The code snippet shown above retrieves the path to the consuming project's package.json using the following line:

    const pkgJsonPath = require.main.paths[0].split('node_modules')[0] + 'package.json'
    

    This logic extracts the pathname at position index 0 from an array returned by require.main.paths, splits it at 'node_modules', and then concatenates 'package.json' to obtain the desired path.

  2. The script then proceeds to parse the package.json file and store the parsed JSON object in the variable json.

  3. If the package.json does not contain a scripts key, one is created with an empty object assignment:

    if (!json.hasOwnProperty('scripts')) {
      json.scripts = {};
    }
    
  4. Following this, a custom npm script can be defined within the script by modifying the placeholder text '<some_commands_here>'. This part of the script allows for flexibility based on individual requirements.

    json.scripts['watch'] = '<some_commands_here>';
    
  5. Finally, the updated JSON data is converted back into a string format using JSON.stringify(). The modified content is then written back to the original package.json file using fs.writeFileSync().

Answer №2

If you want to automate tasks in your project, you can create a small script and include commands directly in the scripts section of your package.json.

Example Script:

const data = require("./package.json")
data.scripts["start:dev"] = "npm run dev"
require("fs").writeFileSync(process.cwd() + "/package.json", JSON.stringify(data, null, 2))

Updated package.json:

{
"scripts": {
    "postinstall": "node example-script.js"
  }
}

You can also write a simple inline script as a string and execute it using node -e.

{
"scripts": {
    "postinstall": "node -e 'const data = require(\"./package.json\"); data.scripts[\"start:dev\"] = \"npm run dev\";require(\"fs\").writeFileSync(process.cwd() + \"/package.json\", JSON.stringify(data, null, 2))'"
  },
}

Answer №3

I encountered a similar issue myself. In case you find yourself needing to make changes to the root package.json file while your module contains a "postinstall" script and is being used as an npm dependency in another project, you will have to navigate through parent directories until you reach a point where no package.json file is found.

const fs = require('fs');
const path = require('path');

let rootPath = '../';
while (!fs.existsSync(path.resolve(rootPath, 'package.json'))){
  rootPath += '../';
}
const pkgJsonPath = path.resolve(rootPath, 'package.json');

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to make a div pull its background image directly from the internet instead of using the cached version?

Running relevant Javascript every fifteen minutes to fetch the appropriate image from the internet: document.getElementById('weatherbug').style.background = "url('http://tinyurl.com/jwltx5s') repeat scroll -1px -24px transparent"; The ...

I encountered an issue while making customizations to my default next.config.js file. Despite attempting various solutions, I consistently encountered an error regarding the invalid src property

I'm currently trying to introduce some custom configurations into the next.config.js file. However, I keep encountering an error regarding an invalid src prop. Despite my attempts to troubleshoot in various ways, the error persists. // ...

Is it possible to retrieve several columns using the pluck method in Underscore.js following the input from the where method, similar to LINQ

var persons = [ {name : "Alice", location : "paris", amount : 5}, {name : "Bob", location : "tokyo", amount : 3}, {name : "Eve", location : "london", amount : 10} ]; var filteredResults=_.pluck(_.where(persons, {location : "paris"}), 'nam ...

Tips on navigating to a URL with a component in React without losing the passed props

When the onClick event is triggered, I aim to redirect to a new component with props passed to it along with a new URL. My App.js import React from "react"; import Main from "./Components/Main/Main"; import "bootstrap/dist/css/boo ...

What is the best way to extract information from the response of a fetch request in JavaScript using Express and Node.js

Currently, my script code resides in the index.html file, which I understand is not the ideal location. However, I am in the process of getting it to work before relocating it. readOperaciones(); async function readOperaciones(){ try{ ...

Error TS2322: Cannot assign type 'Promise<Hero | undefined>' to type 'Promise<Hero>'

I am currently studying angular4 using the angular tutorial. Here is a function to retrieve a hero from a service: @Injectable() export class HeroService { getHeroes(): Promise<Hero[]> { return new Promise(resolve => { // ...

What clues can a regular human use to interpret the "npm ERR! code ERESOLVE" message?

I am currently facing some npm dependency issues and I am looking for a solution without resorting to using --force or --legacy-peer-deps. The error messages are quite confusing and difficult to interpret. Is there a tool available that can give me a more ...

Limit the vertical movement in Vue drag and drop operations

Currently, I am working on a project that involves implementing drag-and-drop functionality using vue-draggable. You can find more information about it here: https://github.com/SortableJS/Vue.Draggable. I am facing an issue where the height of the element ...

What is the best way to arrange this script?

I am currently working on a Javascript script that automatically scrolls down and loads a new URL when it reaches the bottom of the page. However, I would like to add a delay of 30 seconds before the new URL is loaded. Although I am relatively new to Java ...

I'm looking for the best way to send POST data to an API with Meteor's HTTP package

Here's my current code snippet: HTTP.post("http://httpbin.org/post", {}, function(error, results) { if (results) { console.log(results); } else { console.log(error) } ...

How can one create a function that delays the execution of code until AJAX data is received

I developed a CKEditor plugin that fetches data via ajax to create RichCombo functionality. The plugin functions correctly, however, when there are multiple instances of the editor on a single page, each plugin ends up sending its own ajax request, leading ...

How to implement datepicker on multiple input fields

Below are the two input fields that have datepicker functionality: <div class="row"> <input type="text" class="form-control" datepicker-popup="{{format}}" ng-model="dt" is-open="opened" min="minDate" max="'2015-06-22&apos ...

Can you explain the significance of syntax in sample code (typescript, react)?

const sampleFunction: (inputString: string) => string = inputString => { return inputString.split(""); } I'm a bit confused about the code below and would appreciate some clarification. I understand that only "string" as a type is accepted, b ...

Maya model being loaded using Three.js

I've been following a tutorial on how to load Maya models using Three.js, which you can find here. Everything is going smoothly, but the tutorial only covers loading models with a single texture. Below is the source code snippet from the tutorial: ...

Take action upon being added to a collection or being observed

Consider the following scenario: I have an array called "x" and an Observable created from this array (arrObs). There is a button on the page, and when a user clicks on it, a random number is added to the array. The goal is to display the newly added value ...

What steps can I take to troubleshoot the 'Uncaught DOMException: failed to execute add on DOMTokenList' error?

I am looking to create media icons <div class="contact"> <span> <i class="fa fa-phone"></i> </span> <span> <i class="fa fa-facebook"></i> </spa ...

CSS: Unexpected value, received NaNrgb

I was attempting to incorporate a checkbox into a Bootstrap form that turns green when it is checked. Here is the code I used: function updateColor() { $("#check1").animate({ "background-color": "rgb(209, 231, 221)" }); } <script src="http ...

Executing `removeChild` within a timeout during page load does not yield the expected results

I have an HTML div that is designed to contain dynamically generated children. These children are meant to be removed from the list after a specific amount of time (e.g. 1000 ms). Although some people have experienced scope issues with timeout functions, ...

Efficiently rearranging elements by adjusting their top values techniques

My iPhone lockscreen theme features various elements displaying weather facts such as text and small images. Currently, these elements are positioned in the middle of the screen and I want to move them all to the top. Each element has a unique position abs ...

Utilize the power of XMLHttpRequest to fetch and load numerous audio files, seamlessly integrating them for playback through the Web Audio

I am looking to create a web application that loads three different audio files, each one second long, in a specific order, and then merges them into a single Audio Buffer consecutively. To illustrate my goal, here is a sample code snippet: var AudioCo ...