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

[DO NOT MERGE!] Fix language files for Node+ESM #1377

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 40 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,40 @@
# 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

### Typescript

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

in tsconfig

### Webpack 4

Works but only with legacy paths. Webpack 4 does not support "exports". And requires special configuration for handling mjs files

I found out that webpack 4 does not support exports, and there seems to be no easy workaround for that. By tweaking the webpack configuration, I managed to make the project import HyperFormula correctly using the legacy paths. Working demo.

- https://github.com/webpack/webpack/issues/9509#issuecomment-1381896299
- https://stackoverflow.com/a/74957466
- https://github.com/handsontable/hyperformula-demos/tree/import-demos/import-demo-esm-webpack-4

### Parcel

parcel does not support "exports" by default, since v2.9 it can be configured to support it

- https://github.com/parcel-bundler/parcel/issues/4155
- https://parceljs.org/blog/v2-9-0/#new-resolver

### Angular

to be verified

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