Skip to content
Merged
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@

## [Unreleased](https://github.com/Seen-Arabic/Arabic-Services-JavaScript/compare/v1.0.8...HEAD)

### Added

- **Tree Shaking & Named Exports**: Full support for tree shaking with direct named exports for all functions, constants, and utilities, reducing bundle size by over 96% when importing specific functions ([#28](https://github.com/Seen-Arabic/Arabic-Services-JavaScript/issues/28)).
- **Dual Module System (ESM & CommonJS)**: Added native ES Module build under `dist/esm/` with `sideEffects: false` and conditional typed exports in `package.json`.
- **Documentation & Guidelines**: Added `CONTRIBUTING.md` with Conventional Commits specifications and `AGENT.md` tailored for zero-runtime-dependency npm library development ([PR #30](https://github.com/Seen-Arabic/Arabic-Services-JavaScript/pull/30)).

### Updated

- **Tooling Modernization**: Migrated to ESLint 9 Flat Config (`eslint.config.mjs`) with `typescript-eslint` v8 ([PR #31](https://github.com/Seen-Arabic/Arabic-Services-JavaScript/pull/31)).
- **Dependencies**: Upgraded devDependencies (`typescript` 5.7, `webpack` 5.98, `prettier` 3.5, `ts-jest` 29.2) and replaced deprecated `rimraf` with native Node.js `fs.rmSync`.

## [1.0.8](https://github.com/Seen-Arabic/Arabic-Services-JavaScript/releases/tag/v1.0.8) - 2025-09-04 (12 Rabi' al-Awwal 1446)

### Added
Expand Down
19 changes: 18 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,27 @@
"version": "1.0.8",
"description": "Utility functions on Arabic text",
"main": "./dist/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/index.d.ts",
"sideEffects": false,
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"./package.json": "./package.json"
},
"scripts": {
"dev": "ts-node src/index.ts",
"build": "tsc",
"build:cjs": "tsc -p tsconfig.json",
"build:esm": "tsc -p tsconfig.esm.json && node -e \"fs.writeFileSync('dist/esm/package.json', JSON.stringify({ type: 'module', sideEffects: false }))\"",
"build": "npm run clean && npm run build:cjs && npm run build:esm",
"clean": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\"",
"test": "jest --config jest.config.cjs --coverage",
"prepublishOnly": "npm run build",
Expand Down
6 changes: 3 additions & 3 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ export {
ARABIC_SUFFIXES,
PRONOUNCED_LETTERS,
STANDARD_LETTERS,
} from './arabic-letters';
export { BANNED_WORDS } from './banned-words';
export { TASHKEEL } from './tashkeel';
} from './arabic-letters.js';
export { BANNED_WORDS } from './banned-words.js';
export { TASHKEEL } from './tashkeel.js';
17 changes: 13 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import * as constants from './constants';
import { OldArabicOptions } from './options';
import * as script from './scripts';
import * as utils from './utils';
import * as constants from './constants/index.js';
import { OldArabicOptions } from './options.js';
import * as script from './scripts/index.js';
import * as utils from './utils/index.js';

// Named exports for Tree Shaking
export * from './constants/index.js';
export * from './options.js';
export * from './scripts/index.js';
export * from './utils/index.js';

// Full object export for backward compatibility
export const ArabicServices = {
constants,
utils,
...script,
};

export default ArabicServices;

export type { OldArabicOptions };
2 changes: 1 addition & 1 deletion src/scripts/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './scripts';
export * from './scripts.js';
8 changes: 4 additions & 4 deletions src/scripts/scripts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ARABIC_DOTLESS_DICT, BANNED_WORDS, TASHKEEL } from '../constants';
import { ARABIC_DOTLESS_DICT, BANNED_WORDS, TASHKEEL } from '../constants/index.js';
import {
ALEF,
ALONE_LETTERS,
Expand All @@ -9,9 +9,9 @@ import {
STANDARD_LETTERS,
WAW,
YAA,
} from '../constants/arabic-letters';
import { fillDefaultOptions, type OldArabicOptions } from '../options';
import { setCharAt, similarityScore } from '../utils';
} from '../constants/arabic-letters.js';
import { fillDefaultOptions, type OldArabicOptions } from '../options.js';
import { setCharAt, similarityScore } from '../utils/index.js';

/**
* Remove all tashkeel from text
Expand Down
2 changes: 1 addition & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './string.util';
export * from './string.util.js';
47 changes: 47 additions & 0 deletions tests/tree-shaking.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import ArabicServicesDefault, {
ArabicServices,
removeTashkeel,
removeTatweel,
toOldArabic,
TASHKEEL,
setCharAt,
} from '../dist';

describe('Tree Shaking & Named Exports', () => {
it('should support direct named export of functions', () => {
expect(typeof removeTashkeel).toBe('function');
expect(typeof removeTatweel).toBe('function');
expect(typeof toOldArabic).toBe('function');

const text = 'الخَيْلُ وَاللّيْلُ وَالبَيْداءُ تَعرِفُني';
expect(removeTashkeel(text)).toBe('الخيل والليل والبيداء تعرفني');
});

it('should support direct named export of constants and utilities', () => {
expect(Array.isArray(TASHKEEL)).toBe(true);
expect(typeof setCharAt).toBe('function');
});

it('should maintain full backward compatibility via ArabicServices object', () => {
expect(typeof ArabicServices.removeTashkeel).toBe('function');
expect(ArabicServices.removeTashkeel('مَرْحَبًا')).toBe('مرحبا');
});

it('should support default export', () => {
expect(ArabicServicesDefault).toBeDefined();
expect(typeof ArabicServicesDefault.removeTashkeel).toBe('function');
expect(ArabicServicesDefault.removeTashkeel('مَرْحَبًا')).toBe('مرحبا');
});

it('should support native Node.js ESM import with named exports', () => {
const { execSync } = require('child_process');
const stdout = execSync(
'node --input-type=module -e "import(\'./dist/esm/index.js\').then(m => console.log(JSON.stringify({ hasRemoveTashkeel: typeof m.removeTashkeel, hasArabicServices: typeof m.ArabicServices, sampleResult: m.removeTashkeel(\'مَرْحَبًا\') })))"',
{ encoding: 'utf8' }
);
const result = JSON.parse(stdout);
expect(result.hasRemoveTashkeel).toBe('function');
expect(result.hasArabicServices).toBe('object');
expect(result.sampleResult).toBe('مرحبا');
});
});
9 changes: 9 additions & 0 deletions tsconfig.esm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "node",
"target": "ES2020",
"outDir": "./dist/esm"
}
}
6 changes: 6 additions & 0 deletions webpack.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ module.exports = [
],
},
resolve: {
extensionAlias: {
'.js': ['.ts', '.js'],
},
extensions: ['.tsx', '.ts', '.js'],
},
output: {
Expand All @@ -41,6 +44,9 @@ module.exports = [
],
},
resolve: {
extensionAlias: {
'.js': ['.ts', '.js'],
},
extensions: ['.tsx', '.ts', '.js'],
},
output: {
Expand Down
Loading