Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User natives #206

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open

User natives #206

wants to merge 24 commits into from

Conversation

lgassman
Copy link

@lgassman lgassman commented Dec 30, 2024

Este PR necesita de que se aprueba primero uqbar-project/wollok-ts#326

Agrega capacidad para que un proyecto wollok pueda incluir métodos nativos.

Además de los tests que se incluyen, se puede ver los siguientes proyectos que utilizan esta feature:

dolarito: https://github.com/wollok/dolarito : Utiliza métodos nativos para acceder a internet
pelotas: https://github.com/wollok/pelotas : Utiliza métodos nativos para que a través de una librería de node, se persista información.
pruebaNatives: https://github.com/wollok/pruebaNatives : Esta es para tener una referencia rápida de como escribir un método nativo complejo (pasandose parámetros, haciendo invocaciones, etc).

Los archivos TS/JS se pueden poner al mismo nivel que el .wlk que los utiliza, o bien en una carpeta interna. ésta se puede elegir en el init del proyecto y queda almacenada en package.json.

Incluye un nuevo comando dependencies, con tres subcomandos: add, remove, sync, que funcionan como un wrapper de npm. Eso quizás requiera discusión, porque entiendo que podríamos usar directamente npm sobre el proyecto y tiene que andar, pero me pareció más piola ocultar la complejidad de npm.

Las librerías quedan en node_modules a nivel proyecto. Está carpeta ahora se incliuye en el .gitIgnore.

También se resuelve un bug existente en el "assert path exist" y se agrega capacidad de checkeo en json.

Se agrega la abtracción Project encargado de interactuar con el package.json, y capaz de leer los métodos nativos.

Como trabajo futuro me gustaría discutir de que manera conviene incluir acceso a las firmas de los métodos de runtimeModel para que las funciones nativas sean tipadas como corresponde

@lgassman lgassman requested review from fdodino and PalumboN December 30, 2024 00:27
"winston": "^3.11.0",
"wollok-ts": "^4.1.9",
"wollok-ts": "file:../wollok-ts",
Copy link
Author

Choose a reason for hiding this comment

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

Ojo acá! me quedó porque en mi compu laburé siempre contra mi propia version de wollok-ts, pero para pushear volví a poner la versión releaseada. No se bien como manejan el proceso de release cuando hay interdependencias entre los proyectos

@@ -108,6 +108,7 @@ export function interpreteLine(interpreter: Interpreter, line: string): string {
export async function initializeInterpreter(autoImportPath: string | undefined, { project, skipValidations }: Options): Promise<Interpreter> {
let environment: Environment
const timeMeasurer = new TimeMeasurer()
const proj = new Project(project)
Copy link
Author

Choose a reason for hiding this comment

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

paso por Project para leer las natives

Copy link
Contributor

Choose a reason for hiding this comment

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

Hay alguna razón por la que decidiste crear un objeto? Por lo que veo solamente se instancia para mandarle readNatives() en todos los usos.

const project = join(_project, folder ?? '')
const nativesFolder = join(project, natives ?? '')
Copy link
Author

Choose a reason for hiding this comment

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

si me pasaron natives, entonces es esa carpeta, si no es la misma del project

module: 'NodeNext',
moduleResolution: 'NodeNext',
},
})
Copy link
Author

Choose a reason for hiding this comment

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

De estas opciones no estoy seguro si son las mejores. Entiendo que esto es necesario para cargar los archivos ts dinámicamente.

Copy link
Contributor

Choose a reason for hiding this comment

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

Uh nunca use asi ts-node asi que no tengo ninguna opinion fuerte, cli se traspila a commonjs no se si eso traeria problemas.

Lo que si me pregunto es si esta bien que este como dev dependency en el package.json

} catch (_error) {
// No package.json or it is invalid. This is not a real problem.
// Silence here or log?
}
Copy link
Author

Choose a reason for hiding this comment

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

Esto me da mucha duda. Llegué a la conclusión que lo mejor es que si no pudo cargar el package.json hay que hacerse el gil. Pero no se que opinan ustedes

Copy link
Contributor

Choose a reason for hiding this comment

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

Un log seguro tiraria, cortar la ejecución para mi no.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 al warning.

Pero atención que acá se repite lógica con readPackageProperties (que se usa para leer la carpeta assets). Estaría bueno unificar y loggear lo correspondiente en ambos casos: ya sea sugerir que se cree el package.json (no tenemos forma de hacer eso con el comando init no?) o que el archivo está roto.

pathExists(path: string): Assertion
pathExists: Assertion
jsonKeys(expectedKeys: string[]): Assertion;
jsonMatch(expected: Record<string, any>): Assertion;
Copy link
Author

Choose a reason for hiding this comment

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

Agregué para hacer assertions sobre el json, pero también arreglé el pathExist que no estaba bien

Copy link
Contributor

@PalumboN PalumboN Jan 5, 2025

Choose a reason for hiding this comment

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

Piola! Acabo de mergear #205 que arreglaba lo del pathExist y lo teníamos colgado hace tiempo. Perdón por el conflicto, pero no quería más gente arreglando eso mientras esto se mergea 😅

@lgassman lgassman requested a review from ivojawer December 30, 2024 01:12
Copy link
Contributor

@ivojawer ivojawer left a comment

Choose a reason for hiding this comment

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

Wooo cuanta magia que hay aca 🪄

Algo medio sad es que las natives no van a andar para el debugger 😢. Hay que crear ese issue en el LSP para no olvidarse de algun dia implementarlo

} catch (_error) {
// No package.json or it is invalid. This is not a real problem.
// Silence here or log?
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Un log seguro tiraria, cortar la ejecución para mi no.

module: 'NodeNext',
moduleResolution: 'NodeNext',
},
})
Copy link
Contributor

