Skip to content

Conversation

@fcoury
Copy link
Owner

@fcoury fcoury commented Jun 28, 2025

Summary

This PR implements the remaining npm improvement features for the Husk build system:

  • Task 4.1: Import Maps Support - Configure CDN URLs for packages in husk.toml
  • Task 4.2: Tree Shaking Hints - Add /*#__PURE__*/ annotations for better dead code elimination
  • Task 4.3: Development vs Production Mode - Different optimizations based on dev flag

Changes

1. Import Maps Support

  • Added import_map configuration to TargetConfig
  • Import maps allow mapping package names to CDN URLs
  • Example configuration:
    [targets.browser.import_map]
    react = "https://esm.sh/react@18"
    lodash = "https://cdn.skypack.dev/lodash"

2. Tree Shaking Hints

  • Added tree_shaking flag to TargetConfig
  • When enabled, adds /*#__PURE__*/ annotations to:
    • Constructor calls (e.g., new Date())
    • Object/JSON methods
    • Husk utility functions
  • Helps bundlers like Webpack/Rollup eliminate unused code

3. Development vs Production Mode

  • Added dev flag to TargetConfig
  • Development mode features:
    • Debug comments for variables and functions
    • Runtime type assertions with console warnings
    • Tree shaking disabled for better debugging
  • Production mode:
    • Clean output without debug artifacts
    • Tree shaking enabled
    • Optimized for performance

Testing

Added comprehensive integration tests in tests/integration/test_npm_features.sh that verify:

  • Import maps configuration
  • Tree shaking annotations
  • Development mode debug features
  • Production mode optimizations
  • Combined feature interactions

All tests pass locally and in CI.

Example Usage

# husk.toml
[targets.browser]
tree_shaking = true
dev = false

[targets.browser.import_map]
react = "https://esm.sh/react@18"

[targets.browser-dev]
tree_shaking = false
dev = true

🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

This PR implements a comprehensive set of npm improvement features for the Husk build system:

## Phase 1: Core NPM Integration Fixes
- ✅ Task 1.1: Make package.json generation default behavior in build command
- ✅ Task 1.2: Fix import path generation for npm packages
- ✅ Task 1.3: Implement consistent module format across targets
- ✅ Task 1.4: Fix package resolution for CommonJS/ESM interop

## Phase 2: Enhanced NPM Features
- ✅ Task 2.1: Enhanced exports field resolution for subpath imports
- ✅ Task 2.2: Better module type detection (ESM vs CommonJS)
- ✅ Task 2.3: TypeScript definitions support (.d.ts files)

## Phase 3: Advanced Build Features
- ✅ Task 3.1: Wire up target configuration from husk.toml
- ✅ Task 3.2: Platform-specific imports (browser/node/deno/bun)
- ✅ Task 3.3: External dependencies configuration

## Phase 4: Modern JavaScript Features
- ✅ Task 4.1: Import maps support for browser targets
- ✅ Task 4.2: Tree shaking hints with /*#__PURE__*/ annotations
- ✅ Task 4.3: Development vs Production mode

## Key Changes:
- Modified build command to generate package.json by default (use --skip-package-json to opt out)
- Added smart package.json regeneration that only updates when dependencies change
- Implemented proper CommonJS/ESM interop with require() vs import handling
- Added support for package.json exports field with subpath imports
- Implemented platform-specific code generation based on target
- Added external dependencies configuration to exclude packages from bundling
- Implemented import maps for browser targets
- Added tree shaking annotations for better dead code elimination
- Added development/production mode support with environment-specific optimizations

## Testing:
- Added comprehensive integration tests covering all npm features
- Tests run in both local and CI environments
- All features have been validated with real npm packages

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
fcoury and others added 4 commits June 27, 2025 21:46
Add dev, import_map, and tree_shaking fields to fix compilation error in tests.
When building a complete project (as opposed to just transpiling), the generated
JavaScript now automatically calls the main() function at the end of the file.
This ensures that Husk programs execute correctly when run with Node.js or in
the browser.

