preface

Yeoman/ConfigStore is a very useful and powerful tool for configuring storage in Yeoman. The core idea is to create a file in the current system based on the established file naming rules to store user configuration data. The configuration can be modified and saved by reading and writing files.

use

  1. Create a new Node project and install the dependencies.

    npm install configstore
    Copy the code
  2. Set type in package.json to module.

  3. Create test_configstore.js and execute. Test_configstore.js looks like this:

    import Configstore from "configstore";
    import fs from "fs";
    
    const packageJson = JSON.parse(fs.readFileSync("./package.json"."utf8"));
    
    // Create a Configstore instance.
    const config = new Configstore(packageJson.name, { foo: "bar" });
    
    console.log(config.get("foo"));
    //=> 'bar'
    
    config.set("awesome".true);
    console.log(config.get("awesome"));
    //=> true
    
    // Use dot-notation to access nested properties.
    config.set("bar.baz".true);
    console.log(config.get("bar"));
    //=> {baz: true}
    
    config.delete("awesome");
    console.log(config.get("awesome"));
    Copy the code
  4. Run the test_configstore.js file.

Rely on the analysis of

Learn about the NPM package that ConfigStore relies on.

  • path

Node has built-in modules for manipulating files and directories.

  • os

Node has a built-in module that provides operating system-specific methods and properties.

  • graceful-fs

An upgraded version of fs module, compatible with different platforms and environments. In the file read and write this piece, is superior to the native FS module.

  • xdg-basedir

On Linux, obtain the user’s configuration file path. For example: ‘/usr/share’ – a directory for storing shared data, etc.

  • write-file-atomic

Fs.writefile extension module for file writing and support for setting the UID/GID of a file.

Uid indicates the file operation permission of a user, GID indicates the file operation permission of the user group.

  • dotProp

Supports multi-level nesting of objects, through ‘.’ access and manipulation of object properties.

  • unique-string

Generates a random 32-bit length string.

Front variable

/** * 1. ConfigDirectory: stores temporary files for the current operating system. Linux Obtained from the xdgConfig package. * Other operating systems get os.tmpdir(), followed by a concatenated 32-bit string. * eg: 'C:\\Users\\userName\\AppData\\Local\\Tempb4de2a49c8ffa3fbee04446f045483b2' * 2. permissionError: MkdirOptions: Folder creation configuration, mode represents Linux directory permissions, and recursive means recursive creation * mode: 0O0700: indicates that the owner has read, write, and execute permissions * 4. WriteFileOptions: indicates write file configuration. * /
const configDirectory = xdgConfig || path.join(os.tmpdir(), uniqueString());
const permissionError = "You don't have access to this file.";
const mkdirOptions = { mode: 0o0700.recursive: true };
const writeFileOptions = { mode: 0o0600 };
Copy the code

The core code

/** * 5. Configstore class declaration, using ES modules syntax to export */
export default class Configstore {
	/** * 6. Constructor, used to create Configstore instance *@param {*} The id is used to name the configStore storage file *@param {*} Defaults stores data by default, such as an object *@param {*} Options Stores configuration */
	constructor(id, defaults, options = {}) {
		/** * 7. PathPrefix: the path name of the stored file. * If globalConfigPath is set, the name is id/config.json. * Otherwise: configStore /id.json */
		const pathPrefix = options.globalConfigPath
			? path.join(id, "config.json")
			: path.join("configstore".`${id}.json`);

		/** * 8._path: indicates the full path for storing files in the operating system. * options.configPath: indicates the full path to be defined by the user. * Or the system temporary storage path/storage file path. * /
		this._path = options.configPath || path.join(configDirectory, pathPrefix);

		/** * 9._all: Stores the storage data of the current ConfigStore instance in object format. * /
		if (defaults) {
			this.all = { ... defaults, ... this.all, }; }}/** * 10. Read the file storing the object in UTF-8 encoding */
	get all() {
		try {
			return JSON.parse(fs.readFileSync(this._path, "utf8"));
		} catch (error) {
			// Create directory if it doesn't exist
			// File does not exist, return empty object
			if (error.code === "ENOENT") {
				return {};
			}

			// Improve the message of permission errors
			// File permissions are limited
			if (error.code === "EACCES") {
				error.message = `${error.message}\n${permissionError}\n`;
			}

			// Empty the file if it encounters invalid JSON
			if (error.name === "SyntaxError") {
				writeFileAtomic.sync(this._path, "", writeFileOptions);
				return {};
			}

			throwerror; }}/** * 11. Storage data is written, which is equivalent to overwriting */
	set all(value) {
		try {
			// Make sure the folder exists as it could have been deleted in the meantime
			fs.mkdirSync(path.dirname(this._path), mkdirOptions);

			writeFileAtomic.sync(
				this._path,
				JSON.stringify(value, undefined."\t"),
				writeFileOptions
			);
		} catch (error) {
			// Improve the message of permission errors
			if (error.code === "EACCES") {
				error.message = `${error.message}\n${permissionError}\n`;
			}

			throwerror; }}/** * 12. Get the number of stored data (JSON object) attributes */
	get size() {
		return Object.keys(this.all || {}).length;
	}

	/** * 13. Retrieve stored data by key name (essentially object property access, additional support for nested object property access) *@param {*} key
	 * @returns* /
	get(key) {
		return dotProp.get(this.all, key);
	}

	/** * 14. Assign a key-value to the data object attribute *@param {*} key
	 * @param {*} value* /
	set(key, value) {
		const config = this.all;

		if (arguments.length === 1) {
			for (const k of Object.keys(key)) { dotProp.set(config, k, key[k]); }}else {
			dotProp.set(config, key, value);
		}

		this.all = config;
	}

	/** * 15. Check whether an attribute * exists in the data@param {*} key
	 * @returns* /
	has(key) {
		return dotProp.has(this.all, key);
	}

	/** * 16. Delete the attribute * of the specified key@param {*} key* /
	delete(key) {
		const config = this.all;
		dotProp.delete(config, key);
		this.all = config;
	}

	/** * 17. Clear the current stored data */
	clear() {
		this.all = {};
	}

	/** * 18. Obtain the full path of the current data store file */
	get path() {
		return this._path;
	}
Copy the code

conclusion

When developing JS library, the part involving file operation needs to take into account the circumstances of different systems and platforms. Configstore is a good idea to follow.