Skip to content

Commit

Permalink
update source:skypack support
Browse files Browse the repository at this point in the history
  • Loading branch information
FredKSchott committed Dec 10, 2020
1 parent af1de70 commit 70ad6a8
Show file tree
Hide file tree
Showing 13 changed files with 91 additions and 78 deletions.
6 changes: 3 additions & 3 deletions create-snowpack-app/app-template-react/snowpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
proxy: {
/* ... */
},
alias: {
/* ... */
},
experiments: {
source: 'skypack'
}
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as colors from 'kleur/colors';
import path from 'path';
import url from 'url';
import fs from 'fs';
import {VM as VM2} from 'vm2';
import {Plugin} from 'rollup';
Expand Down Expand Up @@ -115,6 +116,9 @@ export function rollupPluginWrapInstallTargets(
buildStart(inputOptions) {
const input = inputOptions.input as {[entryAlias: string]: string};
for (const [key, val] of Object.entries(input)) {
if (url.parse(val).protocol) {
continue;
}
const allInstallTargets = installTargets.filter(
(imp) => getWebDependencyName(imp.specifier) === key,
);
Expand Down
4 changes: 3 additions & 1 deletion skypack/src/rollup-plugin-remote-cdn.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import cacache from 'cacache';
import {OutputOptions, Plugin, ResolvedId} from 'rollup';
import {fetchCDN} from './index';
import {SKYPACK_ORIGIN, HAS_CDN_HASH_REGEX, RESOURCE_CACHE} from './util';
import {SKYPACK_ORIGIN, HAS_CDN_HASH_REGEX, RESOURCE_CACHE, ImportMap} from './util';

const CACHED_FILE_ID_PREFIX = 'snowpack-pkg-cache:';
const PIKA_CDN_TRIM_LENGTH = SKYPACK_ORIGIN.length;
Expand All @@ -15,8 +15,10 @@ const PIKA_CDN_TRIM_LENGTH = SKYPACK_ORIGIN.length;
* rollup that it's safe to load from the cache in the `load()` hook.
*/
export function rollupPluginSkypack({
lockfile,
installTypes,
}: {
lockfile?: ImportMap | null;
installTypes: boolean;
}) {
// const allTypesToInstall = new Set<string>();
Expand Down
1 change: 1 addition & 0 deletions snowpack/index.esm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export const startDevServer = Pkg.startDevServer;
export const buildProject = Pkg.buildProject;
export const loadAndValidateConfig = Pkg.loadAndValidateConfig;
export const createConfiguration = Pkg.createConfiguration;
export const loadLockfile = Pkg.loadLockfile;
export const getUrlForFile = Pkg.getUrlForFile;
31 changes: 8 additions & 23 deletions snowpack/src/build/import-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import fs from 'fs';
import path from 'path';
import { ImportMap, SnowpackConfig } from '../types/snowpack';
import { findMatchingAliasEntry, getExt, isRemoteSpecifier, replaceExt } from '../util';
import { getUrlForFile } from './file-urls';
import {ImportMap, SnowpackConfig} from '../types/snowpack';
import {findMatchingAliasEntry, getExt, isRemoteSpecifier, replaceExt} from '../util';
import {getUrlForFile} from './file-urls';

const cwd = process.cwd();

interface ImportResolverOptions {
fileLoc: string;
lockfile?: ImportMap | null;
config: SnowpackConfig;
}

/** Perform a file disk lookup for the requested import specifier. */
export function getImportStats(importedFileOnDisk: string): fs.Stats | false {
try {
Expand Down Expand Up @@ -47,11 +41,7 @@ function resolveSourceSpecifier(spec: string, stats: fs.Stats | false, config: S
* to a proper URL. Returns false if no matching import was found, which usually indicates a package
* not found in the import map.
*/
export function createImportResolver({
fileLoc,
lockfile,
config,
}: ImportResolverOptions) {
export function createImportResolver({fileLoc, config}: {fileLoc: string; config: SnowpackConfig}) {
return function importResolver(spec: string): string | false {
// Ignore "http://*" imports
if (isRemoteSpecifier(spec)) {
Expand All @@ -61,14 +51,6 @@ export function createImportResolver({
if (config.installOptions.externalPackage?.includes(spec)) {
return spec;
}
// Support snowpack.lock.json entry
if (lockfile && lockfile.imports[spec]) {
const mappedImport = lockfile.imports[spec];
if (isRemoteSpecifier(mappedImport)) {
return mappedImport;
}
throw new Error(`Not supported: "${spec}" lockfile entry must be a full URL (https://...).`);
}
if (spec.startsWith('/')) {
const importStats = getImportStats(path.resolve(cwd, spec.substr(1)));
return resolveSourceSpecifier(spec, importStats, config);
Expand All @@ -80,9 +62,12 @@ export function createImportResolver({
return resolveSourceSpecifier(newSpec, importStats, config);
}
const aliasEntry = findMatchingAliasEntry(config, spec);
if (aliasEntry && aliasEntry.type === 'path') {
if (aliasEntry && (aliasEntry.type === 'path' || aliasEntry.type === 'url')) {
const {from, to} = aliasEntry;
let result = spec.replace(from, to);
if (aliasEntry.type === 'url') {
return result;
}
const importedFileLoc = path.resolve(cwd, result);
const importStats = getImportStats(importedFileLoc);
const newSpec = getUrlForFile(importedFileLoc, config) || spec;
Expand Down
17 changes: 14 additions & 3 deletions snowpack/src/commands/add-rm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {cyan, dim, underline} from 'kleur/colors';
import path from 'path';
import {generateImportMap} from 'skypack';
import {logger} from '../logger';
import {CommandOptions} from '../types/snowpack';
import {CommandOptions, LockfileManifest} from '../types/snowpack';
import {writeLockfile} from '../util';

export async function addCommand(addValue: string, commandOptions: CommandOptions) {
Expand All @@ -20,14 +20,25 @@ export async function addCommand(addValue: string, commandOptions: CommandOption
underline(`https://cdn.skypack.dev/${pkgName}@${pkgSemver}`),
)} to your project lockfile. ${dim('(snowpack.lock.json)')}`,
);
const newLockfile = await generateImportMap({[pkgName]: pkgSemver}, lockfile || undefined);
const addedDependency = {[pkgName]: pkgSemver};
const newLockfile: LockfileManifest = {
...(await generateImportMap(addedDependency, lockfile || undefined)),
dependencies: {
...lockfile?.dependencies,
...addedDependency,
},
};
await writeLockfile(path.join(cwd, 'snowpack.lock.json'), newLockfile);
}

export async function rmCommand(addValue: string, commandOptions: CommandOptions) {
const {cwd, lockfile} = commandOptions;
let [pkgName] = addValue.split('@');
logger.info(`removing ${cyan(pkgName)} from project lockfile...`);
const newLockfile = await generateImportMap({[pkgName]: null}, lockfile || undefined);
const newLockfile: LockfileManifest = {
...(await generateImportMap({[pkgName]: null}, lockfile || undefined)),
dependencies: lockfile?.dependencies ?? {},
};
delete newLockfile.dependencies[pkgName];
await writeLockfile(path.join(cwd, 'snowpack.lock.json'), newLockfile);
}
7 changes: 3 additions & 4 deletions snowpack/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ async function installOptimizedDependencies(
});

const pkgSource = getPackageSource(commandOptions.config.experiments.source);
pkgSource.modifyBuildInstallConfig(installConfig);
pkgSource.modifyBuildInstallConfig({config: installConfig, lockfile: commandOptions.lockfile});

// Unlike dev (where we scan from source code) the built output guarantees that we
// will can scan all used entrypoints. Set to `[]` to improve tree-shaking performance.
Expand Down Expand Up @@ -239,15 +239,14 @@ class FileBuilder {
const file = rawFile as SnowpackSourceFile<string>;
const resolveImportSpecifier = createImportResolver({
fileLoc: file.locOnDisk!, // we’re confident these are reading from disk because we just read them
lockfile: this.lockfile,
config: this.config,
});
const resolvedCode = await transformFileImports(file, (spec) => {
// Try to resolve the specifier to a known URL in the project
let resolvedImportUrl = resolveImportSpecifier(spec);
// If not resolved, then this is a package. During build, dependencies are always
// installed locally via esinstall, so use localPackageSource here.
if (!resolvedImportUrl) {
if (importMap.imports[spec]) {
resolvedImportUrl = localPackageSource.resolvePackageImport(spec, importMap, this.config);
}
// If still not resolved, then this imported package somehow evaded detection
Expand All @@ -259,7 +258,7 @@ class FileBuilder {
}
// Ignore "http://*" imports
if (isRemoteSpecifier(resolvedImportUrl)) {
return spec;
return resolvedImportUrl;
}
// Ignore packages marked as external
if (this.config.installOptions.externalPackage?.includes(resolvedImportUrl)) {
Expand Down
1 change: 0 additions & 1 deletion snowpack/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,6 @@ export async function startDevServer(commandOptions: CommandOptions): Promise<Sn
let missingPackages: string[] = [];
const resolveImportSpecifier = createImportResolver({
fileLoc,
lockfile: lockfile,
config,
});
wrappedResponse = await transformFileImports(
Expand Down
16 changes: 0 additions & 16 deletions snowpack/src/commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,6 @@ export async function getInstallTargets(
} else {
installTargets.push(...(await scanImports(process.env.NODE_ENV === 'test', config)));
}
if (lockfile) {
const importMapSubPaths = Object.keys(lockfile.imports).filter((ent) => ent.endsWith('/'));
installTargets = installTargets.filter((t) => {
if (lockfile.imports[t.specifier]) {
return false;
}
if (
t.specifier.includes('/') &&
importMapSubPaths.some((ent) => t.specifier.startsWith(ent))
) {
return false;
}
return true;
});
}
return installTargets;
}

Expand Down Expand Up @@ -110,7 +95,6 @@ export async function run({
}

let newLockfile: ImportMap | null = null;

const finalResult = await install(installTargets, {
cwd,
lockfile: newLockfile || undefined,
Expand Down
1 change: 1 addition & 0 deletions snowpack/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from './types/snowpack';
export {startDevServer} from './commands/dev';
export {buildProject} from './commands/build';
export {loadConfigurationForCLI as loadAndValidateConfig, createConfiguration} from './config.js';
export {readLockfile as loadLockfile} from './util.js';
export {getUrlForFile} from './build/file-urls';

const cwd = process.cwd();
Expand Down
23 changes: 17 additions & 6 deletions snowpack/src/sources/skypack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
SKYPACK_ORIGIN,
} from 'skypack';
import {logger} from '../logger';
import {ImportMap, PackageSource, SnowpackConfig} from '../types/snowpack';
import {ImportMap, LockfileManifest, PackageSource, SnowpackConfig} from '../types/snowpack';

const fetchedPackages = new Set<string>();
function logFetching(packageName: string) {
Expand Down Expand Up @@ -45,15 +45,24 @@ export default {
return {imports: {}};
},

async modifyBuildInstallConfig(config: SnowpackConfig) {
config.installOptions.rollup?.plugins?.push(
rollupPluginSkypack({installTypes: false}) as Plugin,
async modifyBuildInstallConfig({
config,
lockfile,
}: {
config: SnowpackConfig;
lockfile: LockfileManifest | null;
}) {
config.installOptions.lockfile = lockfile || undefined;
config.installOptions.rollup = config.installOptions.rollup || {};
config.installOptions.rollup.plugins = config.installOptions.rollup.plugins || [];
config.installOptions.rollup.plugins.push(
rollupPluginSkypack({installTypes: false, lockfile: lockfile || undefined}) as Plugin,
);
},

async load(
spec: string,
{config, lockfile}: {config: SnowpackConfig; lockfile: ImportMap | null},
{config, lockfile}: {config: SnowpackConfig; lockfile: LockfileManifest | null},
): Promise<string> {
let body: string;
if (
Expand All @@ -70,7 +79,9 @@ export default {
} else if (lockfile && lockfile.imports[packageName + '/']) {
body = (await fetchCDN(lockfile.imports[packageName + '/'] + packagePath)).body;
} else {
const _packageSemver = config.webDependencies && config.webDependencies[packageName];
// TODO: When config.root is added, look up package.json "dependencies" & "devDependencies"
// from the root and fallback to those if lockfile.dependencies[packageName] doesn't exist.
const _packageSemver = lockfile?.dependencies && lockfile.dependencies[packageName];
if (!_packageSemver) {
logFetching(packageName);
}
Expand Down
16 changes: 12 additions & 4 deletions snowpack/src/types/snowpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,6 @@ export interface SnowpackConfig {
extends?: string;
exclude: string[];
knownEntrypoints: string[];
webDependencies: Record<string, string>;
mount: Record<string, MountEntry>;
alias: Record<string, string>;
scripts: Record<string, string>;
Expand Down Expand Up @@ -246,6 +245,8 @@ export interface SnowpackConfig {
testOptions: {
files: string[];
};
// @deprecated - now found at lockfile.dependencies
webDependencies: Record<string, string>;
// @deprecated - Use experiments.routes instead
proxy: Proxy[];
/** EXPERIMENTAL - This section is experimental and not yet finalized. May change across minor versions. */
Expand Down Expand Up @@ -306,14 +307,18 @@ export interface CLIFlags extends Omit<InstallOptions, 'env'> {
}

export interface ImportMap {
imports: {[packageName: string]: string};
imports: {[specifier: string]: string};
}

export interface LockfileManifest extends ImportMap {
dependencies: {[packageName: string]: string};
}

export interface CommandOptions {
// TODO(fks): remove `cwd`, replace with a new `config.root` property on SnowpackConfig.
cwd: string;
config: SnowpackConfig;
lockfile: ImportMap | null;
lockfile: LockfileManifest | null;
}

export type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'; // same as Pino
Expand Down Expand Up @@ -346,5 +351,8 @@ export interface PackageSource {
/** Handle 1+ missing package imports before failing, if possible. */
recoverMissingPackageImport(missingPackages: string[]): Promise<ImportMap>;
/** Modify the build install config for optimized build install. */
modifyBuildInstallConfig(config: SnowpackConfig): Promise<void>;
modifyBuildInstallConfig(options: {
config: SnowpackConfig;
lockfile: ImportMap | null;
}): Promise<void>;
}
Loading

1 comment on commit 70ad6a8

@vercel
Copy link

@vercel vercel bot commented on 70ad6a8 Dec 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.