Skip to content

Commit

Permalink
Require Node.js 20 and Electron 30 and move to ESM
Browse files Browse the repository at this point in the history
Fixes #266
  • Loading branch information
sindresorhus committed May 1, 2024
1 parent 449b5ee commit 7ddf0c6
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 84 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ jobs:
fail-fast: false
matrix:
node-version:
- 14
- 12
- 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
Expand Down
10 changes: 5 additions & 5 deletions fixture-cwd.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';
const path = require('path');
const electron = require('electron');
const Store = require('.');
import process from 'node:process';
import path from 'node:path';
import electron from 'electron';
import Store from './index.js';

// Prevent Electron from never exiting when an exception happens
process.on('uncaughtException', error => {
Expand All @@ -15,7 +15,7 @@ const store = new Store({cwd: 'foo'});
store.set('unicorn', '🦄');
console.log(store.path);

const store2 = new Store({cwd: path.join(__dirname, 'bar')});
const store2 = new Store({cwd: path.join(import.meta.dirname, 'bar')});
store2.set('ava', '🚀');
console.log(store2.path);

Expand Down
14 changes: 7 additions & 7 deletions fixture.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';
const assert = require('assert');
const electron = require('electron');
const Store = require('.');
import process from 'node:process';
import assert from 'node:assert';
import electron from 'electron';
import Store from './index.js';

// Prevent Electron from never exiting when an exception happens
process.on('uncaughtException', error => {
Expand All @@ -15,9 +15,9 @@ const storeWithSchema = new Store({
name: 'electron-store-with-schema',
schema: {
foo: {
default: 42
}
}
default: 42,
},
},
});

store.set('unicorn', '🦄');
Expand Down
42 changes: 19 additions & 23 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
import {Except} from 'type-fest';
import Conf, {Schema as ConfSchema, Options as ConfOptions} from 'conf';
import {type Except} from 'type-fest';
import Conf, {type Options as ConfigOptions} from 'conf';

declare namespace ElectronStore {
type Schema<T> = ConfSchema<T>;
export {Schema} from 'conf';

type Options<T extends Record<string, any>> = Except<ConfOptions<T>, 'configName' | 'projectName' | 'projectVersion' | 'projectSuffix'> & {
/**
Name of the storage file (without extension).
export type Options<T extends Record<string, any>> = Except<ConfigOptions<T>, 'configName' | 'projectName' | 'projectVersion' | 'projectSuffix'> & {
/**
Name of the storage file (without extension).
This is useful if you want multiple storage files for your app. Or if you're making a reusable Electron module that persists some data, in which case you should **not** use the name `config`.
This is useful if you want multiple storage files for your app. Or if you're making a reusable Electron module that persists some data, in which case you should **not** use the name `config`.
@default 'config'
*/
readonly name?: string;
};
}
@default 'config'
*/
readonly name?: string;
};

/**
Simple data persistence for your [Electron](https://electronjs.org) app or module - Save and load user settings, app state, cache, etc.
*/
declare class ElectronStore<T extends Record<string, any> = Record<string, unknown>> extends Conf<T> {
export default class ElectronStore<T extends Record<string, any> = Record<string, unknown>> extends Conf<T> {
/**
Initializer to set up the required `ipc` communication channels for the module when a `Store` instance is not created in the main process and you are creating a `Store` instance in the Electron renderer process only.
*/
static initRenderer(): void;

/**
Changes are written to disk atomically, so if the process crashes during a write, it will not corrupt the existing store.
@example
```
import Store = require('electron-store');
import Store from 'electron-store';
type StoreType = {
isRainbow: boolean,
Expand All @@ -50,12 +53,7 @@ declare class ElectronStore<T extends Record<string, any> = Record<string, unkno
//=> undefined
```
*/
constructor(options?: ElectronStore.Options<T>);

/**
Initializer to set up the required `ipc` communication channels for the module when a `Store` instance is not created in the main process and you are creating a `Store` instance in the Electron renderer process only.
*/
static initRenderer(): void;
constructor(options?: Options<T>);

/**
Open the storage file in the user's editor.
Expand All @@ -64,5 +62,3 @@ declare class ElectronStore<T extends Record<string, any> = Record<string, unkno
*/
openInEditor(): Promise<void>;
}

export = ElectronStore;
29 changes: 15 additions & 14 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
'use strict';
const path = require('path');
const {app, ipcMain, ipcRenderer, shell} = require('electron');
const Conf = require('conf');
import process from 'node:process';
import path from 'node:path';
import {
app,
ipcMain,
shell,
} from 'electron'; // eslint-disable-line import/no-duplicates
import electron from 'electron'; // eslint-disable-line import/no-duplicates
import Conf from 'conf';

let isInitialized = false;

Expand All @@ -13,7 +18,7 @@ const initDataListener = () => {

const appData = {
defaultCwd: app.getPath('userData'),
appVersion: app.getVersion()
appVersion: app.getVersion(),
};

if (isInitialized) {
Expand All @@ -29,15 +34,15 @@ const initDataListener = () => {
return appData;
};

class ElectronStore extends Conf {
export default class ElectronStore extends Conf {
constructor(options) {
let defaultCwd;
let appVersion;

// If we are in the renderer process, we communicate with the main process
// to get the required data for the module otherwise, we pull from the main process.
if (ipcRenderer) {
const appData = ipcRenderer.sendSync('electron-store-get-data');
if (process.type === 'renderer') {
const appData = electron.ipcRenderer.sendSync('electron-store-get-data');

if (!appData) {
throw new Error('Electron Store: You need to call `.initRenderer()` from the main process.');
Expand All @@ -50,12 +55,10 @@ class ElectronStore extends Conf {

options = {
name: 'config',
...options
...options,
};

if (!options.projectVersion) {
options.projectVersion = appVersion;
}
options.projectVersion ||= appVersion;

if (options.cwd) {
options.cwd = path.isAbsolute(options.cwd) ? options.cwd : path.join(defaultCwd, options.cwd);
Expand All @@ -81,5 +84,3 @@ class ElectronStore extends Conf {
}
}
}

module.exports = ElectronStore;
17 changes: 8 additions & 9 deletions index.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {expectType, expectAssignable} from 'tsd';
import Store = require('.');
import Store, {Schema} from './index.js';

new Store({defaults: {}}); // eslint-disable-line no-new
new Store({name: 'myConfiguration'}); // eslint-disable-line no-new
Expand All @@ -9,7 +9,7 @@ const store = new Store();
store.set('foo', 'bar');
store.set({
foo: 'bar',
foo2: 'bar2'
foo2: 'bar2',
});
store.delete('foo');
store.get('foo');
Expand All @@ -24,13 +24,11 @@ store.size; // eslint-disable-line @typescript-eslint/no-unused-expressions
store.store; // eslint-disable-line @typescript-eslint/no-unused-expressions

store.store = {
foo: 'bar'
foo: 'bar',
};

store.path; // eslint-disable-line @typescript-eslint/no-unused-expressions

type Schema<T> = Store.Schema<T>;

type TypedStore = {
isEnabled: boolean;
interval: number;
Expand All @@ -39,8 +37,8 @@ type TypedStore = {
const typedStore = new Store<TypedStore>({
defaults: {
isEnabled: true,
interval: 30000
}
interval: 30_000,
},
});

expectType<number>(typedStore.get('interval'));
Expand All @@ -49,15 +47,16 @@ const isEnabled = false;
typedStore.set('isEnabled', isEnabled);
typedStore.set({
isEnabled: true,
interval: 10000
interval: 10_000,
});

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const offDidChange = typedStore.onDidChange(
'isEnabled',
(newValue, oldValue) => {
expectType<boolean | undefined>(newValue);
expectType<boolean | undefined>(oldValue);
}
},
);

expectAssignable<() => void>(offDidChange);
Expand Down
29 changes: 22 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@
"email": "sindresorhus@gmail.com",
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": {
"types": "./index.d.ts",
"default": "./index.js"
},
"sideEffects": false,
"engines": {
"node": ">=20"
},
"scripts": {
"test": "xo && ava && tsd"
},
Expand All @@ -35,20 +43,27 @@
"save"
],
"dependencies": {
"conf": "^10.2.0",
"type-fest": "^2.17.0"
"conf": "^12.0.0",
"type-fest": "^4.18.1"
},
"devDependencies": {
"ava": "^2.4.0",
"electron": "^12.0.4",
"execa": "^5.0.0",
"tsd": "^0.14.0",
"xo": "^0.38.2"
"ava": "^6.1.2",
"electron": "^30.0.1",
"execa": "^8.0.1",
"tsd": "^0.31.0",
"xo": "^0.58.0"
},
"xo": {
"envs": [
"node",
"browser"
]
},
"tsd": {
"compilerOptions": {
"module": "node16",
"moduleResolution": "node16",
"moduleDetection": "force"
}
}
}
25 changes: 14 additions & 11 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ You can use this module directly in both the main and renderer process. For use
npm install electron-store
```

*Requires Electron 11 or later.*
*Requires Electron 30 or later.*

> [!NOTE]
> This package is native [ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) and no longer provides a CommonJS export. If your project uses CommonJS, you will have to [convert to ESM](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). More info about [Electron and ESM](https://www.electronjs.org/docs/latest/tutorial/esm). Please don't open issues for questions regarding CommonJS and ESM.
## Usage

```js
const Store = require('electron-store');
import Store from 'electron-store';

const store = new Store();

Expand Down Expand Up @@ -68,7 +71,7 @@ You should define your schema as an object where each key is the name of your da
Example:

```js
const Store = require('electron-store');
import Store from 'electron-store';

const schema = {
foo: {
Expand Down Expand Up @@ -107,7 +110,7 @@ The `migrations` object should consist of a key-value pair of `'version': handle
Example:

```js
const Store = require('electron-store');
import Store from 'electron-store';

const store = new Store({
migrations: {
Expand Down Expand Up @@ -147,7 +150,7 @@ This can be useful for logging purposes, preparing migration data, etc.
Example:

```js
const Store = require('electron-store');
import Store from 'electron-store';

console.log = someLogger.log;

Expand Down Expand Up @@ -245,7 +248,7 @@ Default: `true`
Accessing nested properties by dot notation. For example:

```js
const Store = require('electron-store');
import Store from 'electron-store';

const store = new Store();

Expand Down Expand Up @@ -362,7 +365,7 @@ Get the item count.
Get all the data as an object or replace the current data with an object:

```js
const Store = require('electron-store');
import Store from 'electron-store';

const store = new Store();

Expand All @@ -388,15 +391,15 @@ Initializer to set up the required `ipc` communication channels for the module w
In the main process:

```js
const Store = require('electron-store');
import Store from 'electron-store';

Store.initRenderer();
```

And in the renderer process:

```js
const Store = require('electron-store');
import Store from 'electron-store';

const store = new Store();

Expand All @@ -416,8 +419,8 @@ The `serialize` and `deserialize` options can be used to customize the format of
Example using YAML:

```js
const Store = require('electron-store');
const yaml = require('js-yaml');
import Store from 'electron-store';
import yaml from 'js-yaml';

const store = new Store({
fileExtension: 'yaml',
Expand Down
Loading

0 comments on commit 7ddf0c6

Please sign in to comment.