Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix language files for Node+ESM #1377

Merged
merged 26 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
636ccaa
Add babel script add-import-extension
sequba Feb 15, 2024
c09c336
Adjust check-publish-package script to new way of building ES
sequba Feb 15, 2024
1ba6009
Make babel output ES build with mjs extensions
sequba Feb 15, 2024
2dc845f
Add exports property to package.json
sequba Feb 15, 2024
bdbc35e
Fix the exports property
sequba Feb 15, 2024
fe9f96e
Tweaking package.json exports for cjs modules
sequba Feb 21, 2024
d25db4e
Adjust package.json exports for individual language files in CJS
sequba Feb 21, 2024
1962234
Add info about the source of the add-import-extension babel script
sequba Feb 21, 2024
4bf3f07
Add changelog entry
sequba Feb 21, 2024
36b997e
Move types key to be the first key in JSON object (exports property)
sequba Feb 22, 2024
5348bf7
Fix publint suggestions
sequba Feb 22, 2024
bccb945
Add paths ./i18n/languages/xxXX to exports property in package.json
sequba Feb 28, 2024
8a6d108
Use path ./i18n/languages/xxXX to import languages in the docs
sequba Feb 28, 2024
0cd4e05
Add paths with .js extensions to exports in package.json
sequba Feb 28, 2024
b309044
Merge branch 'develop' of github.com:handsontable/hyperformula into f…
sequba Aug 7, 2024
6f32832
Add migration guide for 3.0
sequba Aug 7, 2024
89159a5
Working on the migration guide
sequba Sep 5, 2024
3c29512
Unpolished vershion of the migration guide
sequba Oct 29, 2024
4c3d108
Upgrade migration instructions for Angular projects
sequba Oct 29, 2024
f0cea7b
Add more explenations to the migration guide
sequba Oct 30, 2024
51732b7
Add a fallback option for parcel projects to the migration guide
sequba Oct 31, 2024
5128ba3
Undo the version number change in package.json
sequba Oct 31, 2024
152b0dd
Update migration guide
sequba Nov 5, 2024
44e9d56
Remove unnsecessary step from the parcel section of the migration guide
sequba Nov 7, 2024
a07d548
Merge branch 'develop' into feature/issue-1344
sequba Nov 12, 2024
fecd03a
Rephrase CHANGELOG.md
sequba Nov 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .config/babel/add-import-extension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Based on https://github.com/handsontable/handsontable/blob/bd7628544ff83d6e74a9cc949e2c3c38fef74d76/handsontable/.config/plugin/babel/add-import-extension.js

const { existsSync, lstatSync } = require('fs');
const { dirname, resolve } = require('path');
const { types } = require('@babel/core');
const { declare } = require('@babel/helper-plugin-utils');

const VALID_EXTENSIONS = ['js', 'mjs'];

const hasExtension = moduleName => VALID_EXTENSIONS.some(ext => moduleName.endsWith(`.${ext}`));
const isCoreJSPolyfill = moduleName => moduleName.startsWith('core-js');
const isLocalModule = moduleName => moduleName.startsWith('.');
const isProcessableModule = (moduleName) => {
return !hasExtension(moduleName) && (isCoreJSPolyfill(moduleName) || isLocalModule(moduleName));
};

const createVisitor = ({ declaration, origArgs, extension = 'js' }) => {
return (path, { file }) => {
const { node: { source, exportKind, importKind } } = path;
const { opts: { filename } } = file;
const isTypeOnly = exportKind === 'type' || importKind === 'type';

if (!source || isTypeOnly || !isProcessableModule(source.value)) {
return;
}

const { value: moduleName } = source;
const absoluteFilePath = resolve(dirname(filename), moduleName);
const finalExtension = isCoreJSPolyfill(moduleName) ? 'js' : extension;

let newModulePath;

// Resolves a case where "import" points to a module name which exists as a file and
// as a directory. For example in this case:
// ```
// import { registerPlugin } from 'plugins';
// ```
// and with this directory structure:
// |- editors
// |- plugins
// |- filters/
// |- ...
// +- index.js
// |- plugins.js
// |- ...
// +- index.js
//
// the plugin will rename import declaration to point to the `plugins.js` file.
if (existsSync(`${absoluteFilePath}.js`)) {
newModulePath = `${moduleName}.${finalExtension}`;

// In a case when the file doesn't exist and the module is a directory it will
// rename to `plugins/index.js`.
} else if (existsSync(absoluteFilePath) && lstatSync(absoluteFilePath).isDirectory()) {
newModulePath = `${moduleName}/index.${finalExtension}`;

// And for other cases it simply put the extension on the end of the module path
} else {
newModulePath = `${moduleName}.${finalExtension}`;
}

path.replaceWith(declaration(...origArgs(path), types.stringLiteral(newModulePath)));
};
};

module.exports = declare((api, options) => {
api.assertVersion(7);

return {
name: 'add-import-extension',
visitor: {
// It covers default and named imports
ImportDeclaration: createVisitor({
extension: options.extension,
declaration: types.importDeclaration,
origArgs: ({ node: { specifiers } }) => [specifiers],
}),
ExportNamedDeclaration: createVisitor({
extension: options.extension,
declaration: types.exportNamedDeclaration,
origArgs: ({ node: { declaration, specifiers } }) => [declaration, specifiers],
}),
ExportAllDeclaration: createVisitor({
extension: options.extension,
declaration: types.exportAllDeclaration,
origArgs: () => [],
}),
}
};
});
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Fixed

- Fix ES module build to make importing language files possible in Node environment. [#1344](https://github.com/handsontable/hyperformula/issues/1344)

## [2.7.1] - 2024-07-18

### Fixed
Expand Down
4 changes: 3 additions & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ module.exports = {
},
// Environment for transpiling files to be compatible with ES Modules.
es: {
plugins: [],
plugins: [
['./.config/babel/add-import-extension.js', { extension: 'mjs' }],
],
},
},
};
3 changes: 2 additions & 1 deletion docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,8 @@ module.exports = {
children: [
['/guide/release-notes', 'Release notes'],
['/guide/migration-from-0.6-to-1.0', 'Migrating from 0.6 to 1.0'],
['/guide/migration-from-1.0-to-2.0', 'Migrating from 1.x to 2.0'],
['/guide/migration-from-1.x-to-2.0', 'Migrating from 1.x to 2.0'],
['/guide/migration-from-2.x-to-3.0', 'Migrating from 2.x to 3.0'],
]
},
{
Expand Down
6 changes: 3 additions & 3 deletions docs/guide/localizing-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ register the language like so:

```javascript
// import the French language pack
import frFR from 'hyperformula/es/i18n/languages/frFR';
import frFR from 'hyperformula/i18n/languages/frFR';

// register the language
HyperFormula.registerLanguage('frFR', frFR);
```

::: tip
To import the language packs, use the module-system-specific dedicated bundles at:
* **ES**: `hyperformula/es/i18n/languages/`
* **CommonJS**: `hyperformula/commonjs/i18n/languages/`
* **ES**: `hyperformula/i18n/languages/`
* **CommonJS**: `hyperformula/i18n/languages/`
* **UMD**: `hyperformula/dist/languages/`

For the UMD build, the languages are accessible through `HyperFormula.languages`, e.g., `HyperFormula.languages.frFR`.
Expand Down
87 changes: 87 additions & 0 deletions docs/guide/migration-from-2.x-to-3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Migrating from 2.x to 3.0

To upgrade your HyperFormula version from 2.x.x to 3.0.0, follow this guide.

## Importing language files

We changed the way of importing language files in ES module system to a more modern way using `mjs` files and `exports` property. This change is required to make HyperFormula compatible with newer ESM configurations in Node and browser environments.

The previous import paths became deprecated. For most environments they still work in version 3.0.0, but it will be removed in the future. To avoid any issues, update your code to use the new paths.

### New import paths for ES and CommonJS module systems

For ES and CommonJS modules, use the path `hyperformula/i18n/languages`, to import the language files. E.g.:

```javascript
import { frFR } from "hyperformula/i18n/languages"; // ESM

const { frFR } = require('hyperformula/i18n/languages'); // CommonJS
```
If you use the UMD module system, you don't need to change anything.

### Additional steps for projects using Angular 17 or older

1. Make sure you use Typescript 5 or newer
2. In your `tsconfig.json`, set:

```
"moduleResolution": "bundler",
```

### Additional steps for projects using Typescript

In your `tsconfig.json`, set:

```
"module": "node16",
"moduleResolution": "node16",
```

### Additional steps for projects using Webpack 4 or older

1. In your code, use the legacy paths for importing language files. Unfortunately, Webpack 4 does not support `exports` property. E.g.:

```javascript
import { frFR } from "hyperformula/es/i18n/languages";
```

2. In your `webpack.config.js`, add the following configuration to handle `.mjs` files properly:

```javascript
module: {
rules: [
{
test: /\.m?js$/,
include: /node_modules/,
type: "javascript/auto",
},
],
}
```

### Additional steps for projects using Parcel

1. Make sure you use Parcel 2.9 or newer. Older versions of Parcel do not support `exports` property.
2. Install the `@babel/core` package:

```
npm install --save-dev @babel/core
```

3. In your `package.json`, add the [following configuration](https://parceljs.org/blog/v2-9-0/#new-resolver):

```
"@parcel/resolver-default": {
"packageExports": true
}
```

If you don't want to upgrade Parcel or install `@babel/core`, you can keep using the legacy import paths for language files. E.g.:

```javascript
import { frFR } from "hyperformula/es/i18n/languages";
```

### Other projects

We tested the changes with the most popular bundlers and frameworks. If you use a different configuration, and you encounter any issues, please contact us via GitHub. We will try to make it work for you, although for older versions of bundlers and frameworks, it might be impossible.
Loading
Loading