The transpiler now:
- Checks if a main() function is defined in the AST
- Adds a call to main() at the end of the generated JavaScript
- Includes a comment to make it clear this is an auto-generated call

This fixes the issue where built JavaScript files would define functions but
not execute anything when run.
- Add transpile_to_js_with_entry_validation function to validate main() in entry files
- Fix transpiler to call async main() functions (was only checking regular functions)
- Add comprehensive tests for entry validation and main() call generation
- Ensure non-entry files don't require main() function

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Add module format-aware export generation in transpiler
- Functions marked as 'pub' now export with CommonJS syntax when targeting node-cjs
- Add test-specific transpiler constructor for module format testing
- Enable and fix test_transpile_node_cjs_target test
- Ensure require() is used for Node.js built-ins in CommonJS mode

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@fcoury
Copy link
Owner Author

fcoury commented Jun 28, 2025

@claude review this PR please

@claude
Copy link
Contributor

claude bot commented Jun 28, 2025

Claude finished @fcoury's task —— View job


PR Review: NPM Improvements - Import Maps, Tree Shaking, and Dev/Prod Mode

I've completed a thorough review of this substantial PR implementing three key npm improvement features. Here's my comprehensive feedback:

Strengths

1. Code Quality & Architecture

  • Clean integration: New features are well-integrated into existing TargetConfig structure
  • Consistent patterns: Follows established patterns for configuration parsing and target handling
  • Proper defaults: Uses #[serde(default)] appropriately for new optional fields
  • Good separation: Features are implemented in separate, focused methods

2. Feature Implementation

Import Maps (config.rs:114, transpiler.rs:1141-1162)

  • ✅ Proper HashMap storage for package→URL mappings
  • ✅ Integration with existing package resolution system
  • ✅ Support for different import types (named, all, single)
  • ✅ Target-specific configuration

Tree Shaking (transpiler.rs:1520-1569)

  • ✅ Comprehensive pure function list (Math, Object, constructors, etc.)
  • ✅ Smart detection of pure operations
  • ✅ Correctly disabled in dev mode to preserve debugging
  • ✅ Standard /*#__PURE__*/ annotations for bundler compatibility

Dev/Prod Mode (transpiler.rs:1571-1603)

  • ✅ Debug comments for variables and functions
  • ✅ Runtime type assertions with helpful warnings
  • ✅ Conditional feature toggling (tree shaking disabled in dev)

3. Test Coverage

  • Excellent unit test coverage: Separate test files for each feature
  • Integration tests: Bash scripts testing real-world scenarios
  • Edge cases covered: Dev/prod interactions, feature combinations
  • Configuration testing: Various target configurations tested

⚠️ Areas for Improvement

1. Security Considerations

// src/transpiler.rs:1141-1162
fn generate_import_map_import(&self, mapped_specifier: &str, ...) -> Result<String> {
    // URL is used directly without validation
    Ok(format!("import * from '{}'", mapped_specifier))
}

Recommendation: Add URL validation for import map entries to prevent injection attacks or malformed URLs.

2. Repository Cleanup

The PR includes unnecessary files that should be cleaned up:

  • src/lib.rs.bak - Backup file
  • src/transpiler_target_modes_test.rs.bak - Backup file
  • Root-level test files: test_cjs.husk, test_no_main.husk, test_module.rs
  • Multiple test project directories in root

3. Performance Considerations

  • Import map lookups: HashMap lookups are O(1), but consider caching for hot paths
  • Pure annotation checking: String matching in should_add_pure_annotation could be optimized with a HashSet
  • Dev mode overhead: Runtime type assertions add overhead - consider compile-time flags

4. Documentation

  • The feature implementations are well-documented, but consider adding configuration examples to main README
  • Import map security considerations should be documented

🔧 Minor Issues

Error Handling

The code properly uses Result types and avoids unsafe patterns like unwrap(). Good defensive programming practices.

Module Format Consistency

Good integration with existing module format detection in determine_module_format.