Choose a reason for hiding this comment

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

Uh nunca use asi ts-node asi que no tengo ninguna opinion fuerte, cli se traspila a commonjs no se si eso traeria problemas.

Lo que si me pregunto es si esta bien que este como dev dependency en el package.json

src/index.ts Show resolved Hide resolved
Copy link
Contributor

@PalumboN PalumboN left a comment

Choose a reason for hiding this comment

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

GROSIIISIMO! 🚀 👨‍🎤 💯 🈴

Dejé un par de comentarios para ir charlando!

Como dijo Ivo, hay que ver cómo se lleva todo esto con el empaquetado (habría que generarlo y probar el ejemplo contra eso).

@@ -108,6 +108,7 @@ export function interpreteLine(interpreter: Interpreter, line: string): string {
export async function initializeInterpreter(autoImportPath: string | undefined, { project, skipValidations }: Options): Promise<Interpreter> {
let environment: Environment
const timeMeasurer = new TimeMeasurer()
const proj = new Project(project)
Copy link
Contributor

Choose a reason for hiding this comment

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

Hay alguna razón por la que decidiste crear un objeto? Por lo que veo solamente se instancia para mandarle readNatives() en todos los usos.

@@ -22,6 +21,7 @@ export type Options = {
host: string,
port: string,
skipDiagram: boolean,
natives?: string
Copy link
Contributor

Choose a reason for hiding this comment

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

Esto no iría acá, no? Es un parámetro del init solamente

Comment on lines +18 to +24
export const addDependency = (pkg: string, { project, verbose }: Options): void => {
executeNpmCommand(`install ${pkg}`, project, verbose)
}

export const removeDependency = (pkg: string, { project, verbose }: Options): void => {
executeNpmCommand(`uninstall ${pkg}`, project, verbose)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

A estos no hay que mandarles -s para que queden guardados en el package.json?

src/index.ts Show resolved Hide resolved
@@ -64,8 +64,38 @@ updateNotifier().finally(() => {
.option('-t, --noTest', 'avoids creating a test file', false)
.option('-c, --noCI', 'avoids creating a file for CI', false)
.option('-ng, --noGit', 'avoids initializing a git repository', false)
.option('-N, --natives [natives]', 'Folder name for native files and dependencies (defaults to root project).', undefined)
Copy link
Contributor

Choose a reason for hiding this comment

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

Pequeño cambio para mantener el TOC con el resto de las options

Suggested change
.option('-N, --natives [natives]', 'Folder name for native files and dependencies (defaults to root project).', undefined)
.option('-N, --natives [natives]', 'folder name for native files (default: root folder).', undefined)

Entiendo que las dependencias siempre se instalan en node_modules.

Comment on lines +71 to +72
const dependencyCommand = new Command('dependencies')
.description('Manage dependencies for a Wollok project')
Copy link
Contributor

Choose a reason for hiding this comment

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

Dos cositas:

  1. Por qué hacer un wrapper de npm y no usar npm directo? Entiendo que en las máquinas de los labos donde la instalación es por el compilado pueden no tener npm, pero en ese caso este comando no va a andar tampoco. En el caso recomendado los usuarios ya usan npm para instalar wollok.

  2. Esto hay que documentarlo en algún lado (por ahora lo mejor es el README, pero necesitamos una wiki/doc con ejemplos de los comandos (para otro issue).

} catch (_error) {
// No package.json or it is invalid. This is not a real problem.
// Silence here or log?
}
Copy link
Contributor

Choose a reason for hiding this comment

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

+1 al warning.

Pero atención que acá se repite lógica con readPackageProperties (que se usa para leer la carpeta assets). Estaría bueno unificar y loggear lo correspondiente en ambos casos: ya sea sugerir que se cree el package.json (no tenemos forma de hacer eso con el comando init no?) o que el archivo está roto.

pathExists(path: string): Assertion
pathExists: Assertion
jsonKeys(expectedKeys: string[]): Assertion;
jsonMatch(expected: Record<string, any>): Assertion;
Copy link
Contributor

@PalumboN PalumboN Jan 5, 2025

Choose a reason for hiding this comment

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

Piola! Acabo de mergear #205 que arreglaba lo del pathExist y lo teníamos colgado hace tiempo. Perdón por el conflicto, pero no quería más gente arreglando eso mientras esto se mergea 😅


expect(join(project, 'myNatives')).to.pathExists
expect('package.json')
expect(join(project, 'package.json')).jsonMatch({ natives: 'myNatives' })
Copy link
Contributor

Choose a reason for hiding this comment

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

Acá no quedaría mejor usar la función que trae el JSON desde el archivo que se usa en los otros comandos?

Además no se puede usar .property[Val] para las assertions?

Comment on lines +91 to +122
const getNestedValue = (obj: Record<string, any>, path: string): any =>
path.split('.').reduce((acc, key) => acc ? acc[key] : undefined, obj)

const matchPartial = (
expected: Record<string, any>,
actual: Record<string, any>,
prefix: string = ''
): boolean =>
Object.keys(expected).every((key) => {
const fullPath = prefix ? `${prefix}.${key}` : key
if (typeof expected[key] === 'object' && expected[key] !== null) {
if (typeof actual[key] !== 'object' || actual[key] === null) {
return false
}
return matchPartial(expected[key], actual[key], fullPath)
}
return actual[key] === expected[key]
})

const getJsonContent = (filePath: string): any => {
if (!existsSync(filePath)) {
throw new chai.AssertionError(`Expected file "${filePath}" to exist`)
}

try {
return JSON.parse(readFileSync(filePath, 'utf8'))
} catch (error) {
throw new chai.AssertionError(
`Failed to parse JSON from file "${filePath}": ${String(error)}`
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Hace falta todo esto? La API de chai es bastante completa.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants