Tengo una biblioteca con un espacio de trabajo que contiene dos proyectos, uno para la biblioteca y otro para una aplicación de prueba.
├── projects ├── midi-app └── midi-lib En el archivo tsconfig.json del espacio de trabajo, configuré algunas @app y @lib :
"paths": { "@app/*": ["projects/midi-app/src/app/*"], "@lib/*": ["projects/midi-lib/src/lib/*"], "midi-lib": [ "dist/midi-lib" ], "midi-lib/*": [ "dist/midi-lib/*" ] } Hay un archivo projects/midi-lib/tsconfig.lib.json que se extiende sobre el archivo tsconfig.json anterior:
"extends": "../../tsconfig.json", Hay un archivo public-api.ts que contiene:
export * from './lib/midi-lib.module';Puedo usar esta biblioteca con la aplicación de prueba muy bien.
Pero cuando trato de usarlo en otra aplicación cliente, en otro espacio de trabajo, importado como un módulo de Nodo, obtengo muchos errores en las rutas desconocidas Can't resolve '@lib/...'
¿Cómo expresar las rutas de la biblioteca para que estén expuestas en una aplicación cliente? ¿O cómo traducir las rutas de la biblioteca al empaquetar la biblioteca?
Como pregunta secundaria, me pregunto por qué las extensiones no se hacen al revés. ¿Por qué no es el archivo tsconfig.json el que se extiende en el archivo projects/midi-lib/tsconfig.lib.json ?
Así es como empaqueto y luego uso la biblioteca:
Para empaquetar la biblioteca, agregue los siguientes scripts en la matriz de scripts del archivo principal package.json
"copy-license": "cp ./LICENSE.md ./dist/midi-lib", "copy-readme": "cp ./README.md ./dist/midi-lib", "copy-files": "npm run copy-license && npm run copy-readme", "build-lib": "ng build midi-lib", "npm-pack": "cd dist/midi-lib && npm pack", "package": "npm run build-lib && npm run copy-files && npm run npm-pack", y ejecute el comando: npm run package
luego instale la dependencia
npm install ../midi-lib/dist/midi-lib/midi-lib-0.0.1.tgz e importe el módulo en el módulo de aplicación En el archivo app.module.ts tenga:
import { MidiLibModule } from 'midi-lib'; @NgModule({ imports: [ MidiLibModulefinalmente inserte el componente en una plantilla
<midi-midi-lib></midi-midi-lib> Cuando la biblioteca se instala en una aplicación cliente, tiene muchos archivos .d.ts en los node_modules/midi-lib :
├── bundles ├── esm2015 │ └── lib │ ├── device │ ├── keyboard │ ├── model │ │ ├── measure │ │ └── note │ │ ├── duration │ │ └── pitch │ ├── service │ ├── sheet │ ├── soundtrack │ ├── store │ ├── synth │ └── upload ├── esm5 │ └── lib │ ├── device │ ├── keyboard │ ├── model │ │ ├── measure │ │ └── note │ │ ├── duration │ │ └── pitch │ ├── service │ ├── sheet │ ├── soundtrack │ ├── store │ ├── synth │ └── upload ├── fesm2015 ├── fesm5 └── lib ├── device ├── keyboard ├── model │ ├── measure │ └── note │ ├── duration │ └── pitch ├── service ├── sheet ├── soundtrack ├── store ├── synth └── upload Me gusta este archivo lib/service/melody.service.d.ts
import { SoundtrackStore } from '@lib/store/soundtrack-store'; import { ParseService } from '@lib/service/parse.service'; import { CommonService } from './common.service'; export declare class MelodyService { private soundtrackStore; private parseService; private commonService; constructor(soundtrackStore: SoundtrackStore, parseService: ParseService, commonService: CommonService); addSomeMelodies(): void; private addSoundtrack; private generateNotes; } Como puede verse, contiene referencias al mapeo de ruta @lib , que no se conoce en la aplicación cliente.
También traté de usar la propiedad baseUrl como solución alternativa, pero eso tampoco ayudó, ya que al instalar la biblioteca, este valor de baseUrl no se especificó.
¿Por qué empaquetar la biblioteca con el comando npm run package no resuelve las asignaciones de paths ?
La asignación de paths que establece en su tsconfig.json es puramente una asignación en tiempo de compilación . No tiene ningún efecto sobre el código generado por el compilador de TypeScript. Es por eso que tiene una falla en el tiempo de ejecución. Eso es algo que se informó al proyecto TypeScript, lo que sugiere que tsc debería traducir automáticamente las rutas de los módulos en el código emitido para ajustarse al mapeo establecido por las paths . Los desarrolladores de TS respondieron que tsc funciona según lo previsto y que la solución es configurar un cargador de módulos que realice en tiempo de ejecución un mapeo similar al establecido por las paths .
Esto es lo que creo que deberías hacer, según cómo describiste tu caso.
Supongo que midi-app es una aplicación de prueba que no está destinada a ser distribuida. Debería poder continuar usando la asignación de paths que tiene sin ningún problema. (No ha mencionado ningún problema al ejecutar esta aplicación. Por lo tanto, parece que sus herramientas ya se encargan del problema del tiempo de ejecución).
Para midi-lib , dejaría de depender de las asignaciones establecidas por las paths y solo usaría rutas relativas. Esta es una biblioteca, destinada a ser consumida por otros. Debido a esto, cualquier configuración que corrija la asignación de nombres de módulos en tiempo de ejecución (o en tiempo de agrupación) deberá ser manejada por los consumidores de su biblioteca. Los consumidores que usan Webpack tendrán que agregar una configuración a su configuración de Webpack para proporcionar la asignación correcta. Los consumidores que usan Rollup tendrían que hacer lo mismo con Rollup. Los consumidores que usan SystemJS tendrían que hacer lo mismo con SystemJS, etc.
Además, la configuración requerida podría complicarse según el contexto en el que se utilice su biblioteca. Siempre que su biblioteca sea la única que necesite asignar @lib a alguna ruta, la asignación que debe agregarse a Webpack (o SystemJS, etc.) puede ser global . El paquete de módulos o el cargador de módulos siempre reemplazarán @lib con su ruta, lo cual está bien porque su paquete es el único que necesita que @lib . Sin embargo, suponga que otro autor de la biblioteca hace exactamente lo que usted hizo, y un consumidor de su biblioteca también usa esa otra biblioteca. Ahora tiene una situación en la que @lib debe asignarse a una ruta en algunos casos y debe asignarse a otra ruta en otros casos. Esto se puede configurar, pero requiere una configuración más compleja.
Me he centrado en el problema de resolver módulos durante la agrupación o al cargarlos en tiempo de ejecución, pero hay otro problema. Los consumidores también tendrían que configurar su compilación tsc con una configuración especial porque los archivos .d.ts
Si solo usa rutas relativas en su código, los consumidores de su biblioteca no tendrán que preocuparse por agregar configuraciones especiales para adaptarse a las necesidades especiales de su biblioteca.
Hay un caso especial que puede encajar en su caso. Si su biblioteca se va a publicar como midi-lib , puede cambiar su mapa de paths para que, en lugar de @lib/* , tenga un mapa para midi-lib/* :
"midi-lib/*": ["projects/midi-lib/src/*"], (Tenga en cuenta que el símbolo @ no tiene un significado especial en lo que respecta a TypeScript. También tenga en cuenta que si su paquete debe instalarse con un ámbito, como @midi-project/midi-lib entonces necesita el ámbito en tsconfig.json mapeo también: "@midi-project/midi-lib/*": ... )
Básicamente, el objetivo aquí es establecer una asignación que le permita importar módulos en su proyecto exactamente de la misma manera que un consumidor de su proyecto importaría módulos individuales de él . Si un consumidor de su módulo importara ParseService con import { ParseService } from "midi-lib/lib/service/parse.service" , entonces en su propio código usaría la misma import cuando quiera usar ese módulo. (Tenga en cuenta que no importa si les dice a los consumidores que importen este módulo directamente. Si los consumidores importaran el módulo directamente , ¿qué ruta usarían?) Por lo tanto, la misma ruta funciona en tiempo de compilación y en tiempo de ejecución (o tiempo de agrupación). ). En tiempo de compilación, tsc convierte la ruta. En tiempo de ejecución o tiempo de agrupación, el algoritmo de resolución del módulo de Node (o una herramienta que puede seguir el mismo algoritmo, como Webpack o Rollup) convierte la ruta.
La cantidad de escritura que ahorraría con esto depende en gran medida de los nombres que haya elegido y de cómo haya estructurado su biblioteca.
En teoría, podría tener un paso después de ejecutar ng build que revisaría los archivos producidos por ng build y reemplazaría @lib en los nombres de los módulos con la ruta real a la que se supone que apunta. Los problemas con esto:
No se trata solo de ejecutar una sola herramienta o activar una opción de configuración. Tal vez una herramienta como el rollup pueda transformar los archivos JS, pero ahora necesita aprender cómo funciona y escribir una configuración para él.
AFAIK no hay una herramienta fácilmente disponible que transforme los archivos .d.ts cuando los necesite. Lo más probable es que tengas que escribir tu propia herramienta.
También necesitaría parchear los metadatos de compilación AOT generados por el compilador Angular AOT porque también contiene referencias de módulo, y estas referencias las usan los consumidores de su biblioteca. AFAIK, no existe tal herramienta. Así que aquí también tendrías que rodar el tuyo.
Su proceso de compilación podría interrumpirse si una nueva versión de Angular cambia el formato de los metadatos de compilación AOT o agrega un tipo diferente de archivo de metadatos que necesita parches. Lo sé por experiencia: tengo un par de paquetes que son aplicaciones angulares altamente experimentales. Por razones históricas, omiten por completo el uso de Angular CLI para la construcción. Cada actualización de Angular desde la versión 4 en adelante rompió algo en el proceso de construcción de estas aplicaciones. A menudo tenía que ver con cómo se manejaban los metadatos de compilación AOT.
Como han dicho otros, mecanografiado no modifica sus importaciones @app y @lib . Me encontré con el mismo problema al intentar usar rutas absolutas en un paquete de biblioteca. Lo que necesita es preparar su biblioteca para publicar con resumen o algo similar.
Rollup tiene varios complementos, y no cubriré la configuración completa, pero lo que necesita es un complemento que reescriba sus importaciones . Parece que este complemento lo hace: https://github.com/bitshiftza/rollup-plugin-ts-paths
Para el resto de la configuración del resumen, es probable que necesite rollup-plugin-node-resolve y rollup-plugin-commonjs , así como un complemento para manejar mecanografiado ( rollup-plugin-typescript ), o podría seguir la ruta más nueva de usar babel. Busque algunas guías, porque hay bibliotecas populares escritas en mecanografiado que usan resumen para preparar su código para empaquetar (React, por ejemplo).
Codificación feliz.