📊 Test Quality Assessment

Unit Tests: ⭐⭐⭐⭐⭐

  • Comprehensive coverage of all features
  • Good test organization with separate files
  • Edge cases well covered

Integration Tests: ⭐⭐⭐⭐☆

  • Real-world scenarios tested
  • Bash scripts verify end-to-end functionality
  • Could benefit from more error condition testing

🚀 Overall Assessment

This is a high-quality implementation of important npm ecosystem features. The code is well-structured, thoroughly tested, and follows Rust best practices. The three features work together coherently and provide significant value for JavaScript ecosystem integration.

Recommendation: Approve with minor cleanup

Required Actions Before Merge:

  1. Remove backup files (.bak) and test artifacts from root directory
  2. Add URL validation for import map entries
  3. Consider documenting security considerations for import maps

Optional Improvements:

  1. Optimize pure annotation checking with HashSet
  2. Add configuration examples to documentation
  3. Consider caching for import map lookups in hot paths

The implementation successfully addresses the npm improvement goals and significantly enhances Husk's JavaScript ecosystem compatibility.


@claude
Copy link
Contributor

claude bot commented Jun 28, 2025

Claude finished @fcoury's task —— View job


PR Review: npm improvements - import maps, tree shaking, and dev/prod mode

Todo List:

  • Read repository guidelines (CLAUDE.md)
  • Review core configuration changes (config.rs)
  • Review transpiler implementation (transpiler.rs)
  • Review package resolver changes (package_resolver.rs)
  • Examine test coverage and quality
  • Check integration tests
  • Provide comprehensive review feedback

Overall Assessment

This is a well-implemented PR that adds significant value to the Husk build system. The three new features (import maps, tree shaking hints, and dev/prod mode) are properly integrated with comprehensive test coverage. The implementation follows good software engineering practices with proper separation of concerns.

✅ Strengths

1. Excellent Test Coverage

  • Dedicated test files for each feature (transpiler_import_maps_test.rs, transpiler_tree_shaking_test.rs, transpiler_dev_prod_test.rs)
  • Comprehensive integration tests (test_npm_features.sh) that test end-to-end functionality
  • Tests cover edge cases like disabled features, dev mode overrides, and different target platforms

2. Clean Configuration Integration

  • New fields properly added to TargetConfig in config.rs:114-118
  • Backward-compatible with #[serde(default)] annotations
  • Intelligent package.json generation includes "sideEffects": false when tree shaking is enabled (config.rs:329-332)

3. Robust Implementation

  • Import Maps: Proper resolution in visit_use() method with fallback to external deps and package resolution
  • Tree Shaking: Comprehensive list of pure functions (transpiler.rs:1520-1540) with smart conditions (disabled in dev mode)
  • Dev Mode: Helpful debug comments and runtime type assertions for better developer experience

4. Smart Feature Interactions

  • Tree shaking automatically disabled in development mode (good UX decision)
  • Import maps have proper precedence over external dependencies and package resolution
  • Platform-specific handling (Node.js vs Browser vs Deno)

🔍 Areas for Improvement

1. Minor: Tree Shaking Logic transpiler.rs:1520

The pure annotation logic could be more conservative. Some functions marked as pure might have side effects in certain contexts:

// Consider being more selective about these:
"Math.random" => true,  // Actually has side effects (changes RNG state)
"JSON.parse" | "JSON.stringify" => true,  // Can throw exceptions

Suggestion: Consider moving Math.random and exception-throwing functions to a separate category or adding error handling context.

2. Enhancement: Import Map Validation

The current implementation doesn't validate import map URLs at configuration time. Consider adding validation in config.rs:validate():

// In config.rs validate() method
for (package, url) in &target_config.import_map {
    if !url.starts_with("http") && !url.starts_with("/") && !url.starts_with("./") {
        return Err(Error::new_config(format!(
            "Invalid import map URL for '{}': {}", package, url
        )));
    }
}

3. Documentation: Feature Interactions

While the implementation correctly handles feature interactions, the PR description could better document:

  • Tree shaking is automatically disabled in dev mode
  • Import maps take precedence over package resolution
  • How external deps interact with import maps

4. Minor: Type Safety

The runtime type assertions in dev mode (transpiler.rs:1560-1574) only check basic types. Consider extending to check for null/undefined values which are common sources of runtime errors.

🚀 Implementation Highlights

Import Maps (transpiler.rs:1300-1310)

// Check if this package is mapped in the import map
if let Some(ref target_info) = self.target_info {
    if let Some(mapped_specifier) = target_info.import_map.get(package_name) {
        return self.generate_import_map_import(mapped_specifier, items);
    }
}

✅ Excellent: Clean precedence handling and proper fallback chain.

Tree Shaking Annotations (transpiler.rs:1545-1555)

fn should_add_pure_annotation(&self, function_name: &str) -> bool {
    if let Some(ref target_info) = self.target_info {
        if !target_info.tree_shaking || target_info.dev {
            return false;
        }
    }
    // ... pure function matching
}

✅ Smart: Properly disables tree shaking in dev mode for better debugging experience.

Development Mode Features (transpiler.rs:1580-1590)

fn add_debug_comment(&self, comment: &str) -> String {
    if let Some(ref target_info) = self.target_info {
        if target_info.dev {
            return format!("/* {} */", comment);
        }
    }
    String::new()
}

✅ Clean: Non-intrusive debug comments that help developers understand generated code.

🎯 Minor Suggestions

  1. Consider adding a configuration option to control the verbosity of dev mode comments
  2. Add validation for import map URLs at configuration parse time
  3. Consider extending runtime type checking to include null/undefined checks
  4. Document the precedence order of import resolution in the README or documentation

✅ Recommendation

APPROVE: This PR significantly enhances the Husk build system with well-thought-out features, excellent test coverage, and clean implementation. The minor suggestions above are enhancements rather than blocking issues.

The implementation demonstrates good understanding of JavaScript bundler best practices and provides valuable developer experience improvements through the dev/prod mode distinction.

fcoury and others added 13 commits June 27, 2025 23:10
- Add URL validation for import maps to prevent security issues
  - Validate URLs start with http://, https://, /, ./, or ../
  - Add comprehensive tests for URL validation
  - Return configuration errors for invalid URLs

- Adjust pure functions list for tree shaking correctness
  - Remove Math.random (has RNG state side effects)
  - Remove JSON.parse/stringify (can throw exceptions)
  - Add explicit exclusion for JSON.* methods

- Clean up repository
  - Remove backup files (*.bak)
  - Remove test files from root directory
  - Remove test project directories

- Fix all compilation warnings
  - Remove unused import in config_test.rs
  - Fix unused variables in transpiler_target_modes_test.rs

All high-priority security and correctness issues from PR review have been addressed.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Add configuration examples to README
  - Import maps with CDN and local path examples
  - Tree shaking configuration options
  - Development mode features and output
  - Multiple target configurations

- Document feature interactions and precedence
  - Tree shaking automatically disabled in dev mode
  - Import maps take precedence over package resolution
  - External dependencies work with import maps
  - Platform-specific behaviors

- Add import map security considerations
  - URL validation and allowed protocols
  - Best practices for secure usage
  - Version pinning and HTTPS recommendations
  - Examples of secure vs insecure configurations

Addresses documentation feedback from PR #24 review.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Replace string matching with HashSet for O(1) pure function lookups
- Add null/undefined checks to runtime type assertions in dev mode
- Improve performance of tree shaking pure annotations
- Fix compilation warnings by removing unused imports

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Adds support for importing default exports from npm packages using the
`external::package::default` syntax. This allows importing packages like
Express that export as default.

- Modified transpiler to detect and handle `external::package::default` pattern
- Updated semantic analyzer to register package name for default imports
- Added Express example app demonstrating the new import syntax
- Removed old dist files that are no longer needed

