diff --git a/.github/workflows/javascript.yml b/.github/workflows/javascript.yml index ec1c01176..847fd9751 100644 --- a/.github/workflows/javascript.yml +++ b/.github/workflows/javascript.yml @@ -42,6 +42,21 @@ jobs: npm install npm run test-typescript + commonjs_test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version: '18' + cache-dependency-path: ./package-lock.json + cache: 'npm' + - name: Run CommonJS example + run: | + npm install + npm run test-commonjs + integration_tests: uses: optimizely/javascript-sdk/.github/workflows/integration_test.yml@master secrets: diff --git a/examples/node-commonjs/index.js b/examples/node-commonjs/index.js new file mode 100644 index 000000000..4a1ee5260 --- /dev/null +++ b/examples/node-commonjs/index.js @@ -0,0 +1,84 @@ +/** + * Copyright 2026, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const fs = require('fs'); +const path = require('path'); + +const { + createInstance, + createStaticProjectConfigManager, + createForwardingEventProcessor, + createLogger, + createErrorNotifier, + INFO, +} = require('@optimizely/optimizely-sdk'); + +const datafilePath = path.join(__dirname, '..', 'shared', 'datafile.json'); +const testDatafile = fs.readFileSync(datafilePath, 'utf8'); + +const TIMEOUT_MS = 10000; + +async function testCommonJsRequire() { + let resolveUuid; + const uuidPromise = new Promise((resolve, reject) => { + resolveUuid = resolve; + setTimeout(() => reject(new Error('Timed out waiting for event dispatch')), TIMEOUT_MS); + }); + + const dispatcher = { + dispatchEvent(logEvent) { + const uuid = logEvent.params.visitors[0].snapshots[0].events[0].uuid; + console.log(`Dispatched event with uuid: ${uuid}`); + + if (typeof uuid === 'string' && uuid.length > 0) { + resolveUuid(uuid); + } + + return Promise.resolve({}); + }, + }; + + const configManager = createStaticProjectConfigManager({ + datafile: testDatafile, + }); + + const eventProcessor = createForwardingEventProcessor(dispatcher); + + const client = createInstance({ + projectConfigManager: configManager, + eventProcessor: eventProcessor, + }); + + await client.onReady(); + + const userContext = client.createUserContext('test_user', { age: 22 }); + userContext.decide('flag_1'); + + const uuid = await uuidPromise; + console.log(`Test passed: event contained valid uuid "${uuid}"`); + + client.close(); +} + +testCommonJsRequire() + .then(() => { + console.log('\n=== CommonJS example completed successfully! ===\n'); + process.exit(0); + }) + .catch((error) => { + console.error('Test failed:', error); + process.exit(1); + }); diff --git a/examples/node-commonjs/package.json b/examples/node-commonjs/package.json new file mode 100644 index 000000000..ef436bad9 --- /dev/null +++ b/examples/node-commonjs/package.json @@ -0,0 +1,9 @@ +{ + "name": "optimizely-sdk-commonjs-example", + "version": "1.0.0", + "private": true, + "description": "CommonJS example for Optimizely SDK - verifies CJS compatibility", + "scripts": { + "start": "node index.js" + } +} diff --git a/examples/typescript/datafile-server.js b/examples/shared/datafile-server.js similarity index 90% rename from examples/typescript/datafile-server.js rename to examples/shared/datafile-server.js index d6984c1d3..58ffced11 100755 --- a/examples/typescript/datafile-server.js +++ b/examples/shared/datafile-server.js @@ -21,14 +21,9 @@ * Runs at http://localhost:PORT */ -import http from 'http'; -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import { dirname } from 'path'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); +const http = require('http'); +const fs = require('fs'); +const path = require('path'); const PORT = 8910; const datafilePath = path.join(__dirname, 'datafile.json'); diff --git a/examples/typescript/datafile.json b/examples/shared/datafile.json similarity index 100% rename from examples/typescript/datafile.json rename to examples/shared/datafile.json diff --git a/examples/typescript/.gitignore b/examples/typescript/.gitignore index 5d3055a0d..dd6e803c7 100644 --- a/examples/typescript/.gitignore +++ b/examples/typescript/.gitignore @@ -2,4 +2,3 @@ node_modules/ dist/ *.log .DS_Store -package-lock.json diff --git a/examples/typescript/README.md b/examples/typescript/README.md index cc4430e0f..918f71624 100644 --- a/examples/typescript/README.md +++ b/examples/typescript/README.md @@ -5,8 +5,6 @@ This example demonstrates all factory functions from the Optimizely SDK, includi ## Files - `src/index.ts` - Main example demonstrating all SDK factory functions -- `datafile.json` - Test datafile with Optimizely configuration -- `datafile-server.js` - Simple HTTP server for testing polling datafile manager ## Running the Example diff --git a/examples/typescript/package-lock.json b/examples/typescript/package-lock.json new file mode 100644 index 000000000..907f416c9 --- /dev/null +++ b/examples/typescript/package-lock.json @@ -0,0 +1,41 @@ +{ + "name": "optimizely-sdk-typescript-example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "optimizely-sdk-typescript-example", + "version": "1.0.0", + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^4.7.4" + } + }, + "node_modules/@types/node": { + "version": "20.19.28", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "dev": true, + "license": "MIT" + } + } +} diff --git a/examples/typescript/src/index.ts b/examples/typescript/src/index.ts index 1681d245b..85d9655d0 100644 --- a/examples/typescript/src/index.ts +++ b/examples/typescript/src/index.ts @@ -50,7 +50,7 @@ import { fileURLToPath } from 'url'; const __filename: string = fileURLToPath(import.meta.url); const __dirname: string = dirname(__filename); -const datafilePath: string = path.join(__dirname, '..', 'datafile.json'); +const datafilePath: string = path.join(__dirname, '..', '..', 'shared', 'datafile.json'); const testDatafile: string = fs.readFileSync(datafilePath, 'utf8'); async function testStaticConfigSetup(): Promise { diff --git a/lib/tests/uuid_cjs_shim.js b/lib/tests/uuid_cjs_shim.js new file mode 100644 index 000000000..117f990da --- /dev/null +++ b/lib/tests/uuid_cjs_shim.js @@ -0,0 +1,22 @@ +/** + * CJS require hook that intercepts `require('uuid')` to provide a CJS-compatible + * shim. Needed because uuid v13+ is ESM-only and cannot be loaded via require(). + * Uses Node's built-in crypto.randomUUID() which produces identical RFC 4122 v4 UUIDs. + */ +const Module = require('module'); // eslint-disable-line @typescript-eslint/no-var-requires +const crypto = require('crypto'); // eslint-disable-line @typescript-eslint/no-var-requires + +const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + +const uuidShim = { + v4: () => crypto.randomUUID(), + validate: (str) => typeof str === 'string' && UUID_REGEX.test(str), +}; + +const originalLoad = Module._load; +Module._load = function (request, parent, isMain) { + if (request === 'uuid') { + return uuidShim; + } + return originalLoad.call(this, request, parent, isMain); +}; diff --git a/package-lock.json b/package-lock.json index 57da2656f..e58033959 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "decompress-response": "^7.0.0", "json-schema": "^0.4.0", "murmurhash": "^2.0.1", - "uuid": "^10.0.0" + "uuid": "^13.0.2" }, "devDependencies": { "@react-native-async-storage/async-storage": "^2", @@ -26,7 +26,6 @@ "@types/nise": "^1.4.0", "@types/node": "^18.7.18", "@types/ua-parser-js": "^0.7.36", - "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^5.33.0", "@typescript-eslint/parser": "^5.33.0", "@vitest/browser": "3.2.4", @@ -56,7 +55,7 @@ "rollup": "^4.55.1", "sinon": "^2.3.1", "ts-loader": "^9.3.1", - "ts-node": "^8.10.2", + "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "tslib": "^2.8.1", "typescript": "^4.7.4", @@ -634,6 +633,28 @@ "node": ">=0.1.90" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -2628,6 +2649,30 @@ "dev": true, "license": "MIT" }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -2845,13 +2890,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/whatwg-mimetype": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", @@ -3822,6 +3860,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -5238,6 +5288,12 @@ "node": ">= 14" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -13045,29 +13101,46 @@ } }, "node_modules/ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", "arg": "^4.1.0", + "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.17", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "bin": { "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" }, - "engines": { - "node": ">=6.0.0" - }, "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, "node_modules/ts-node/node_modules/diff": { @@ -13332,18 +13405,23 @@ } }, "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz", + "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist-node/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 8c548407a..70a798480 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "test-umd": "node ./scripts/run-umd-tests.js", "test-umd-local": "USE_LOCAL_BROWSER=true node ./scripts/run-umd-tests.js", "test-umd-browserstack": "USE_LOCAL_BROWSER=false node ./scripts/run-umd-tests.js", - "test-mocha": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register -r tsconfig-paths/register -r lib/tests/exit_on_unhandled_rejection.js 'lib/**/*.tests.ts' 'lib/**/*.tests.js'", + "test-mocha": "mocha -r lib/tests/uuid_cjs_shim.js -r ts-node/register -r tsconfig-paths/register -r lib/tests/exit_on_unhandled_rejection.js 'lib/**/*.tests.ts' 'lib/**/*.tests.js'", "test": "npm run test-mocha && npm run test-vitest", "posttest": "npm run lint", "test-ci": "npm run test-xbrowser && npm run test-umdbrowser", @@ -82,7 +82,8 @@ "prepare": "npm run build", "prepublishOnly": "npm test", "genmsg": "jiti message_generator ./lib/message/error_message.ts ./lib/message/log_message.ts", - "test-typescript": "node ./scripts/run-ts-example.js" + "test-typescript": "node ./scripts/run-ts-example.js", + "test-commonjs": "node ./scripts/run-cjs-example.js" }, "repository": { "type": "git", @@ -103,7 +104,7 @@ "decompress-response": "^7.0.0", "json-schema": "^0.4.0", "murmurhash": "^2.0.1", - "uuid": "^10.0.0" + "uuid": "^13.0.2" }, "devDependencies": { "@react-native-async-storage/async-storage": "^2", @@ -117,7 +118,6 @@ "@types/nise": "^1.4.0", "@types/node": "^18.7.18", "@types/ua-parser-js": "^0.7.36", - "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^5.33.0", "@typescript-eslint/parser": "^5.33.0", "@vitest/browser": "3.2.4", @@ -147,7 +147,7 @@ "rollup": "^4.55.1", "sinon": "^2.3.1", "ts-loader": "^9.3.1", - "ts-node": "^8.10.2", + "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "tslib": "^2.8.1", "typescript": "^4.7.4", diff --git a/rollup.config.mjs b/rollup.config.mjs index 020778d8f..fe3df3d85 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -28,15 +28,22 @@ const __dirname = dirname(__filename); const require = createRequire(import.meta.url); const { dependencies, peerDependencies } = require('./package.json'); -const resolvePlugin = nodeResolve({ - extensions: ['.js'] +const resolvePlugin = nodeResolve(); + +const resolvePluginNode = nodeResolve({ + exportConditions: ['node'], }); const resolvePluginBrowser = nodeResolve({ browser: true, - extensions: ['.js'] + exportConditions: ['browser'], }); +const resolvePluginByPlatform = { + node: resolvePluginNode, + browser: resolvePluginBrowser, +}; + const aliasPlugin = alias({ entries: [ { find: 'error_message', replacement: join(__dirname, '.build/message/error_message.gen.js') }, @@ -44,6 +51,13 @@ const aliasPlugin = alias({ ] }); +const externalDeps = ['https', 'http', 'url'].concat(Object.keys({ ...dependencies, ...peerDependencies } || {})); + +// uuid@13 is ESM-only and does not provide a CommonJS export. +// Since our CJS bundles must work in CommonJS environments, we bundle uuid +// into the CJS output instead of leaving it as an external dependency. +const externalDepsWithoutUuid = externalDeps.filter(dep => dep !== 'uuid'); + const cjsBundleFor = (platform, opt = {}) => { const { minify, ext } = { minify: true, @@ -54,8 +68,8 @@ const cjsBundleFor = (platform, opt = {}) => { const min = minify ? '.min' : ''; return { - plugins: [aliasPlugin, resolvePlugin, commonjs()], - external: ['https', 'http', 'url'].concat(Object.keys({ ...dependencies, ...peerDependencies } || {})), + plugins: [aliasPlugin, resolvePluginByPlatform[platform] || resolvePlugin, commonjs()], + external: externalDepsWithoutUuid, input: `.build/index.${platform}.js`, output: { exports: 'named', @@ -78,6 +92,7 @@ const esmBundleFor = (platform, opt) => { return { ...cjsBundleFor(platform), + external: externalDeps, output: [ { format: 'es', @@ -100,7 +115,7 @@ const cjsBundleForUAParser = (opt = {}) => { return { plugins: [aliasPlugin, resolvePlugin, commonjs()], - external: ['https', 'http', 'url'].concat(Object.keys({ ...dependencies, ...peerDependencies } || {})), + external: externalDepsWithoutUuid, input: `.build/odp/ua_parser/ua_parser.js`, output: { exports: 'named', @@ -123,6 +138,7 @@ const esmBundleForUAParser = (opt = {}) => { return { ...cjsBundleForUAParser(), + external: externalDeps, output: [ { format: 'es', @@ -162,7 +178,7 @@ const umdBundle = { // A separate bundle for json schema validator. const jsonSchemaBundle = { plugins: [aliasPlugin, resolvePlugin, commonjs()], - external: ['json-schema', 'uuid'], + external: ['json-schema'], input: '.build/utils/json_schema_validator/index.js', output: { exports: 'named', diff --git a/scripts/run-cjs-example.js b/scripts/run-cjs-example.js new file mode 100644 index 000000000..fa213d54c --- /dev/null +++ b/scripts/run-cjs-example.js @@ -0,0 +1,80 @@ +#!/usr/bin/env node + +/** + * Copyright 2026, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); + +const rootDir = path.join(__dirname, '..'); +const exampleDir = path.join(rootDir, 'examples', 'node-commonjs'); + +function run(command, cwd) { + console.log(`\n> ${command}`); + console.log(` (in ${cwd})\n`); + try { + execSync(command, { + cwd, + stdio: 'inherit', + shell: true + }); + } catch (error) { + console.error(`\nError executing: ${command}`); + process.exit(1); + } +} + +function runQuiet(command, cwd) { + try { + const output = execSync(command, { + cwd, + encoding: 'utf8', + shell: true + }).trim(); + const lines = output.split('\n'); + return lines[lines.length - 1].trim(); + } catch (error) { + console.error(`\nError executing: ${command}`); + process.exit(1); + } +} + +function main() { + console.log('=== Building SDK and Running CommonJS Example ===\n'); + + console.log('Installing SDK dependencies...'); + run('npm install', rootDir); + + console.log('\nPacking SDK tarball...'); + const packOutput = runQuiet('npm pack', rootDir); + const tarballPath = path.join(rootDir, packOutput); + console.log(`Created: ${packOutput}`); + + console.log('\nInstalling SDK tarball in cjs-example...'); + run(`npm install --no-save --no-package-lock ${tarballPath}`, exampleDir); + + console.log('\nRunning cjs-example...'); + run('npm start', exampleDir); + + console.log('\nCleaning up tarball...'); + fs.unlinkSync(tarballPath); + console.log(`Removed: ${packOutput}`); + + console.log('\n=== Example completed successfully! ===\n'); +} + +main(); diff --git a/scripts/run-ts-example.js b/scripts/run-ts-example.js index 32c7f7ec6..ee1c7255d 100755 --- a/scripts/run-ts-example.js +++ b/scripts/run-ts-example.js @@ -62,7 +62,7 @@ function startDatafileServer() { console.log('\n=== Starting Datafile Server ==='); console.log('Starting server at http://localhost:8910...\n'); - const serverPath = path.join(exampleDir, 'datafile-server.js'); + const serverPath = path.join(rootDir, 'examples', 'shared', 'datafile-server.js'); datafileServer = spawn('node', [serverPath], { stdio: 'inherit', detached: false @@ -114,7 +114,7 @@ async function main() { run('npm install', exampleDir); console.log('\nInstalling SDK tarball in ts-example...'); - run(`npm install --no-save ${tarballPath}`, exampleDir); + run(`npm install --no-save --no-package-lock ${tarballPath}`, exampleDir); console.log('\nBuilding ts-example...'); run('npm run build', exampleDir); diff --git a/tsconfig.json b/tsconfig.json index b3d1744e4..25be5af80 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,6 +33,12 @@ "skipLibCheck": true, "useUnknownInCatchVariables": false }, + "ts-node": { + "compilerOptions": { + "module": "commonjs" + }, + "transpileOnly": true + }, "exclude": [ "./dist", "./.build",