@questwork/q-utilities 0.1.30 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,217 @@
1
+ # Best Practices for Questwork Model Projects
2
+
3
+ This document outlines the best practices for developing and maintaining Questwork model projects. It is intended to guide both AI agents and developers on key principles and considerations.
4
+
5
+ ## 1. Code Structure
6
+
7
+ ### 1.1 File Organization
8
+ - **Models**: Place all data models in `lib/models/` directory
9
+ - **Constants**: Place all constant definitions in `lib/constants/` directory
10
+ - **Helpers**: Place utility functions in `lib/helpers/` directory
11
+ - **Tests**: Place unit tests next to the corresponding model files with `.spec.ts` extension
12
+
13
+ ### 1.2 Export Patterns
14
+ - Place all exports at the bottom of the file
15
+ - Separate named exports from type exports
16
+ - Use clear, consistent export patterns
17
+
18
+ Example:
19
+ ```typescript
20
+ export {
21
+ BaseModel
22
+ }
23
+
24
+ export type {
25
+ BaseModelOptions, BaseModelUpdate
26
+ }
27
+ ```
28
+
29
+ ## 2. TypeScript Best Practices
30
+
31
+ ### 2.1 Type Definitions
32
+ - Use interfaces for complex type definitions
33
+ - Use `as const` for constant objects to create literal types
34
+ - Define types for all function parameters and return values
35
+
36
+ ### 2.2 Coding Style
37
+ - **Semi-colons**: Do not use semi-colons at the end of lines
38
+ - **Naming Conventions**:
39
+ - Constants: `CONSTANT_CASE`
40
+ - Interfaces: `PascalCase`
41
+ - Classes: `PascalCase`
42
+ - Methods and functions: `camelCase`
43
+ - Properties: `camelCase`
44
+ - Private properties: `_underscorePrefix`
45
+ - **Ordering**:
46
+ - Place `[key: string]: any` at the top of class definitions
47
+ - Order class properties in alphabetical order
48
+ - Order class methods in alphabetical order
49
+
50
+ ### 2.3 Model Patterns
51
+ - Extend appropriate base classes for shared functionality
52
+ - Use `init()` method for object creation
53
+ - Include static methods for testing purposes
54
+ - Define arrays to restrict updatable fields
55
+ - **Unique Identifiers**:
56
+ - Use `xxxCode` as the primary unique ID
57
+ - Generate unique codes using appropriate utility functions
58
+ - Maintain compatibility with different ID formats
59
+
60
+ ## 3. Business Logic
61
+
62
+ ### 3.1 Data Integrity
63
+ - **Immutable Fields**: Once created, most fields should not be updatable
64
+ - **Unique Identifiers**: Use consistent patterns for generating unique codes
65
+ - **Timestamps**: Use `Date.now()` for timestamps
66
+
67
+ ### 3.2 Separation of Concerns
68
+ - **Data Models**: Should only contain data structure and basic validation
69
+ - **Repository Logic**: Should be implemented in the application layer
70
+ - **Business Logic**: Should be implemented in the application layer
71
+
72
+ ## 4. Testing
73
+
74
+ ### 4.1 Test Patterns
75
+ - Use Vitest for unit testing
76
+ - Place test files next to the corresponding model files
77
+ - Use descriptive test names
78
+ - Set test values to match field names (e.g., `myCode: 'myCode'`)
79
+ - Test both success and failure cases
80
+
81
+ ### 4.2 Test Coverage
82
+ - Test model creation
83
+ - Test field validation
84
+ - Test update functionality
85
+ - Test edge cases
86
+
87
+ ## 5. Build Process
88
+
89
+ ### 5.1 TypeScript Configuration
90
+ - Use `tsconfig.json` for TypeScript configuration
91
+ - Generate declaration files (`*.d.ts`) for type hints
92
+ - Use appropriate plugins to generate declaration files during build
93
+
94
+ ### 5.2 Package Configuration
95
+ - Configure `package.json` exports for different module formats
96
+ - Include proper type definitions in exports
97
+ - Use `pnpm` for package management
98
+
99
+ ### 5.3 Build Output File Extensions
100
+ - **ESM**: `.esm.js` (ECMAScript Module)
101
+ - **CJS**: `.cjs` (CommonJS Module)
102
+ - **UMD**: `.umd.js` (Universal Module Definition)
103
+ - **IIFE**: `.iife.js` (Immediately Invoked Function Expression)
104
+
105
+ Example file names for a project named "project-name":
106
+ - `project-name.esm.js`
107
+ - `project-name.cjs`
108
+ - `project-name.umd.js`
109
+ - `project-name.iife.js`
110
+
111
+ ## 6. Dependency Management
112
+
113
+ ### 6.1 External Dependencies
114
+ - Use shared utility packages for common functionality
115
+ - Import utilities directly from packages
116
+
117
+ ### 6.2 Internal Dependencies
118
+ - Use relative paths for internal imports
119
+ - Import constants from `../../constants`
120
+ - Organize imports logically
121
+
122
+ ## 7. Constants
123
+
124
+ ### 7.1 Constant Organization
125
+ - Place all constants in `lib/constants/` directory
126
+ - Group constants by domain
127
+ - Export constants from a central index file
128
+
129
+ ### 7.2 Constant Usage
130
+ - Use constants instead of hardcoded strings
131
+ - Maintain consistent naming conventions for constants
132
+
133
+ Example:
134
+ ```typescript
135
+ // Instead of using hardcoded strings:
136
+ if (status === 'active') {
137
+ // logic
138
+ }
139
+
140
+ // Use constants:
141
+ const STATUS = {
142
+ ACTIVE: 'active',
143
+ INACTIVE: 'inactive',
144
+ PENDING: 'pending'
145
+ }
146
+
147
+ if (status === STATUS.ACTIVE) {
148
+ // logic
149
+ }
150
+ ```
151
+
152
+ ## 8. Error Handling
153
+
154
+ ### 8.1 Validation
155
+ - Implement validation logic in model classes
156
+ - Use consistent patterns for indicating invalid models
157
+ - Use `isValid` property for model validation
158
+ - Return null from `init()` method for invalid models
159
+
160
+ ### 8.2 Error Reporting
161
+ - Use descriptive error messages
162
+ - Log errors appropriately
163
+ - Handle edge cases gracefully
164
+
165
+ ## 9. Performance
166
+
167
+ ### 9.1 Code Optimization
168
+ - Use efficient algorithms
169
+ - Avoid unnecessary computations
170
+ - Use proper data structures
171
+
172
+ ### 9.2 Build Optimization
173
+ - Use Vite for fast builds
174
+ - Configure build options for different module formats
175
+ - Generate optimized bundles
176
+
177
+ ## 10. Documentation
178
+
179
+ ### 10.1 Code Comments
180
+ - Add comments for complex logic
181
+ - Document public methods and properties
182
+ - Explain design decisions
183
+
184
+ ### 10.2 Project Documentation
185
+ - Maintain this best practices document
186
+ - Update documentation when adding new features
187
+ - Document breaking changes
188
+
189
+ ## 11. Build Tool Migration (Optional)
190
+
191
+ This section is only necessary for projects with existing Webpack, Gulp, or Mocha setups. If you're starting a new project, you can skip this section and directly use Vite and Vitest.
192
+
193
+ ### 11.1 Considerations for Migration
194
+
195
+ #### Key Steps
196
+ - Update package.json to remove Webpack, Gulp, and Mocha dependencies
197
+ - Add Vite, Vitest, and related plugin dependencies
198
+ - Update scripts to use Vite for building and Vitest for testing
199
+ - Configure build output formats and type definitions
200
+ - Remove Webpack and Gulp configuration files
201
+ - Verify the migration works correctly
202
+
203
+ #### Benefits of Migrating to Vite and Vitest
204
+ - Faster build times compared to Webpack
205
+ - Modern, actively maintained tooling ecosystem
206
+ - Simplified configuration compared to Webpack
207
+ - Better TypeScript support
208
+ - Integrated testing with Vitest
209
+ - Consistency across Questwork projects
210
+
211
+ ### 11.2 Core Configuration Elements
212
+
213
+ When setting up Vite and Vitest, ensure to include:
214
+ - Build configuration for multiple output formats (ESM, CJS, UMD, IIFE)
215
+ - Type declaration file generation
216
+ - Test configuration with appropriate environment setup
217
+ - Proper external dependency handling
@@ -0,0 +1,321 @@
1
+ # Building a JavaScript + TypeScript Library
2
+
3
+ This guide outlines the best practices for building a library that supports both JavaScript and TypeScript, based on the approaches used in the `qw.q.utilities` and `qw.q.ecoffice.model` projects.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Project Setup](#project-setup)
8
+ 2. [Code Structure](#code-structure)
9
+ 3. [TypeScript Configuration](#typescript-configuration)
10
+ 4. [Build Process](#build-process)
11
+ 5. [Package Configuration](#package-configuration)
12
+ 6. [Testing](#testing)
13
+ 7. [Publishing](#publishing)
14
+ 8. [Best Practices Reference](#best-practices-reference)
15
+
16
+ ## 1. Project Setup
17
+
18
+ ### 1.1 Initialize Project
19
+
20
+ ```bash
21
+ # Create project directory
22
+ mkdir my-library
23
+ cd my-library
24
+
25
+ # Initialize with pnpm
26
+ pnpm init
27
+
28
+ # Install dependencies
29
+ pnpm add -D typescript vite vitest vite-plugin-dts
30
+ ```
31
+
32
+ ### 1.2 Directory Structure
33
+
34
+ ```
35
+ my-library/
36
+ ├── lib/ # Source code
37
+ │ ├── helpers/ # Utility functions
38
+ │ ├── models/ # Data models
39
+ │ ├── index.ts # Main entry point
40
+ ├── dist/ # Build output
41
+ ├── package.json # Package configuration
42
+ ├── tsconfig.json # TypeScript configuration
43
+ ├── vite.config.js # Vite build configuration
44
+ ├── vitest.config.js # Vitest configuration
45
+ ```
46
+
47
+ ## 2. Code Structure
48
+
49
+ ### 2.1 Main Entry Point
50
+
51
+ Create a TypeScript entry point that exports all modules:
52
+
53
+ ```typescript
54
+ // lib/index.ts
55
+ export * from './helpers/index.js';
56
+ export * from './models/index.js';
57
+ ```
58
+
59
+ ### 2.2 Export Patterns
60
+
61
+ - Place all exports at the bottom of the file
62
+ - Separate named exports from type exports
63
+ - Use consistent naming conventions
64
+
65
+ ## 3. TypeScript Configuration
66
+
67
+ ### 3.1 tsconfig.json
68
+
69
+ ```json
70
+ {
71
+ "compilerOptions": {
72
+ "target": "ES2020",
73
+ "module": "ESNext",
74
+ "lib": ["ES2020"],
75
+ "allowJs": true,
76
+ "checkJs": false,
77
+ "jsx": "preserve",
78
+ "declaration": true,
79
+ "declarationMap": true,
80
+ "sourceMap": true,
81
+ "outDir": "./dist",
82
+ "rootDir": "./lib",
83
+ "strict": false,
84
+ "moduleResolution": "node",
85
+ "allowSyntheticDefaultImports": true,
86
+ "esModuleInterop": true,
87
+ "skipLibCheck": true,
88
+ "forceConsistentCasingInFileNames": true
89
+ },
90
+ "include": [
91
+ "lib/**/*"
92
+ ],
93
+ "exclude": [
94
+ "node_modules",
95
+ "dist"
96
+ ]
97
+ }
98
+ ```
99
+
100
+ ## 4. Build Process
101
+
102
+ ### 4.1 Vite Configuration
103
+
104
+ ```javascript
105
+ // vite.config.js
106
+ import { defineConfig } from 'vite';
107
+ import path from 'path';
108
+ import dts from 'vite-plugin-dts';
109
+
110
+ export default defineConfig({
111
+ plugins: [
112
+ dts({
113
+ include: ['lib/**/*.js', 'lib/**/*.ts'],
114
+ exclude: ['**/*.spec.js', '**/*.spec.ts'],
115
+ rollupTypes: true,
116
+ outDir: 'dist',
117
+ rootDir: 'lib',
118
+ }),
119
+ ],
120
+ build: {
121
+ outDir: 'dist',
122
+ sourcemap: true,
123
+ lib: {
124
+ entry: path.resolve(__dirname, 'lib/index.ts'),
125
+ name: 'MyLibrary',
126
+ fileName: (format) => {
127
+ if (format === 'es') return 'my-library.esm.js';
128
+ if (format === 'cjs') return 'my-library.cjs';
129
+ if (format === 'umd') return 'my-library.umd.js';
130
+ return `my-library.${format}.js`;
131
+ },
132
+ },
133
+ rollupOptions: {
134
+ external: [], // Add external dependencies here
135
+ output: [
136
+ {
137
+ format: 'es',
138
+ generatedCode: 'es2015',
139
+ exports: 'named',
140
+ },
141
+ {
142
+ format: 'cjs',
143
+ exports: 'named',
144
+ },
145
+ {
146
+ format: 'umd',
147
+ name: 'MyLibrary',
148
+ exports: 'named',
149
+ },
150
+ {
151
+ format: 'iife',
152
+ name: 'MyLibrary',
153
+ exports: 'named',
154
+ },
155
+ ],
156
+ },
157
+ },
158
+ test: {
159
+ environment: 'node',
160
+ coverage: {
161
+ reporter: ['text', 'html'],
162
+ },
163
+ globals: true,
164
+ setupFiles: ['lib/test.setup.js'],
165
+ mocha: {
166
+ ui: 'bdd',
167
+ },
168
+ },
169
+ });
170
+ ```
171
+
172
+ ### 4.2 Build Scripts
173
+
174
+ Add build scripts to `package.json`:
175
+
176
+ ```json
177
+ {
178
+ "scripts": {
179
+ "build": "vite build",
180
+ "test": "vitest run"
181
+ }
182
+ }
183
+ ```
184
+
185
+ ## 5. Package Configuration
186
+
187
+ ### 5.1 package.json
188
+
189
+ ```json
190
+ {
191
+ "name": "my-library",
192
+ "version": "1.0.0",
193
+ "description": "My JavaScript + TypeScript library",
194
+ "type": "module",
195
+ "exports": {
196
+ ".": {
197
+ "require": "./dist/my-library.cjs",
198
+ "import": "./dist/my-library.esm.js",
199
+ "default": "./dist/my-library.umd.js",
200
+ "types": "./dist/my-library.d.ts"
201
+ }
202
+ },
203
+ "types": "./dist/my-library.d.ts",
204
+ "main": "./dist/my-library.cjs",
205
+ "module": "./dist/my-library.esm.js",
206
+ "unpkg": "./dist/my-library.umd.js",
207
+ "files": [
208
+ "dist"
209
+ ],
210
+ "scripts": {
211
+ "build": "vite build",
212
+ "test": "vitest run"
213
+ },
214
+ "devDependencies": {
215
+ "typescript": "^5.9.3",
216
+ "vite": "^5.4.10",
217
+ "vite-plugin-dts": "^4.5.4",
218
+ "vitest": "^2.1.4"
219
+ }
220
+ }
221
+ ```
222
+
223
+ ## 6. Testing
224
+
225
+ ### 6.1 Test Setup
226
+
227
+ Use Vitest for testing. Place test files next to the corresponding source files with `.spec.ts` extension:
228
+
229
+ ```typescript
230
+ // lib/models/myModel.spec.ts
231
+ import { expect } from 'chai';
232
+ import { MyModel } from './myModel.js';
233
+
234
+ describe('MyModel', () => {
235
+ it('should create a valid model', () => {
236
+ const model = MyModel.init({ name: 'Test' });
237
+ expect(model.isValid).to.be.true;
238
+ });
239
+ });
240
+ ```
241
+
242
+ ## 7. Publishing
243
+
244
+ ### 7.1 Build and Publish
245
+
246
+ ```bash
247
+ # Build the library
248
+ pnpm run build
249
+
250
+ # Publish to npm
251
+ pnpm publish
252
+ ```
253
+
254
+ ## 8. Best Practices Reference
255
+
256
+ ### 8.1 From qw.q.ecoffice.model/best_practices.md
257
+
258
+ #### 8.1.1 Build Output File Extensions
259
+
260
+ - **ESM**: `.esm.js` (ECMAScript Module)
261
+ - **CJS**: `.cjs` (CommonJS Module)
262
+ - **UMD**: `.umd.js` (Universal Module Definition)
263
+ - **IIFE**: `.iife.js` (Immediately Invoked Function Expression)
264
+
265
+ #### 8.1.2 TypeScript Configuration
266
+ - Use `tsconfig.json` for TypeScript configuration
267
+ - Generate declaration files (`*.d.ts`) for type hints
268
+ - Use `vite-plugin-dts` to generate declaration files during build
269
+
270
+ #### 8.1.3 Package Configuration
271
+ - Configure `package.json` exports for different module formats
272
+ - Include proper type definitions in exports
273
+ - Use `pnpm` for package management
274
+
275
+ #### 8.1.4 Code Structure
276
+ - **Models**: Place all data models in `lib/models/` directory
277
+ - **Helpers**: Place utility functions in `lib/helpers/` directory
278
+ - **Tests**: Place unit tests next to the corresponding model files with `.spec.ts` extension
279
+
280
+ #### 8.1.5 Testing
281
+ - Use Vitest for unit testing
282
+ - Place test files next to the corresponding model files
283
+ - Use descriptive test names
284
+ - Test both success and failure cases
285
+
286
+ ## 9. Example Projects
287
+
288
+ - **qw.q.utilities**: A utility library with TypeScript support
289
+ - **qw.q.ecoffice.model**: A data model library with TypeScript support
290
+
291
+ ## 10. Troubleshooting
292
+
293
+ ### 10.1 Common Issues
294
+
295
+ #### 10.1.1 Circular Dependencies
296
+
297
+ - Avoid circular dependencies by importing directly from specific files instead of index files
298
+ - Use forward references when necessary
299
+
300
+ #### 10.1.2 Declaration File Generation
301
+
302
+ - Ensure `vite-plugin-dts` is properly configured
303
+ - Use a TypeScript entry point for better type analysis
304
+ - Check that all exports are properly typed
305
+
306
+ #### 10.1.3 Module Resolution
307
+
308
+ - Use proper file extensions in imports (e.g., `.js` for ESM even when importing from `.ts` files)
309
+ - Configure `package.json` exports correctly for different module formats
310
+
311
+ ## 11. Conclusion
312
+
313
+ Building a JavaScript + TypeScript library requires careful configuration of TypeScript, Vite, and package.json. By following the best practices outlined in this guide and referencing the `qw.q.ecoffice.model` project, you can create a robust library that provides excellent type support for TypeScript users while remaining compatible with JavaScript users.
314
+
315
+ Key takeaways:
316
+ - Use TypeScript for the main entry point
317
+ - Configure Vite to generate multiple module formats
318
+ - Use `vite-plugin-dts` to generate declaration files
319
+ - Configure `package.json` exports for different module formats
320
+ - Include proper type definitions in the package configuration
321
+ - Test both TypeScript and JavaScript usage