<commit_before>import { basename, extname } from './utils/path';
import { Promise } from 'sander';
import MagicString from 'magic-string';
import { blank, keys } from './utils/object';
import Module from './Module';
import ExternalModule from './ExternalModule';
import finalisers from './finalisers/index';
import makeLegalIdentifier from './utils/makeLegalIdentifier';
import ensureArray from './utils/ensureArray';
import { defaultResolver, defaultExternalResolver } from './utils/resolveId';
import { defaultLoader } from './utils/load';
import getExportMode from './utils/getExportMode';
import getIndentString from './utils/getIndentString';
import { unixizePath } from './utils/normalizePlatform.js';

export default class Bundle {
	constructor ( options ) {
		this.entry = options.entry;
		this.entryModule = null;

		this.resolveId = options.resolveId || defaultResolver;
		this.load = options.load || defaultLoader;

		this.resolveOptions = {
			external: ensureArray( options.external ),
			resolveExternal: options.resolveExternal || defaultExternalResolver
		};

		this.loadOptions = {
			transform: ensureArray( options.transform )
		};

		this.varExports = blank();
		this.toExport = null;

		this.modulePromises = blank();
		this.modules = [];

		this.statements = null;
		this.externalModules = [];
		this.internalNamespaceModules = [];

		this.assumedGlobals = blank();
		this.assumedGlobals.exports = true; // TODO strictly speaking, this only applies with non-ES6, non-default-only bundles
	}

	build () {
		return this.fetchModule( this.entry, undefined )
			.then( entryModule => {
				const defaultExport = entryModule.exports.default;

				this.entryModule = entryModule;

				if ( defaultExport ) {
					// `export default function foo () {...}` -
					// use the declared name for the export
					if ( defaultExport.declaredName ) {
						entryModule.suggestName( 'default', defaultExport.declaredName );
					}

					// `export default a + b` - generate an export name
					// based on the id of the entry module
					else {
						let defaultExportName = makeLegalIdentifier( basename( this.entryModule.id ).slice( 0, -extname( this.entryModule.id ).length ) );

						// deconflict
						let topLevelNames = [];
						entryModule.statements.forEach( statement => {
							keys( statement.defines ).forEach( name => topLevelNames.push( name ) );
						});

						while ( ~topLevelNames.indexOf( defaultExportName ) ) {
							defaultExportName = `_${defaultExportName}`;
						}

						entryModule.suggestName( 'default', defaultExportName );
					}
				}

				return entryModule.markAllStatements( true );
			})
			.then( () => {
				return this.markAllModifierStatements();
			})
			.then( () => {
				this.orderedModules = this.sort();
			});
	}

	deconflict ( es6 ) {
		let definers = blank();
		let conflicts = blank();

		// Assign names to external modules
		this.externalModules.forEach( module => {
			let name = makeLegalIdentifier( module.suggestedNames['*'] || module.suggestedNames.default || module.id );

			while ( definers[ name ] ) {
				conflicts[ name ] = true;
				name = `_${name}`;
			}

			definers[ name ] = [ module ];
			module.name = name;
			this.assumedGlobals[ name ] = true;
		});

		// Discover conflicts (i.e. two statements in separate modules both define `foo`)
		this.orderedModules.forEach( module => {
			module.statements.forEach( statement => {
				if ( !statement.isIncluded ) return;

				const names = keys( statement.defines );

				// with default exports that are expressions (`export default 42`),
				// we need to ensure that the name chosen for the expression does
				// not conflict
				if ( statement.node.type === 'ExportDefaultDeclaration' ) {
					const name = module.getCanonicalName( 'default', es6 );

					const isProxy = statement.node.declaration && statement.node.declaration.type === 'Identifier';
					const shouldDeconflict = !isProxy || ( module.getCanonicalName( statement.node.declaration.name, es6 ) !== name );

					if ( shouldDeconflict && !~names.indexOf( name ) ) {
						names.push( name );
					}
				}

				names.forEach( name => {
					if ( definers[ name ] ) {
						conflicts[ name ] = true;
					} else {
						definers[ name ] = [];
					}

					// TODO in good js, there shouldn't be duplicate definitions
					// per module... but some people write bad js
					definers[ name ].push( module );
				});
			});
		});

		// Ensure we don't conflict with globals
		keys( this.assumedGlobals ).forEach( name => {
			if ( definers[ name ] ) {
				conflicts[ name ] = true;
			}
		});

		// Rename conflicting identifiers so they can live in the same scope
		keys( conflicts ).forEach( name => {
			const modules = definers[ name ];

			if ( !this.assumedGlobals[ name ] ) {
				// the module closest to the entryModule gets away with
				// keeping things as they are, unless we have a conflict
				// with a global name
				modules.pop();
			}

			modules.forEach( module => {
				const replacement = getSafeName( name );
				module.rename( name, replacement );
			});
		});

		function getSafeName ( name ) {
			while ( conflicts[ name ] ) {
				name = `_${name}`;
			}

			conflicts[ name ] = true;
			return name;
		}
	}

	fetchModule ( importee, importer ) {
		return Promise.resolve( this.resolveId( importee, importer, this.resolveOptions ) )
			.then( id => {
				if ( !id ) {
					// external module
					if ( !this.modulePromises[ importee ] ) {
						const module = new ExternalModule( importee );
						this.externalModules.push( module );
						this.modulePromises[ importee ] = Promise.resolve( module );<commit_msg>dont call module.getCanonicalName before module.rename (has undesired side effects)<commit_after>import { basename, extname } from './utils/path';
import { Promise } from 'sander';
import MagicString from 'magic-string';
import { blank, keys } from './utils/object';
import Module from './Module';
import ExternalModule from './ExternalModule';
import finalisers from './finalisers/index';
import makeLegalIdentifier from './utils/makeLegalIdentifier';
import ensureArray from './utils/ensureArray';
import { defaultResolver, defaultExternalResolver } from './utils/resolveId';
import { defaultLoader } from './utils/load';
import getExportMode from './utils/getExportMode';
import getIndentString from './utils/getIndentString';
import { unixizePath } from './utils/normalizePlatform.js';

export default class Bundle {
	constructor ( options ) {
		this.entry = options.entry;
		this.entryModule = null;

		this.resolveId = options.resolveId || defaultResolver;
		this.load = options.load || defaultLoader;

		this.resolveOptions = {
			external: ensureArray( options.external ),
			resolveExternal: options.resolveExternal || defaultExternalResolver
		};

		this.loadOptions = {
			transform: ensureArray( options.transform )
		};

		this.varExports = blank();
		this.toExport = null;

		this.modulePromises = blank();
		this.modules = [];

		this.statements = null;
		this.externalModules = [];
		this.internalNamespaceModules = [];

		this.assumedGlobals = blank();
		this.assumedGlobals.exports = true; // TODO strictly speaking, this only applies with non-ES6, non-default-only bundles
	}

	build () {
		return this.fetchModule( this.entry, undefined )
			.then( entryModule => {
				const defaultExport = entryModule.exports.default;

				this.entryModule = entryModule;

				if ( defaultExport ) {
					// `export default function foo () {...}` -
					// use the declared name for the export
					if ( defaultExport.declaredName ) {
						entryModule.suggestName( 'default', defaultExport.declaredName );
					}

					// `export default a + b` - generate an export name
					// based on the id of the entry module
					else {
						let defaultExportName = makeLegalIdentifier( basename( this.entryModule.id ).slice( 0, -extname( this.entryModule.id ).length ) );

						// deconflict
						let topLevelNames = [];
						entryModule.statements.forEach( statement => {
							keys( statement.defines ).forEach( name => topLevelNames.push( name ) );
						});

						while ( ~topLevelNames.indexOf( defaultExportName ) ) {
							defaultExportName = `_${defaultExportName}`;
						}

						entryModule.suggestName( 'default', defaultExportName );
					}
				}

				return entryModule.markAllStatements( true );
			})
			.then( () => {
				return this.markAllModifierStatements();
			})
			.then( () => {
				this.orderedModules = this.sort();
			});
	}

	deconflict ( es6 ) {
		let definers = blank();
		let conflicts = blank();

		// Assign names to external modules
		this.externalModules.forEach( module => {
			let name = makeLegalIdentifier( module.suggestedNames['*'] || module.suggestedNames.default || module.id );

			while ( definers[ name ] ) {
				conflicts[ name ] = true;
				name = `_${name}`;
			}

			definers[ name ] = [ module ];
			module.name = name;
			this.assumedGlobals[ name ] = true;
		});

		// Discover conflicts (i.e. two statements in separate modules both define `foo`)
		this.orderedModules.forEach( module => {
			module.statements.forEach( statement => {
				if ( !statement.isIncluded ) return;

				keys( statement.defines ).forEach( name => {
					if ( definers[ name ] ) {
						conflicts[ name ] = true;
					} else {
						definers[ name ] = [];
					}

					// TODO in good js, there shouldn't be duplicate definitions
					// per module... but some people write bad js
					definers[ name ].push( module );
				});
			});
		});

		// Ensure we don't conflict with globals
		keys( this.assumedGlobals ).forEach( name => {
			if ( definers[ name ] ) {
				conflicts[ name ] = true;
			}
		});

		// Rename conflicting identifiers so they can live in the same scope
		keys( conflicts ).forEach( name => {
			const modules = definers[ name ];

			if ( !this.assumedGlobals[ name ] ) {
				// the module closest to the entryModule gets away with
				// keeping things as they are, unless we have a conflict
				// with a global name
				modules.pop();
			}

			modules.forEach( module => {
				const replacement = getSafeName( name );
				module.rename( name, replacement );
			});
		});

		// TODO assign names to default/namespace exports, based on suggestions
		// TODO trace bindings and rename within modules here (rather than later
		// with getCanonicalName)

		function getSafeName ( name ) {
			while ( conflicts[ name ] ) {
				name = `_${name}`;
			}

			conflicts[ name ] = true;
			return name;
		}
	}

	fetchModule ( importee, importer ) {
		return Promise.resolve( this.resolveId( importee, importer, this.resolveOptions ) )
			.then( id => {
				if ( !id ) {
					// external module
					if ( !this.modulePromises[ importee ] ) {
						const module = new ExternalModule( importee );
						this.externalModules.push( module );
						this.modulePromises[ importee ] = Promise.resolve( module );
{'fraction_non_alphanumeric': 0.12711241734019105, 'fraction_numerical': 0.0009184423218221896, 'mean_word_length': 3.050967261904762, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5645267', 'n_tokens_mistral': 3849, 'n_tokens_neox': 3098, 'n_words': 877}