I'm trying to dynamically require all rkt files in a directory without using any bindings of them, because they have conflicts.
The tree is the following
|- index.rkt
|--- commands
|- echo.rkt
|- another-cmd.rkt
|- ...
so the idea is that each of this commands export three variables name
, description
and a function execute
, I'm looking for a way to tell racks
to include these files automatically, I tried to hack together a require transformer, yet it seems that I need at least one variable in the body of the module for it to work, I can't just visit the modules.
This is what I tried using
visit-in.rkt
#lang racket/base
(require racket/require
(for-syntax racket/base
racket/path
racket/require-transform
syntax/parse))
(provide visit-in visit-all-rkt-in)
(define-syntax visit-in
(make-require-transformer
(lambda (stx)
(syntax-parse stx
[(_ path)
(expand-import #'(only-in path))]
[(_ subs ...+)
(let-values ([(_ import-sources) ; only visit sources
(expand-import #'(multi-in subs ...))])
(values '() import-sources))]
))))
(define-syntax visit-all-rkt-in
(make-require-transformer
(lambda (stx)
(syntax-parse stx
[(_ path:string)
(define path/source (syntax-source #'path))
(unless (and path/source (path-string? path/source) (absolute-path? path/source))
(raise-syntax-error 'visit-all-rkt-in "expected path? source location" #'path))
(let-values ([(base _name _dir?) (split-path path/source)])
(let* ([is-rkt? (lambda (x) (path-has-extension? x #".rkt"))]
[dirlist (directory-list (build-path base (syntax-e #'path)))])
(expand-import
#`(visit-in
path
#,(map path->string (filter is-rkt? dirlist))))))]))))
and in "index.rkt"
#lang racketscript/base
(require "visit-in.rkt"
(visit-all-rkt-in "commands"))
these two also do not work
(define-syntax (require-folder stx)
(syntax-parse stx
[(_ path:string)
(let ([base-src (syntax-source #'path)]
[path/string (syntax-e #'path)])
(let-values ([(base _name _dir?) (split-path base-src)])
(with-syntax
([(mod-paths ...)
(for/list ([p (in-list (directory-list (build-path base path/string)))]
#:when (path-has-extension? p #".rkt"))
(path->string (build-path path/string p)))])
#'(begin
(define x ($/require mod-paths))
...))))]))
(define-syntax (require-folder2 stx)
(syntax-parse stx
[(_ path:string)
(let ([base-src (syntax-source #'path)]
[path/string (syntax-e #'path)])
(let-values ([(base _name _dir?) (split-path base-src)])
(with-syntax
([(mod-paths ...)
(for/list ([p (in-list (directory-list (build-path base path/string)))]
#:when (path-has-extension? p #".rkt"))
(path->string (build-path path/string p)))])
#'(begin
(require mod-paths)
...))))]))
(require-folder2 "commands")
(require-folder "commands")
Okay, I'm virtually certain that I'm missing something obvious here, but is there a reason that you can't use e.g.
(dynamic-require file #f)
?
yes, I should have put it in the title that what I need is a solution for racketscript (although I did put it in the racketscript category, but I get that is not very visible)
Basically, racketscript compiles to javascript, and it seems that if you don't use any identifiers from a module, it doesnt register them as dependencies.
After much testing, and trying to understand racketscript's compiler internals, it turns out it only registers a module as part of the dependency graph if one of the identifiers in the body of the module comes from another module, so the solution ends up like this.
(define-syntax (require-folder* stx)
(syntax-parse stx
[(_ path:string)
#:with name-bind (format-id this-syntax "name")
(let ([base-src (syntax-source #'path)]
[path/string (syntax-e #'path)])
(let-values ([(base _name _dir?) (split-path base-src)])
(with-syntax*
([(mod-paths ...)
(for/list ([p (in-list (directory-list (build-path base path/string)))]
#:when (path-has-extension? p #".rkt"))
(path->string (build-path path/string p)))]
[(tmp ...) (generate-temporaries #'(mod-paths ...))])
#'(begin
(begin (require (only-in mod-paths [name-bind tmp])) tmp)
...))))]))
You need to have one provide
d binding in common throughout all the files you want to have compiled, and actually use it,. Inside the macro we use the only-in
form to avoid any issues with conflicting bindings.
Not the prettiest solution, but it works.
Note: $/require
seems to only be intended to work with external libraries.
Glad to hear you found a workaround. Does RacketScript's design make sense here, or is this something that should change?
I opened two issues that might make this easier for newcomers (like me).
so hopefully an alternative to above could be made available to users.
2 Likes