Example usage: `use external::express::default;`

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Adds comprehensive tests for the new default export import features:
- Tests for external::package::default syntax
- Tests for package::default syntax
- Verifies correct transpilation to ES module default imports

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Updates the test output files after running update.sh --force to match
the actual compiler output. The tests now correctly verify:
- Interpreter mode errors for external packages
- Proper transpilation to ES module default imports

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Updates the Express example to document the current limitation where
external packages have Unknown type and don't support method calls
in the semantic analyzer.

- Simplified main.husk to show what works
- Added comprehensive README explaining the limitation
- Documented potential future improvements

The import syntax works correctly and generates proper ES modules,
but full Express usage requires type system enhancements.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Modified parser to handle 'self' parameter without type annotation in extern methods
- Updated semantic analyzer to filter out 'self' when registering extern impl methods
- Fixed extern mod processing to correctly include module prefix in type and method names
- Updated express example to use proper type names with module prefix
- Fixed method call handling to not prepend object for extern types

This allows extern type declarations to use self parameter syntax like regular impl blocks,
making the API more consistent and natural for declaring external object-oriented APIs.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
The transpiler was incorrectly treating extern method calls like app.get()
as array indexing because "get" is a builtin array method. Added special
handling to check argument count - array.get() takes 1 arg, so methods
with more args fall through to default method call syntax.

This fixes the Express.js example where app.get("/", handler) was being
transpiled as app["/"] instead of app.get("/", handler).

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Moved package.json generation from project root to dist folder
- Changed main entry point from "dist/index.js" to "main.js"
- Updated npm install instructions to reflect new location
- Removed unused test files from express-app example

This makes the dist folder self-contained and deployable without
requiring files from the parent directory.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Fixed failing test that was expecting "build/index.js" as the main
entry point. The test now correctly expects "main.js" to match our
recent change to generate package.json in the dist folder.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Added tests for parsing extern impl blocks with self parameters
- Added tests for mixed self and non-self methods
- Added tests for self with multiple parameters
- Added tests for method chaining patterns with self
- All tests verify that self is correctly parsed as the first parameter

These tests ensure the self parameter support in extern declarations
works correctly for various use cases.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
…arameters

- Add comprehensive tests for extern impl blocks with self parameters
- Add tests for mixed self/non-self methods and method chaining
- Create detailed documentation for extern declarations feature
- Update language features index to include extern declarations
- Tests cover parsing, semantic analysis, and transpilation of extern self params

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@fcoury fcoury merged commit 0de8392 into master Jun 29, 2025
1 check passed
fcoury added a commit that referenced this pull request Jun 29, 2025
- Add URL validation for import maps to prevent security issues
  - Validate URLs start with http://, https://, /, ./, or ../
  - Add comprehensive tests for URL validation
  - Return configuration errors for invalid URLs

- Adjust pure functions list for tree shaking correctness
  - Remove Math.random (has RNG state side effects)
  - Remove JSON.parse/stringify (can throw exceptions)
  - Add explicit exclusion for JSON.* methods

- Clean up repository
  - Remove backup files (*.bak)
  - Remove test files from root directory
  - Remove test project directories

- Fix all compilation warnings
  - Remove unused import in config_test.rs
  - Fix unused variables in transpiler_target_modes_test.rs

All high-priority security and correctness issues from PR review have been addressed.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
fcoury added a commit that referenced this pull request Jun 29, 2025
- Add configuration examples to README
  - Import maps with CDN and local path examples
  - Tree shaking configuration options
  - Development mode features and output
  - Multiple target configurations

- Document feature interactions and precedence
  - Tree shaking automatically disabled in dev mode
  - Import maps take precedence over package resolution
  - External dependencies work with import maps
  - Platform-specific behaviors

- Add import map security considerations
  - URL validation and allowed protocols
  - Best practices for secure usage
  - Version pinning and HTTPS recommendations
  - Examples of secure vs insecure configurations

Addresses documentation feedback from PR #24 review.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@fcoury fcoury deleted the build-command branch June 29, 2025 03:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants