project Service

The project service provides access to information about the current Pinstripe project, including its configuration, paths, and metadata. It acts as a bridge to the underlying Project class, which is a singleton that analyzes the current working directory to find and parse project information from package.json.

Description

The project service gives you access to essential project metadata and file system information needed for building applications, generating files, and configuring services. It automatically discovers project boundaries by walking up the directory tree to find the nearest package.json file, then extracts relevant information about the project structure.

This service is particularly useful for:

  • Accessing project name and version information
  • Getting absolute paths to project root and entry points
  • Reading project configuration from package.json
  • Determining if you're currently within a valid project
  • Finding node_modules directories in the project hierarchy

Interface

The project service returns a Project instance with the following properties:

Properties

name (String)

The project name from package.json, or 'unknown' if not found.

rootPath (String)

The absolute path to the project root directory (where package.json is located).

configPath (String)

The absolute path to the package.json file.

config (Object)

The parsed contents of the package.json file.

exists (Boolean)

Whether a valid project was found (has both configPath and config.name).

entryPath (String)

The absolute path to the main entry point file (from exports['.'] or main field).

mainPath (String)

The absolute path to the main file specified in package.json.

exports (Object)

Map of export names to their resolved absolute file paths.

nodePaths (Array)

Array of absolute paths to node_modules directories found in the project hierarchy.

Examples

Basic Project Information

export default {
    async run(){
        // Get project name for database naming
        const projectName = await this.project.name;
        console.log(`Project: ${projectName}`);
        
        // Check if we're in a valid project
        if(await this.project.exists){
            console.log('Valid project detected');
        } else {
            console.log('No project found');
        }
    }
};

File Path Resolution

export default {
    async run(){
        // Get project root for file operations
        const rootPath = await this.project.rootPath;
        const configFile = `${rootPath}/pinstripe.config.js`;
        
        // Access package.json data
        const config = await this.project.config;
        console.log(`Version: ${config.version}`);
        console.log(`Description: ${config.description}`);
    }
};

Database Configuration

// From config service - using project info for database names
export default {
    async normalizeDatabaseConfig(config){
        const environment = process.env.NODE_ENV || 'development';
        
        return {
            ...config,
            database: `${this.inflector.snakeify(await this.project.name)}_${environment}`
        };
    }
};

Version Management

// From version service - using project config for versioning
export default {
    create(){
        return this.defer(async () => {
            let version = await this.project.config.version || '0.1.0';
            
            if(await this.environment == 'development'){
                version += `.${Date.now()}`;
            }
            
            return version;
        });
    }
};

Project Initialization

// From initialize_project command - using project name for setup
export default {
    async generateSeedDatabaseCommand(){
        const { generateFile } = this.fsBuilder;
        
        await generateFile(`lib/commands/seed_database.js`, ({ line, indent }) => {
            line(`export default {`);
            indent(({ line, indent }) => {
                line('async run(){');
                indent(({ line }) => {
                    line(`await this.database.site.update({`);
                    indent(async ({ line }) => {
                        // Capitalize project name for site title
                        line(`title: '${this.inflector.capitalize(await this.project.name)}'`);
                    });
                    line(`});`);
                });
                line('}');
            });
            line('};');
        });
    }
};

Configuration File Resolution

// From config service - using project root to find config files
export default {
    async createConfig(){
        const candidateConfigFilePath = `${await this.project.rootPath}/pinstripe.config.js`;
        
        let config = {};
        if(existsSync(candidateConfigFilePath)){
            config = await (await import(candidateConfigFilePath)).default;
        }
        
        return config;
    }
};

CLI Integration

// From cli.js - using project existence to determine available commands  
const { entryPath, exists } = await Project.instance;

if(exists){
    Command.unregister('generate-project');
} else {
    // Remove most commands when not in a project
    Command.names.forEach(commandName => {
        if(commandName == 'list-commands' || commandName == 'generate-project') return;
        Command.unregister(commandName);
    });
}

Bundle Configuration

// From bundle.js - using node paths for build resolution
export default {
    async build(){
        return esbuild.build({
            // Use project's node_modules for resolution
            nodePaths: await Project.instance.nodePaths,
            // ... other build options
        });
    }
};

Notes

  • The project service uses deferred initialization, so accessing properties requires await
  • The service automatically walks up the directory tree to find the project root
  • Returns reasonable defaults (like 'unknown' for name) when project information is missing
  • The exists property is useful for determining if you're running within a valid project context
  • All file paths returned are absolute and resolved (symlinks are resolved to actual paths)
  • The service is a singleton, so the same project instance is shared across the application