Skip to content

Commit

Permalink
feat: support tsconfig paths (#285)
Browse files Browse the repository at this point in the history
* feat/tsconfig-paths

* Adding post-process resolver

* fix up lockfile

* bump canary

* bump canary

Co-authored-by: L <[email protected]>
  • Loading branch information
jlalmes and louisgv authored Nov 11, 2022
1 parent 0f7b5f6 commit 8dbd9f5
Show file tree
Hide file tree
Showing 18 changed files with 377 additions and 301 deletions.
2 changes: 1 addition & 1 deletion cli/create-plasmo/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-plasmo",
"version": "0.58.0",
"version": "0.59.0-alpha.1",
"description": "Create Plasmo Framework Browser Extension",
"main": "dist/index.js",
"bin": "bin/index.mjs",
Expand Down
2 changes: 1 addition & 1 deletion cli/plasmo/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "plasmo",
"version": "0.58.0",
"version": "0.59.0-alpha.1",
"description": "The Plasmo Platform CLI",
"main": "dist/index.js",
"types": "dist/type.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion cli/plasmo/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ async function dev() {
}

if (event.type === "buildSuccess") {
aLog(`Extension re-packaged in ${event.buildTime}ms!`)
aLog(`Extension re-packaged in ${event.buildTime}ms! 🚀`)
await plasmoManifest.postBuild()
} else if (event.type === "buildFailure") {
event.diagnostics.forEach((diagnostic) => {
Expand Down
6 changes: 5 additions & 1 deletion packages/parcel-config/index.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"extends": "@parcel/config-default",
"bundler": "@plasmohq/parcel-bundler",
"resolvers": ["@plasmohq/parcel-resolver", "..."],
"resolvers": [
"@plasmohq/parcel-resolver",
"...",
"@plasmohq/parcel-resolver-post"
],
"transformers": {
"*.plasmo.manifest.json": ["@plasmohq/parcel-transformer-manifest"],

Expand Down
3 changes: 2 additions & 1 deletion packages/parcel-config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@plasmohq/parcel-config",
"version": "0.21.0",
"version": "0.22.0",
"license": "MIT",
"repository": {
"type": "git",
Expand All @@ -26,6 +26,7 @@
"@plasmohq/parcel-namer-manifest": "workspace:*",
"@plasmohq/parcel-packager": "workspace:*",
"@plasmohq/parcel-resolver": "workspace:*",
"@plasmohq/parcel-resolver-post": "workspace:*",
"@plasmohq/parcel-runtime": "workspace:*",
"@plasmohq/parcel-transformer-inject-env": "workspace:*",
"@plasmohq/parcel-transformer-inline-css": "workspace:*",
Expand Down
3 changes: 3 additions & 0 deletions packages/parcel-resolver-post/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules

dist/
37 changes: 37 additions & 0 deletions packages/parcel-resolver-post/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@plasmohq/parcel-resolver-post",
"version": "0.0.0",
"description": "Plasmo Parcel Resolver Post-processing",
"files": [
"dist"
],
"main": "dist/index.js",
"scripts": {
"prepublishOnly": "pnpm build",
"build": "tsup src/index.ts --minify --clean",
"dev": "tsup src/index.ts --watch"
},
"author": "Plasmo Corp. <[email protected]>",
"homepage": "https://docs.plasmo.com/",
"engines": {
"parcel": ">= 2.7.0"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/PlasmoHQ/plasmo.git"
},
"devDependencies": {
"@plasmo/config": "workspace:*",
"@plasmo/utils": "workspace:*"
},
"dependencies": {
"@parcel/core": "2.7.0",
"@parcel/utils": "2.7.0",
"@parcel/types": "2.7.0",
"@parcel/hash": "2.7.0",
"@parcel/plugin": "2.7.0",
"typescript": "4.8.4",
"tsup": "6.3.0"
}
}
193 changes: 193 additions & 0 deletions packages/parcel-resolver-post/src/handle-ts-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/**
* Copyright (c) 2022 Plasmo Corp. <[email protected]> (https://www.plasmo.com) and contributors
* MIT License
*
* Based on: https://github.com/zachbryant/parcel-resolver-tspaths
* Copyright (c) 2021 Zach Bryant
* MIT License
*/

import { loadConfig } from "@parcel/utils"
import { extname, join, resolve } from "path"
import type { CompilerOptions } from "typescript"

import type { ResolverProps, ResolverResult } from "./shared"
import { checkWebpackSpecificImportSyntax, findModule, trimStar } from "./utils"

const tsRegex = /\.tsx?$/

const relevantExtList = [
".ts",
".tsx",
".svelte",
".vue",
".json",

".css",
".scss",
".sass",
".less",

".svg",

".js",
".jsx"
] as const

const relevantExtSet = new Set(relevantExtList)

type TsPaths = string[]

type TsPathsMap = Map<string, TsPaths>

const state = {
pathsMap: null as TsPathsMap,
pathsMapRegex: null as [string, TsPaths, RegExp][]
}

export async function handleTsPath(
props: ResolverProps
): Promise<ResolverResult> {
try {
const { dependency } = props

checkWebpackSpecificImportSyntax(dependency.specifier)

const isTypescript = tsRegex.test(dependency.resolveFrom)

if (!isTypescript) {
return null
}

const { compilerOptions } = await getTsconfigCompilerOptions(props)
if (!compilerOptions) {
return null
}

loadTsPathsMap(compilerOptions)

const result = attemptResolve(props)

if (!result) {
return null
}

return {
filePath: result
}
} catch {
return null
}
}

/** Populate a map with any paths from tsconfig.json starting from baseUrl */
function loadTsPathsMap(compilerOptions: CompilerOptions) {
if (state.pathsMap) {
return
}

const baseUrl = compilerOptions.baseUrl || "."
const tsPaths = compilerOptions.paths || {}

const tsPathsMap = Object.entries(tsPaths).reduce(
(output, [key, pathList]) => {
output.set(
key,
pathList.map((p) => join(baseUrl, p))
)
return output
},
new Map<string, TsPaths>()
)

state.pathsMap = tsPathsMap
state.pathsMapRegex = Array.from(tsPathsMap.entries()).map((entry) => [
...entry,
new RegExp(`^${entry[0].replace("*", ".*")}$`)
])
}

function attemptResolve({ specifier, dependency }: ResolverProps) {
const { pathsMap, pathsMapRegex } = state
if (pathsMap.has(specifier)) {
return attemptResolveArray(
specifier,
specifier,
pathsMap.get(specifier),
dependency.resolveFrom
)
}

const relevantEntry = pathsMapRegex.find(([, , aliasRegex]) =>
aliasRegex.test(specifier)
)

if (!!relevantEntry) {
return attemptResolveArray(
specifier,
relevantEntry[0],
relevantEntry[1],
dependency.resolveFrom
)
}

return null
}

// TODO support resource loaders like 'url:@alias/my.svg'
/** Attempt to resolve any path associated with the alias to a file or directory index */
function attemptResolveArray(
from: string,
alias: string,
realPaths: TsPaths,
parentFile: string
) {
for (const option of realPaths) {
const absoluteBaseFile = resolve(
from.replace(trimStar(alias), trimStar(option))
)

const importExt = extname(absoluteBaseFile)

if (importExt.length > 0 && relevantExtSet.has(importExt as any)) {
return absoluteBaseFile
}

const parentExt = extname(parentFile)

const checkingExts = [
parentExt,
...relevantExtList.filter((ext) => ext !== parentExt)
]

const mod = findModule(absoluteBaseFile, checkingExts)

if (mod !== null) {
return mod
}
}
return null
}

async function getTsconfigCompilerOptions({
options,
dependency
}: ResolverProps) {
const result = await loadConfig(
options.inputFS,
dependency.resolveFrom,
["tsconfig.json", "tsconfig.js"],
join(process.env.PLASMO_PROJECT_DIR, "lab")
)

if (!result?.config?.compilerOptions) {
return null
}

const filePath = result.files[0].filePath
const compilerOptions = result?.config?.compilerOptions as CompilerOptions
return {
compilerOptions,
filePath
}
}
9 changes: 9 additions & 0 deletions packages/parcel-resolver-post/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Resolver } from "@parcel/plugin"

import { handleTsPath } from "./handle-ts-path"

export default new Resolver({
async resolve(props) {
return (await handleTsPath(props)) || null
}
})
20 changes: 20 additions & 0 deletions packages/parcel-resolver-post/src/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Resolver } from "@parcel/plugin"
import type { ResolveResult } from "@parcel/types"

export const relevantExtensionList = [
".ts",
".tsx",
".svelte",
".vue",
".json",

".js",
".jsx"
] as const

export const relevantExtensionSet = new Set(relevantExtensionList)

type ResolveFx = ConstructorParameters<typeof Resolver>[0]["resolve"]

export type ResolverResult = ResolveResult
export type ResolverProps = Parameters<ResolveFx>[0]
65 changes: 65 additions & 0 deletions packages/parcel-resolver-post/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { ResolveResult } from "@parcel/types"
import { statSync } from "fs"
import { resolve } from "path"

import { type ResolverResult, relevantExtensionList } from "./shared"

const WEBPACK_IMPORT_REGEX = /\S+-loader\S*!\S+/g

export function checkWebpackSpecificImportSyntax(specifier = "") {
// Throw user friendly errors on special webpack loader syntax
// ex. `imports-loader?$=jquery!./example.js`
if (WEBPACK_IMPORT_REGEX.test(specifier)) {
throw new Error(
`The import path: ${specifier} is using webpack specific loader import syntax, which isn't supported by Parcel.`
)
}
}

export function trimStar(str: string) {
return trim(str, "*")
}

export function trim(str: string, trim: string) {
if (str.endsWith(trim)) {
str = str.substring(0, str.length - trim.length)
}
return str
}

const isFile = (filePath: string) => {
try {
return statSync(filePath).isFile()
} catch {
return false
}
}

export function findModule(
absoluteBaseFile: string,
checkingExts = relevantExtensionList as readonly string[]
) {
return checkingExts
.flatMap((ext) => [
resolve(`${absoluteBaseFile}${ext}`),
resolve(absoluteBaseFile, `index${ext}`)
])
.find(isFile)
}

/**
* Look for source code file (crawl index)
*/
export const resolveSourceIndex = async (
absoluteBaseFile: string,
checkingExts = relevantExtensionList as readonly string[],
opts = {} as Partial<ResolveResult>
): Promise<ResolverResult> => {
const filePath = findModule(absoluteBaseFile, checkingExts)

if (!filePath) {
return null
}

return { filePath, ...opts }
}
5 changes: 5 additions & 0 deletions packages/parcel-resolver-post/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "@plasmo/config/ts/cli",
"include": ["src/**/*.ts"],
"exclude": ["dist", "node_modules"]
}
Loading

0 comments on commit 8dbd9f5

Please sign in to comment.